summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/amba/bus.c4
-rw-r--r--drivers/amba/tegra-ahb.c2
-rw-r--r--drivers/base/topology.c12
-rw-r--r--drivers/block/rbd.c87
-rw-r--r--drivers/block/zram/Kconfig10
-rw-r--r--drivers/block/zram/Makefile4
-rw-r--r--drivers/block/zram/zcomp.c353
-rw-r--r--drivers/block/zram/zcomp.h68
-rw-r--r--drivers/block/zram/zcomp_lz4.c47
-rw-r--r--drivers/block/zram/zcomp_lz4.h17
-rw-r--r--drivers/block/zram/zcomp_lzo.c47
-rw-r--r--drivers/block/zram/zcomp_lzo.h17
-rw-r--r--drivers/block/zram/zram_drv.c383
-rw-r--r--drivers/block/zram/zram_drv.h21
-rw-r--r--drivers/bus/arm-cci.c24
-rw-r--r--drivers/bus/imx-weim.c58
-rw-r--r--drivers/bus/mvebu-mbus.c3
-rw-r--r--drivers/char/hw_random/Kconfig6
-rw-r--r--drivers/char/tpm/Kconfig2
-rw-r--r--drivers/clk/Kconfig7
-rw-r--r--drivers/clk/Makefile4
-rw-r--r--drivers/clk/at91/clk-programmable.c202
-rw-r--r--drivers/clk/at91/clk-system.c76
-rw-r--r--drivers/clk/bcm/Kconfig9
-rw-r--r--drivers/clk/bcm/Makefile3
-rw-r--r--drivers/clk/bcm/clk-bcm281xx.c416
-rw-r--r--drivers/clk/bcm/clk-kona-setup.c769
-rw-r--r--drivers/clk/bcm/clk-kona.c1033
-rw-r--r--drivers/clk/bcm/clk-kona.h410
-rw-r--r--drivers/clk/clk-axi-clkgen.c312
-rw-r--r--drivers/clk/clk-divider.c10
-rw-r--r--drivers/clk/clk-moxart.c97
-rw-r--r--drivers/clk/clk-ppc-corenet.c70
-rw-r--r--drivers/clk/clk-s2mps11.c29
-rw-r--r--drivers/clk/clk.c131
-rw-r--r--drivers/clk/clkdev.c2
-rw-r--r--drivers/clk/hisilicon/Makefile5
-rw-r--r--drivers/clk/hisilicon/clk-hi3620.c298
-rw-r--r--drivers/clk/hisilicon/clk-hip04.c58
-rw-r--r--drivers/clk/hisilicon/clk.c62
-rw-r--r--drivers/clk/hisilicon/clk.h17
-rw-r--r--drivers/clk/mmp/clk-frac.c20
-rw-r--r--drivers/clk/mvebu/Kconfig8
-rw-r--r--drivers/clk/mvebu/Makefile2
-rw-r--r--drivers/clk/mvebu/armada-375.c184
-rw-r--r--drivers/clk/mvebu/armada-38x.c167
-rw-r--r--drivers/clk/mvebu/clk-corediv.c154
-rw-r--r--drivers/clk/samsung/clk-exynos-audss.c2
-rw-r--r--drivers/clk/samsung/clk-exynos4.c172
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c49
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c49
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c2
-rw-r--r--drivers/clk/samsung/clk-s3c64xx.c79
-rw-r--r--drivers/clk/samsung/clk.c71
-rw-r--r--drivers/clk/samsung/clk.h14
-rw-r--r--drivers/clk/shmobile/Makefile1
-rw-r--r--drivers/clk/shmobile/clk-div6.c2
-rw-r--r--drivers/clk/shmobile/clk-mstp.c2
-rw-r--r--drivers/clk/shmobile/clk-rcar-gen2.c8
-rw-r--r--drivers/clk/shmobile/clk-rz.c103
-rw-r--r--drivers/clk/sirf/clk-atlas6.c3
-rw-r--r--drivers/clk/sirf/clk-common.c3
-rw-r--r--drivers/clk/sirf/clk-prima2.c3
-rw-r--r--drivers/clk/socfpga/Makefile3
-rw-r--r--drivers/clk/socfpga/clk-gate.c263
-rw-r--r--drivers/clk/socfpga/clk-periph.c94
-rw-r--r--drivers/clk/socfpga/clk-pll.c131
-rw-r--r--drivers/clk/socfpga/clk.c326
-rw-r--r--drivers/clk/socfpga/clk.h57
-rw-r--r--drivers/clk/st/Makefile1
-rw-r--r--drivers/clk/st/clkgen-fsyn.c1039
-rw-r--r--drivers/clk/st/clkgen-mux.c820
-rw-r--r--drivers/clk/st/clkgen-pll.c698
-rw-r--r--drivers/clk/st/clkgen.h48
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c305
-rw-r--r--drivers/clk/tegra/clk-periph.c2
-rw-r--r--drivers/clk/ti/clk-33xx.c1
-rw-r--r--drivers/clk/ti/clk-3xxx.c4
-rw-r--r--drivers/clk/ti/clk-44xx.c1
-rw-r--r--drivers/clk/ti/clk-54xx.c1
-rw-r--r--drivers/clk/ti/clk-7xx.c1
-rw-r--r--drivers/clk/ti/divider.c8
-rw-r--r--drivers/clk/ux500/u8500_of_clk.c3
-rw-r--r--drivers/clk/versatile/clk-icst.c21
-rw-r--r--drivers/clk/versatile/clk-icst.h1
-rw-r--r--drivers/clk/versatile/clk-impd1.c12
-rw-r--r--drivers/clk/versatile/clk-integrator.c83
-rw-r--r--drivers/clk/versatile/clk-realview.c4
-rw-r--r--drivers/clk/zynq/clkc.c93
-rw-r--r--drivers/clk/zynq/pll.c18
-rw-r--r--drivers/clocksource/Kconfig3
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/dummy_timer.c11
-rw-r--r--drivers/clocksource/exynos_mct.c2
-rw-r--r--drivers/clocksource/qcom-timer.c330
-rw-r--r--drivers/clocksource/timer-marco.c13
-rw-r--r--drivers/clocksource/timer-prima2.c16
-rw-r--r--drivers/clocksource/timer-u300.c2
-rw-r--r--drivers/cpufreq/Kconfig.arm2
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c7
-rw-r--r--drivers/cpuidle/Kconfig.arm2
-rw-r--r--drivers/dma/omap-dma.c659
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/gpio/gpio-ich.c26
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile4
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c24
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c12
-rw-r--r--drivers/gpu/drm/ast/ast_ttm.c5
-rw-r--r--drivers/gpu/drm/bochs/bochs_kms.c4
-rw-r--r--drivers/gpu/drm/bochs/bochs_mm.c6
-rw-r--r--drivers/gpu/drm/bridge/Kconfig5
-rw-r--r--drivers/gpu/drm/bridge/Makefile3
-rw-r--r--drivers/gpu/drm/bridge/ptn3460.c350
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_mode.c10
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_ttm.c5
-rw-r--r--drivers/gpu/drm/drm_crtc.c936
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c245
-rw-r--r--drivers/gpu/drm/drm_crtc_internal.h38
-rw-r--r--drivers/gpu/drm/drm_dp_helper.c396
-rw-r--r--drivers/gpu/drm/drm_drv.c136
-rw-r--r--drivers/gpu/drm/drm_edid.c34
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c45
-rw-r--r--drivers/gpu/drm/drm_fops.c121
-rw-r--r--drivers/gpu/drm/drm_gem.c71
-rw-r--r--drivers/gpu/drm/drm_gem_cma_helper.c38
-rw-r--r--drivers/gpu/drm/drm_ioctl.c7
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c6
-rw-r--r--drivers/gpu/drm/drm_mm.c285
-rw-r--r--drivers/gpu/drm/drm_modes.c346
-rw-r--r--drivers/gpu/drm/drm_pci.c2
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c333
-rw-r--r--drivers/gpu/drm/drm_platform.c2
-rw-r--r--drivers/gpu/drm/drm_prime.c110
-rw-r--r--drivers/gpu/drm/drm_stub.c503
-rw-r--r--drivers/gpu/drm/drm_usb.c2
-rw-r--r--drivers/gpu/drm/exynos/Kconfig24
-rw-r--r--drivers/gpu/drm/exynos/Makefile9
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c (renamed from drivers/video/exynos/exynos_dp_core.c)304
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.h (renamed from drivers/video/exynos/exynos_dp_core.h)9
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_reg.c (renamed from drivers/video/exynos/exynos_dp_reg.c)0
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_reg.h (renamed from drivers/video/exynos/exynos_dp_reg.h)0
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c92
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c193
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c159
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h20
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c339
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c197
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h163
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c1524
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c359
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h18
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c7
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c23
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c700
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.c439
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.h67
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c19
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.h2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c441
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c472
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c573
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.h20
-rw-r--r--drivers/gpu/drm/gma500/Makefile2
-rw-r--r--drivers/gpu/drm/gma500/blitter.c51
-rw-r--r--drivers/gpu/drm/gma500/blitter.h22
-rw-r--r--drivers/gpu/drm/gma500/cdv_device.c40
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_crt.c9
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_display.c73
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c2
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c11
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c5
-rw-r--r--drivers/gpu/drm/gma500/framebuffer.c2
-rw-r--r--drivers/gpu/drm/gma500/gem.c56
-rw-r--r--drivers/gpu/drm/gma500/gem.h21
-rw-r--r--drivers/gpu/drm/gma500/gma_device.c56
-rw-r--r--drivers/gpu/drm/gma500/gma_device.h21
-rw-r--r--drivers/gpu/drm/gma500/gma_display.c23
-rw-r--r--drivers/gpu/drm/gma500/gma_display.h3
-rw-r--r--drivers/gpu/drm/gma500/gtt.c45
-rw-r--r--drivers/gpu/drm/gma500/gtt.h3
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_output.c2
-rw-r--r--drivers/gpu/drm/gma500/mdfld_intel_display.c16
-rw-r--r--drivers/gpu/drm/gma500/mmu.c297
-rw-r--r--drivers/gpu/drm/gma500/mmu.h93
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_crtc.c12
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi.c9
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c5
-rw-r--r--drivers/gpu/drm/gma500/opregion.c25
-rw-r--r--drivers/gpu/drm/gma500/psb_device.c42
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c404
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.h203
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_display.c32
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_drv.h2
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c5
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_sdvo.c22
-rw-r--r--drivers/gpu/drm/gma500/psb_irq.c81
-rw-r--r--drivers/gpu/drm/i2c/tda998x_drv.c602
-rw-r--r--drivers/gpu/drm/i915/Makefile80
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7xxx.c4
-rw-r--r--drivers/gpu/drm/i915/dvo_ivch.c30
-rw-r--r--drivers/gpu/drm/i915/dvo_ns2501.c10
-rw-r--r--drivers/gpu/drm/i915/dvo_sil164.c10
-rw-r--r--drivers/gpu/drm/i915/dvo_tfp410.c24
-rw-r--r--drivers/gpu/drm/i915/i915_cmd_parser.c485
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c716
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c162
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c299
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h729
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c697
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c483
-rw-r--r--drivers/gpu/drm/i915/i915_gem_debug.c2
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c63
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c198
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c1256
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c2
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c8
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c616
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c836
-rw-r--r--drivers/gpu/drm/i915/i915_params.c154
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h560
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c40
-rw-r--r--drivers/gpu/drm/i915/i915_sysfs.c71
-rw-r--r--drivers/gpu/drm/i915/i915_trace.h24
-rw-r--r--drivers/gpu/drm/i915/i915_ums.c8
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c8
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h174
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c70
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c117
-rw-r--r--drivers/gpu/drm/i915/intel_display.c1579
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c912
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h106
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.c23
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c6
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c371
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c108
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c24
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c40
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c21
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c1202
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c152
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h42
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c78
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c18
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c21
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c281
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c26
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_ttm.c5
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c105
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c65
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h16
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c50
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h25
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_audio.c273
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_bridge.c26
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c33
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c8
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c27
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c8
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp_kms.c3
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c139
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h4
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c15
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c85
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h16
-rw-r--r--drivers/gpu/drm/nouveau/Makefile16
-rw-r--r--drivers/gpu/drm/nouveau/core/core/namedb.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/core/parent.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/base.c85
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/gm100.c106
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv04.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv10.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv20.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv30.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv40.c32
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv50.c28
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nvc0.c44
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nve0.c20
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dport.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/gm107.c101
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv04.c12
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.c370
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.h54
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv84.c191
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv94.c50
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva0.c67
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva3.c22
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c361
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nve0.c187
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c22
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/priv.h10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/falcon.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c509
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c507
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c989
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c1041
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c308
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h170
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c270
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c (renamed from drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c)78
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c144
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c216
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c278
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c285
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c782
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc7
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc542
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5.h473
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnv108.fuc5.h354
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h336
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h396
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h396
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h396
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc540
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h916
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/gm107.c465
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv108.c133
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv20.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv40.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv50.c45
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c312
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h214
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c115
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc4.c (renamed from drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c)86
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c104
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c175
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c154
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nve4.c192
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c136
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/xtensa.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/class.h12
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/device.h31
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/namedb.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/device.h18
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/disp.h19
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/graph.h3
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/P0260.h23
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h7
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/devinit.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/fb.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h3
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/mc.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/therm.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/timer.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/os.h12
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/base.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c15
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/P0260.c109
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/base.c55
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c29
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/init.c18
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/ramcfg.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/therm.c11
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/base.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h8
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/gm107.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.h3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c50
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/gm107.c38
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h8
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramgm107.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c34
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c7
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c7
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.c (renamed from drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c)109
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h21
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c142
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/base.c55
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mxm/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/base.c14
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/fan.c25
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c40
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/timer/gk20a.c57
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c83
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/timer/nv04.h27
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/timer/priv.h6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/crtc.c22
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dfp.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c22
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_agp.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c22
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c88
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.h5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hwmon.c17
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sysfs.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c35
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c5
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c17
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c13
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c5
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c34
-rw-r--r--drivers/gpu/drm/panel/Kconfig14
-rw-r--r--drivers/gpu/drm/panel/Makefile2
-rw-r--r--drivers/gpu/drm/panel/panel-ld9040.c376
-rw-r--r--drivers/gpu/drm/panel/panel-s6e8aa0.c1069
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c157
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c10
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_release.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c7
-rw-r--r--drivers/gpu/drm/radeon/Makefile8
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c20
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c268
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c82
-rw-r--r--drivers/gpu/drm/radeon/btc_dpm.c4
-rw-r--r--drivers/gpu/drm/radeon/ci_dpm.c67
-rw-r--r--drivers/gpu/drm/radeon/cik.c142
-rw-r--r--drivers/gpu/drm/radeon/cik_sdma.c6
-rw-r--r--drivers/gpu/drm/radeon/cikd.h49
-rw-r--r--drivers/gpu/drm/radeon/cypress_dpm.c4
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c6
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c210
-rw-r--r--drivers/gpu/drm/radeon/evergreen_dma.c4
-rw-r--r--drivers/gpu/drm/radeon/kv_dpm.c64
-rw-r--r--drivers/gpu/drm/radeon/ni.c8
-rw-r--r--drivers/gpu/drm/radeon/ni_dma.c6
-rw-r--r--drivers/gpu/drm/radeon/ni_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/r100.c49
-rw-r--r--drivers/gpu/drm/radeon/r200.c20
-rw-r--r--drivers/gpu/drm/radeon/r300.c32
-rw-r--r--drivers/gpu/drm/radeon/r600.c6
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c110
-rw-r--r--drivers/gpu/drm/radeon/r600_dma.c6
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c48
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon.h158
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c19
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h14
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c19
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c163
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c248
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c59
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c958
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c49
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c30
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c149
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h9
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c32
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c119
-rw-r--r--drivers/gpu/drm/radeon/radeon_sa.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_semaphore.c14
-rw-r--r--drivers/gpu/drm/radeon/radeon_test.c39
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c699
-rw-r--r--drivers/gpu/drm/radeon/radeon_vm.c966
-rw-r--r--drivers/gpu/drm/radeon/rs780_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/rv6xx_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/rv770_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/si.c10
-rw-r--r--drivers/gpu/drm/radeon/si_dma.c4
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/sid.h47
-rw-r--r--drivers/gpu/drm/radeon/sumo_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/trinity_dpm.c7
-rw-r--r--drivers/gpu/drm/radeon/uvd_v1_0.c2
-rw-r--r--drivers/gpu/drm/radeon/vce_v1_0.c187
-rw-r--r--drivers/gpu/drm/radeon/vce_v2_0.c181
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c10
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c5
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_crtc.c16
-rw-r--r--drivers/gpu/drm/tegra/Makefile2
-rw-r--r--drivers/gpu/drm/tegra/bus.c2
-rw-r--r--drivers/gpu/drm/tegra/dc.c16
-rw-r--r--drivers/gpu/drm/tegra/dc.h1
-rw-r--r--drivers/gpu/drm/tegra/dpaux.c544
-rw-r--r--drivers/gpu/drm/tegra/dpaux.h73
-rw-r--r--drivers/gpu/drm/tegra/drm.c19
-rw-r--r--drivers/gpu/drm/tegra/drm.h20
-rw-r--r--drivers/gpu/drm/tegra/dsi.c20
-rw-r--r--drivers/gpu/drm/tegra/dsi.h20
-rw-r--r--drivers/gpu/drm/tegra/gem.c25
-rw-r--r--drivers/gpu/drm/tegra/gem.h14
-rw-r--r--drivers/gpu/drm/tegra/gr2d.c14
-rw-r--r--drivers/gpu/drm/tegra/mipi-phy.c20
-rw-r--r--drivers/gpu/drm/tegra/mipi-phy.h20
-rw-r--r--drivers/gpu/drm/tegra/output.c8
-rw-r--r--drivers/gpu/drm/tegra/sor.c1092
-rw-r--r--drivers/gpu/drm/tegra/sor.h278
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_crtc.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c29
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_manager.c11
-rw-r--r--drivers/gpu/drm/ttm/ttm_execbuf_util.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_object.c46
-rw-r--r--drivers/gpu/drm/udl/udl_fb.c2
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c2
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_context.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c15
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c148
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h9
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c14
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c29
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c24
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_shader.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_surface.c143
-rw-r--r--drivers/gpu/host1x/syncpt.c1
-rw-r--r--drivers/hwmon/adm1021.c70
-rw-r--r--drivers/hwmon/asc7621.c1
-rw-r--r--drivers/hwmon/atxp1.c2
-rw-r--r--drivers/hwmon/coretemp.c14
-rw-r--r--drivers/hwmon/f71805f.c2
-rw-r--r--drivers/hwmon/it87.c9
-rw-r--r--drivers/hwmon/lm63.c157
-rw-r--r--drivers/hwmon/lm77.c1
-rw-r--r--drivers/hwmon/lm80.c70
-rw-r--r--drivers/hwmon/lm83.c1
-rw-r--r--drivers/hwmon/lm87.c1
-rw-r--r--drivers/hwmon/lm90.c111
-rw-r--r--drivers/hwmon/lm92.c1
-rw-r--r--drivers/hwmon/lm93.c1
-rw-r--r--drivers/hwmon/max1619.c1
-rw-r--r--drivers/hwmon/pc87360.c12
-rw-r--r--drivers/hwmon/via-cputemp.c14
-rw-r--r--drivers/hwmon/w83792d.c1
-rw-r--r--drivers/hwmon/w83l785ts.c4
-rw-r--r--drivers/i2c/busses/Kconfig2
-rw-r--r--drivers/idle/intel_idle.c12
-rw-r--r--drivers/iio/adc/Kconfig10
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/twl4030-madc.c (renamed from drivers/mfd/twl4030-madc.c)321
-rw-r--r--drivers/input/misc/Kconfig4
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c2
-rw-r--r--drivers/iommu/Kconfig2
-rw-r--r--drivers/iommu/amd_iommu.c8
-rw-r--r--drivers/iommu/amd_iommu_init.c16
-rw-r--r--drivers/iommu/amd_iommu_types.h11
-rw-r--r--drivers/iommu/arm-smmu.c105
-rw-r--r--drivers/iommu/dmar.c513
-rw-r--r--drivers/iommu/intel-iommu.c1610
-rw-r--r--drivers/iommu/intel_irq_remapping.c108
-rw-r--r--drivers/iommu/iova.c64
-rw-r--r--drivers/iommu/omap-iommu.c162
-rw-r--r--drivers/iommu/omap-iommu.h5
-rw-r--r--drivers/iommu/omap-iommu2.c3
-rw-r--r--drivers/irqchip/Kconfig16
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/exynos-combiner.c3
-rw-r--r--drivers/irqchip/irq-clps711x.c243
-rw-r--r--drivers/irqchip/irq-crossbar.c208
-rw-r--r--drivers/irqchip/irq-gic.c86
-rw-r--r--drivers/irqchip/irq-mmp.c2
-rw-r--r--drivers/irqchip/irq-vic.c60
-rw-r--r--drivers/leds/Kconfig4
-rw-r--r--drivers/lguest/page_tables.c6
-rw-r--r--drivers/md/Kconfig11
-rw-r--r--drivers/md/Makefile2
-rw-r--r--drivers/md/dm-cache-block-types.h11
-rw-r--r--drivers/md/dm-cache-metadata.c132
-rw-r--r--drivers/md/dm-cache-metadata.h15
-rw-r--r--drivers/md/dm-cache-target.c131
-rw-r--r--drivers/md/dm-era-target.c1746
-rw-r--r--drivers/md/dm-mpath.c219
-rw-r--r--drivers/md/dm-table.c21
-rw-r--r--drivers/md/dm-thin-metadata.c80
-rw-r--r--drivers/md/dm-thin.c263
-rw-r--r--drivers/md/dm.c24
-rw-r--r--drivers/md/dm.h2
-rw-r--r--drivers/md/persistent-data/dm-bitset.c10
-rw-r--r--drivers/md/persistent-data/dm-bitset.h1
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c15
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h3
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.c5
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.h17
-rw-r--r--drivers/media/i2c/Kconfig8
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c207
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-spi.c6
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h4
-rw-r--r--drivers/media/i2c/s5k6a3.c389
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig9
-rw-r--r--drivers/media/platform/exynos4-is/Makefile4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.h5
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c16
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.h1
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.c285
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.h49
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c98
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.h9
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.c660
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.h44
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c29
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h27
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c363
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h32
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-regs.h24
-rw-r--r--drivers/mfd/88pm800.c3
-rw-r--r--drivers/mfd/88pm860x-core.c6
-rw-r--r--drivers/mfd/Kconfig98
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/ab8500-core.c27
-rw-r--r--drivers/mfd/adp5520.c1
-rw-r--r--drivers/mfd/as3722.c1
-rw-r--r--drivers/mfd/bcm590xx.c93
-rw-r--r--drivers/mfd/cs5535-mfd.c1
-rw-r--r--drivers/mfd/da9052-core.c3
-rw-r--r--drivers/mfd/da9052-i2c.c5
-rw-r--r--drivers/mfd/da9052-spi.c1
-rw-r--r--drivers/mfd/da9055-i2c.c8
-rw-r--r--drivers/mfd/da9063-core.c25
-rw-r--r--drivers/mfd/db8500-prcmu.c34
-rw-r--r--drivers/mfd/janz-cmodio.c1
-rw-r--r--drivers/mfd/kempld-core.c31
-rw-r--r--drivers/mfd/lpc_ich.c151
-rw-r--r--drivers/mfd/lpc_sch.c1
-rw-r--r--drivers/mfd/max14577.c6
-rw-r--r--drivers/mfd/max77686.c4
-rw-r--r--drivers/mfd/max77693.c12
-rw-r--r--drivers/mfd/max8925-i2c.c9
-rw-r--r--drivers/mfd/max8997.c18
-rw-r--r--drivers/mfd/max8998.c4
-rw-r--r--drivers/mfd/mc13xxx-spi.c5
-rw-r--r--drivers/mfd/mcp-sa11x0.c1
-rw-r--r--drivers/mfd/omap-usb-host.c189
-rw-r--r--drivers/mfd/omap-usb-tll.c2
-rw-r--r--drivers/mfd/pcf50633-adc.c1
-rw-r--r--drivers/mfd/pm8921-core.c419
-rw-r--r--drivers/mfd/pm8xxx-irq.c371
-rw-r--r--drivers/mfd/rc5t583-irq.c1
-rw-r--r--drivers/mfd/rdc321x-southbridge.c1
-rw-r--r--drivers/mfd/retu-mfd.c1
-rw-r--r--drivers/mfd/rtsx_usb.c760
-rw-r--r--drivers/mfd/sec-core.c17
-rw-r--r--drivers/mfd/smsc-ece1099.c1
-rw-r--r--drivers/mfd/stmpe.c2
-rw-r--r--drivers/mfd/stw481x.c8
-rw-r--r--drivers/mfd/syscon.c9
-rw-r--r--drivers/mfd/tc3589x.c84
-rw-r--r--drivers/mfd/ti-ssp.c465
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c24
-rw-r--r--drivers/mfd/timberdale.c2
-rw-r--r--drivers/mfd/tps65218.c282
-rw-r--r--drivers/mfd/tps65910.c5
-rw-r--r--drivers/mfd/tps65912-core.c1
-rw-r--r--drivers/mfd/tps65912-irq.c1
-rw-r--r--drivers/mfd/twl-core.c10
-rw-r--r--drivers/mfd/twl4030-irq.c1
-rw-r--r--drivers/mfd/twl6030-irq.c1
-rw-r--r--drivers/mfd/twl6040.c6
-rw-r--r--drivers/mfd/ucb1x00-core.c4
-rw-r--r--drivers/mfd/vexpress-config.c3
-rw-r--r--drivers/mfd/vexpress-sysreg.c2
-rw-r--r--drivers/mfd/wm5102-tables.c36
-rw-r--r--drivers/mfd/wm5110-tables.c174
-rw-r--r--drivers/mfd/wm8350-core.c1
-rw-r--r--drivers/mfd/wm8350-irq.c1
-rw-r--r--drivers/mfd/wm8400-core.c22
-rw-r--r--drivers/misc/sgi-gru/grukdump.c6
-rw-r--r--drivers/mmc/host/mmci.h9
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/bcm47xxpart.c11
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c40
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c9
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c3
-rw-r--r--drivers/mtd/chips/cfi_probe.c4
-rw-r--r--drivers/mtd/chips/cfi_util.c4
-rw-r--r--drivers/mtd/chips/gen_probe.c2
-rw-r--r--drivers/mtd/devices/Kconfig8
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/block2mtd.c19
-rw-r--r--drivers/mtd/devices/elm.c49
-rw-r--r--drivers/mtd/devices/m25p80.c35
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c24
-rw-r--r--drivers/mtd/devices/phram.c41
-rw-r--r--drivers/mtd/devices/pmc551.c7
-rw-r--r--drivers/mtd/devices/serial_flash_cmds.h81
-rw-r--r--drivers/mtd/devices/spear_smi.c2
-rw-r--r--drivers/mtd/devices/sst25l.c1
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c2108
-rw-r--r--drivers/mtd/inftlmount.c1
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c4
-rw-r--r--drivers/mtd/lpddr/qinfo_probe.c5
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/bfin-async-flash.c1
-rw-r--r--drivers/mtd/maps/gpio-addr-flash.c1
-rw-r--r--drivers/mtd/maps/intel_vr_nor.c1
-rw-r--r--drivers/mtd/maps/ixp4xx.c1
-rw-r--r--drivers/mtd/maps/lantiq-flash.c1
-rw-r--r--drivers/mtd/maps/latch-addr-flash.c1
-rw-r--r--drivers/mtd/maps/pci.c1
-rw-r--r--drivers/mtd/maps/physmap_of.c1
-rw-r--r--drivers/mtd/maps/plat-ram.c2
-rw-r--r--drivers/mtd/maps/pxa2xx-flash.c1
-rw-r--r--drivers/mtd/maps/rbtx4939-flash.c1
-rw-r--r--drivers/mtd/maps/scb2_flash.c1
-rw-r--r--drivers/mtd/maps/sun_uflash.c1
-rw-r--r--drivers/mtd/mtd_blkdevs.c1
-rw-r--r--drivers/mtd/mtdchar.c20
-rw-r--r--drivers/mtd/mtdcore.c24
-rw-r--r--drivers/mtd/mtdpart.c14
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/ams-delta.c1
-rw-r--r--drivers/mtd/nand/atmel_nand.c14
-rw-r--r--drivers/mtd/nand/au1550nd.c4
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c1
-rw-r--r--drivers/mtd/nand/cafe_nand.c68
-rw-r--r--drivers/mtd/nand/davinci_nand.c23
-rw-r--r--drivers/mtd/nand/denali_dt.c39
-rw-r--r--drivers/mtd/nand/diskonchip.c5
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c1
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c1
-rw-r--r--drivers/mtd/nand/gpio.c1
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c102
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c1
-rw-r--r--drivers/mtd/nand/mxc_nand.c2
-rw-r--r--drivers/mtd/nand/nand_base.c165
-rw-r--r--drivers/mtd/nand/nand_ids.c3
-rw-r--r--drivers/mtd/nand/nuc900_nand.c6
-rw-r--r--drivers/mtd/nand/omap2.c512
-rw-r--r--drivers/mtd/nand/pasemi_nand.c1
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c3
-rw-r--r--drivers/mtd/nand/s3c2410.c1
-rw-r--r--drivers/mtd/onenand/generic.c1
-rw-r--r--drivers/mtd/onenand/omap2.c1
-rw-r--r--drivers/mtd/onenand/onenand_base.c38
-rw-r--r--drivers/mtd/onenand/samsung.c4
-rw-r--r--drivers/mtd/rfd_ftl.c9
-rw-r--r--drivers/mtd/sm_ftl.c11
-rw-r--r--drivers/mtd/tests/mtd_test.c1
-rw-r--r--drivers/mtd/ubi/ubi.h1
-rw-r--r--drivers/net/can/sja1000/Kconfig2
-rw-r--r--drivers/net/ethernet/3com/Kconfig2
-rw-r--r--drivers/net/rionet.c1
-rw-r--r--drivers/of/of_mtd.c34
-rw-r--r--drivers/oprofile/nmi_timer_int.c23
-rw-r--r--drivers/pci/slot.c6
-rw-r--r--drivers/phy/Kconfig2
-rw-r--r--drivers/power/reset/Kconfig2
-rw-r--r--drivers/power/reset/qnap-poweroff.c49
-rw-r--r--drivers/powercap/intel_rapl.c10
-rw-r--r--drivers/pwm/Kconfig30
-rw-r--r--drivers/pwm/Makefile3
-rw-r--r--drivers/pwm/pwm-atmel.c9
-rw-r--r--drivers/pwm/pwm-clps711x.c176
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c495
-rw-r--r--drivers/pwm/pwm-lpss.c183
-rw-r--r--drivers/pwm/pwm-pxa.c4
-rw-r--r--drivers/pwm/pwm-samsung.c5
-rw-r--r--drivers/rapidio/devices/tsi721.c1
-rw-r--r--drivers/rapidio/devices/tsi721.h4
-rw-r--r--drivers/rapidio/devices/tsi721_dma.c111
-rw-r--r--drivers/rapidio/rio-driver.c22
-rw-r--r--drivers/rapidio/rio-scan.c1
-rw-r--r--drivers/rapidio/rio-sysfs.c40
-rw-r--r--drivers/rapidio/rio.c11
-rw-r--r--drivers/rapidio/rio.h1
-rw-r--r--drivers/reset/Kconfig2
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/core.c71
-rw-r--r--drivers/reset/sti/Kconfig15
-rw-r--r--drivers/reset/sti/Makefile4
-rw-r--r--drivers/reset/sti/reset-stih415.c112
-rw-r--r--drivers/reset/sti/reset-stih416.c143
-rw-r--r--drivers/reset/sti/reset-syscfg.c186
-rw-r--r--drivers/reset/sti/reset-syscfg.h69
-rw-r--r--drivers/rtc/rtc-at91sam9.c2
-rw-r--r--drivers/rtc/rtc-isl12057.c5
-rw-r--r--drivers/rtc/rtc-mv.c12
-rw-r--r--drivers/rtc/rtc-pxa.c1
-rw-r--r--drivers/s390/block/dasd_diag.c4
-rw-r--r--drivers/s390/char/raw3270.c9
-rw-r--r--drivers/s390/char/sclp.c88
-rw-r--r--drivers/s390/char/sclp.h9
-rw-r--r--drivers/s390/char/sclp_cmd.c17
-rw-r--r--drivers/s390/char/tape_std.c3
-rw-r--r--drivers/s390/crypto/zcrypt_api.c4
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c18
-rw-r--r--drivers/s390/kvm/kvm_virtio.c2
-rw-r--r--drivers/s390/net/lcs.c1
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c12
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c12
-rw-r--r--drivers/scsi/fcoe/fcoe.c15
-rw-r--r--drivers/sh/clk/cpg.c38
-rw-r--r--drivers/sh/intc/Kconfig2
-rw-r--r--drivers/staging/imx-drm/ipuv3-crtc.c6
-rw-r--r--drivers/staging/imx-drm/ipuv3-plane.c2
-rw-r--r--drivers/staging/speakup/kobjects.c62
-rw-r--r--drivers/staging/speakup/speakup.h2
-rw-r--r--drivers/staging/speakup/speakup_acntpc.c22
-rw-r--r--drivers/staging/speakup/speakup_acntsa.c22
-rw-r--r--drivers/staging/speakup/speakup_apollo.c24
-rw-r--r--drivers/staging/speakup/speakup_audptr.c24
-rw-r--r--drivers/staging/speakup/speakup_bns.c22
-rw-r--r--drivers/staging/speakup/speakup_decext.c24
-rw-r--r--drivers/staging/speakup/speakup_decpc.c24
-rw-r--r--drivers/staging/speakup/speakup_dectlk.c24
-rw-r--r--drivers/staging/speakup/speakup_dtlk.c28
-rw-r--r--drivers/staging/speakup/speakup_dummy.c22
-rw-r--r--drivers/staging/speakup/speakup_keypc.c18
-rw-r--r--drivers/staging/speakup/speakup_ltlk.c28
-rw-r--r--drivers/staging/speakup/speakup_soft.c30
-rw-r--r--drivers/staging/speakup/speakup_spkout.c24
-rw-r--r--drivers/staging/speakup/speakup_txprt.c22
-rw-r--r--drivers/thermal/Kconfig2
-rw-r--r--drivers/thermal/x86_pkg_temp_thermal.c14
-rw-r--r--drivers/tty/serial/Kconfig2
-rw-r--r--drivers/tty/serial/atmel_serial.c49
-rw-r--r--drivers/usb/gadget/lpc32xx_udc.c1
-rw-r--r--drivers/video/exynos/Kconfig7
-rw-r--r--drivers/video/exynos/Makefile1
-rw-r--r--drivers/video/omap2/displays-new/connector-analog-tv.c43
-rw-r--r--drivers/video/omap2/displays-new/connector-dvi.c43
-rw-r--r--drivers/video/omap2/displays-new/connector-hdmi.c30
-rw-r--r--drivers/video/omap2/displays-new/encoder-tfp410.c43
-rw-r--r--drivers/video/omap2/displays-new/encoder-tpd12s015.c56
-rw-r--r--drivers/video/omap2/displays-new/panel-dsi-cm.c60
-rw-r--r--drivers/video/omap2/displays-new/panel-sony-acx565akm.c33
-rw-r--r--drivers/video/omap2/dss/Makefile2
-rw-r--r--drivers/video/omap2/dss/dispc.c8
-rw-r--r--drivers/video/omap2/dss/display.c28
-rw-r--r--drivers/video/omap2/dss/dpi.c47
-rw-r--r--drivers/video/omap2/dss/dsi.c147
-rw-r--r--drivers/video/omap2/dss/dss-of.c159
-rw-r--r--drivers/video/omap2/dss/dss.c66
-rw-r--r--drivers/video/omap2/dss/dss.h6
-rw-r--r--drivers/video/omap2/dss/hdmi4.c14
-rw-r--r--drivers/video/omap2/dss/hdmi_wp.c2
-rw-r--r--drivers/video/omap2/dss/sdi.c45
-rw-r--r--drivers/video/omap2/dss/venc.c66
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c67
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/iTCO_wdt.c137
-rw-r--r--drivers/watchdog/octeon-wdt-main.c11
-rw-r--r--drivers/watchdog/orion_wdt.c381
-rw-r--r--drivers/xen/balloon.c36
-rw-r--r--drivers/xen/events/events_base.c2
872 files changed, 61135 insertions, 21351 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 9e6029105607..3cf61a127ee5 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -83,7 +83,7 @@ static struct device_attribute amba_dev_attrs[] = {
__ATTR_NULL,
};
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
/*
* Hooks to provide runtime PM of the pclk (bus clock). It is safe to
* enable/disable the bus clock at runtime PM suspend/resume as this
@@ -123,7 +123,7 @@ static const struct dev_pm_ops amba_pm = {
.thaw = pm_generic_thaw,
.poweroff = pm_generic_poweroff,
.restore = pm_generic_restore,
- SET_RUNTIME_PM_OPS(
+ SET_PM_RUNTIME_PM_OPS(
amba_pm_runtime_suspend,
amba_pm_runtime_resume,
NULL
diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c
index 1f44e56cc65d..558a239954e8 100644
--- a/drivers/amba/tegra-ahb.c
+++ b/drivers/amba/tegra-ahb.c
@@ -256,8 +256,6 @@ static int tegra_ahb_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
ahb->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ahb->regs))
return PTR_ERR(ahb->regs);
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index ad9d17762664..bbcbd3c43926 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -160,16 +160,20 @@ static int topology_cpu_callback(struct notifier_block *nfb,
static int topology_sysfs_init(void)
{
int cpu;
- int rc;
+ int rc = 0;
+
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu) {
rc = topology_add_dev(cpu);
if (rc)
- return rc;
+ goto out;
}
- hotcpu_notifier(topology_cpu_callback, 0);
+ __hotcpu_notifier(topology_cpu_callback, 0);
- return 0;
+out:
+ cpu_notifier_register_done();
+ return rc;
}
device_initcall(topology_sysfs_init);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 34898d53395b..4c95b503b09e 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1654,7 +1654,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
if (osd_req->r_result < 0)
obj_request->result = osd_req->r_result;
- BUG_ON(osd_req->r_num_ops > 2);
+ rbd_assert(osd_req->r_num_ops <= CEPH_OSD_MAX_OP);
/*
* We support a 64-bit length, but ultimately it has to be
@@ -1662,11 +1662,15 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
*/
obj_request->xferred = osd_req->r_reply_op_len[0];
rbd_assert(obj_request->xferred < (u64)UINT_MAX);
+
opcode = osd_req->r_ops[0].op;
switch (opcode) {
case CEPH_OSD_OP_READ:
rbd_osd_read_callback(obj_request);
break;
+ case CEPH_OSD_OP_SETALLOCHINT:
+ rbd_assert(osd_req->r_ops[1].op == CEPH_OSD_OP_WRITE);
+ /* fall through */
case CEPH_OSD_OP_WRITE:
rbd_osd_write_callback(obj_request);
break;
@@ -1715,9 +1719,16 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
snapc, CEPH_NOSNAP, &mtime);
}
+/*
+ * Create an osd request. A read request has one osd op (read).
+ * A write request has either one (watch) or two (hint+write) osd ops.
+ * (All rbd data writes are prefixed with an allocation hint op, but
+ * technically osd watch is a write request, hence this distinction.)
+ */
static struct ceph_osd_request *rbd_osd_req_create(
struct rbd_device *rbd_dev,
bool write_request,
+ unsigned int num_ops,
struct rbd_obj_request *obj_request)
{
struct ceph_snap_context *snapc = NULL;
@@ -1733,10 +1744,13 @@ static struct ceph_osd_request *rbd_osd_req_create(
snapc = img_request->snapc;
}
- /* Allocate and initialize the request, for the single op */
+ rbd_assert(num_ops == 1 || (write_request && num_ops == 2));
+
+ /* Allocate and initialize the request, for the num_ops ops */
osdc = &rbd_dev->rbd_client->client->osdc;
- osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_ATOMIC);
+ osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false,
+ GFP_ATOMIC);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -1756,8 +1770,8 @@ static struct ceph_osd_request *rbd_osd_req_create(
/*
* Create a copyup osd request based on the information in the
- * object request supplied. A copyup request has two osd ops,
- * a copyup method call, and a "normal" write request.
+ * object request supplied. A copyup request has three osd ops,
+ * a copyup method call, a hint op, and a write op.
*/
static struct ceph_osd_request *
rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
@@ -1773,12 +1787,12 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
rbd_assert(img_request);
rbd_assert(img_request_write_test(img_request));
- /* Allocate and initialize the request, for the two ops */
+ /* Allocate and initialize the request, for the three ops */
snapc = img_request->snapc;
rbd_dev = img_request->rbd_dev;
osdc = &rbd_dev->rbd_client->client->osdc;
- osd_req = ceph_osdc_alloc_request(osdc, snapc, 2, false, GFP_ATOMIC);
+ osd_req = ceph_osdc_alloc_request(osdc, snapc, 3, false, GFP_ATOMIC);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -2178,6 +2192,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
const char *object_name;
u64 offset;
u64 length;
+ unsigned int which = 0;
object_name = rbd_segment_name(rbd_dev, img_offset);
if (!object_name)
@@ -2190,6 +2205,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
rbd_segment_name_free(object_name);
if (!obj_request)
goto out_unwind;
+
/*
* set obj_request->img_request before creating the
* osd_request so that it gets the right snapc
@@ -2207,7 +2223,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
clone_size,
GFP_ATOMIC);
if (!obj_request->bio_list)
- goto out_partial;
+ goto out_unwind;
} else {
unsigned int page_count;
@@ -2220,19 +2236,27 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
}
osd_req = rbd_osd_req_create(rbd_dev, write_request,
- obj_request);
+ (write_request ? 2 : 1),
+ obj_request);
if (!osd_req)
- goto out_partial;
+ goto out_unwind;
obj_request->osd_req = osd_req;
obj_request->callback = rbd_img_obj_callback;
- osd_req_op_extent_init(osd_req, 0, opcode, offset, length,
- 0, 0);
+ if (write_request) {
+ osd_req_op_alloc_hint_init(osd_req, which,
+ rbd_obj_bytes(&rbd_dev->header),
+ rbd_obj_bytes(&rbd_dev->header));
+ which++;
+ }
+
+ osd_req_op_extent_init(osd_req, which, opcode, offset, length,
+ 0, 0);
if (type == OBJ_REQUEST_BIO)
- osd_req_op_extent_osd_data_bio(osd_req, 0,
+ osd_req_op_extent_osd_data_bio(osd_req, which,
obj_request->bio_list, length);
else
- osd_req_op_extent_osd_data_pages(osd_req, 0,
+ osd_req_op_extent_osd_data_pages(osd_req, which,
obj_request->pages, length,
offset & ~PAGE_MASK, false, false);
@@ -2249,11 +2273,9 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
return 0;
-out_partial:
- rbd_obj_request_put(obj_request);
out_unwind:
for_each_obj_request_safe(img_request, obj_request, next_obj_request)
- rbd_obj_request_put(obj_request);
+ rbd_img_obj_request_del(img_request, obj_request);
return -ENOMEM;
}
@@ -2353,7 +2375,7 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
/*
* The original osd request is of no use to use any more.
- * We need a new one that can hold the two ops in a copyup
+ * We need a new one that can hold the three ops in a copyup
* request. Allocate the new copyup osd request for the
* original request, and release the old one.
*/
@@ -2372,17 +2394,22 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
osd_req_op_cls_request_data_pages(osd_req, 0, pages, parent_length, 0,
false, false);
- /* Then the original write request op */
+ /* Then the hint op */
+
+ osd_req_op_alloc_hint_init(osd_req, 1, rbd_obj_bytes(&rbd_dev->header),
+ rbd_obj_bytes(&rbd_dev->header));
+
+ /* And the original write request op */
offset = orig_request->offset;
length = orig_request->length;
- osd_req_op_extent_init(osd_req, 1, CEPH_OSD_OP_WRITE,
+ osd_req_op_extent_init(osd_req, 2, CEPH_OSD_OP_WRITE,
offset, length, 0, 0);
if (orig_request->type == OBJ_REQUEST_BIO)
- osd_req_op_extent_osd_data_bio(osd_req, 1,
+ osd_req_op_extent_osd_data_bio(osd_req, 2,
orig_request->bio_list, length);
else
- osd_req_op_extent_osd_data_pages(osd_req, 1,
+ osd_req_op_extent_osd_data_pages(osd_req, 2,
orig_request->pages, length,
offset & ~PAGE_MASK, false, false);
@@ -2603,8 +2630,8 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)
rbd_assert(obj_request->img_request);
rbd_dev = obj_request->img_request->rbd_dev;
- stat_request->osd_req = rbd_osd_req_create(rbd_dev, false,
- stat_request);
+ stat_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ stat_request);
if (!stat_request->osd_req)
goto out;
stat_request->callback = rbd_img_obj_exists_callback;
@@ -2807,7 +2834,8 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, u64 notify_id)
return -ENOMEM;
ret = -ENOMEM;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out;
@@ -2870,7 +2898,8 @@ static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
if (!obj_request)
goto out_cancel;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out_cancel;
@@ -2978,7 +3007,8 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev,
obj_request->pages = pages;
obj_request->page_count = page_count;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out;
@@ -3211,7 +3241,8 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev,
obj_request->pages = pages;
obj_request->page_count = page_count;
- obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, obj_request);
+ obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1,
+ obj_request);
if (!obj_request->osd_req)
goto out;
diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig
index 3450be850399..6489c0fd0ea6 100644
--- a/drivers/block/zram/Kconfig
+++ b/drivers/block/zram/Kconfig
@@ -15,6 +15,16 @@ config ZRAM
See zram.txt for more information.
+config ZRAM_LZ4_COMPRESS
+ bool "Enable LZ4 algorithm support"
+ depends on ZRAM
+ select LZ4_COMPRESS
+ select LZ4_DECOMPRESS
+ default n
+ help
+ This option enables LZ4 compression algorithm support. Compression
+ algorithm can be changed using `comp_algorithm' device attribute.
+
config ZRAM_DEBUG
bool "Compressed RAM block device debug support"
depends on ZRAM
diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile
index cb0f9ced6a93..be0763ff57a2 100644
--- a/drivers/block/zram/Makefile
+++ b/drivers/block/zram/Makefile
@@ -1,3 +1,5 @@
-zram-y := zram_drv.o
+zram-y := zcomp_lzo.o zcomp.o zram_drv.o
+
+zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o
obj-$(CONFIG_ZRAM) += zram.o
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
new file mode 100644
index 000000000000..f1ff39a3d1c1
--- /dev/null
+++ b/drivers/block/zram/zcomp.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include "zcomp.h"
+#include "zcomp_lzo.h"
+#ifdef CONFIG_ZRAM_LZ4_COMPRESS
+#include "zcomp_lz4.h"
+#endif
+
+/*
+ * single zcomp_strm backend
+ */
+struct zcomp_strm_single {
+ struct mutex strm_lock;
+ struct zcomp_strm *zstrm;
+};
+
+/*
+ * multi zcomp_strm backend
+ */
+struct zcomp_strm_multi {
+ /* protect strm list */
+ spinlock_t strm_lock;
+ /* max possible number of zstrm streams */
+ int max_strm;
+ /* number of available zstrm streams */
+ int avail_strm;
+ /* list of available strms */
+ struct list_head idle_strm;
+ wait_queue_head_t strm_wait;
+};
+
+static struct zcomp_backend *backends[] = {
+ &zcomp_lzo,
+#ifdef CONFIG_ZRAM_LZ4_COMPRESS
+ &zcomp_lz4,
+#endif
+ NULL
+};
+
+static struct zcomp_backend *find_backend(const char *compress)
+{
+ int i = 0;
+ while (backends[i]) {
+ if (sysfs_streq(compress, backends[i]->name))
+ break;
+ i++;
+ }
+ return backends[i];
+}
+
+static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
+{
+ if (zstrm->private)
+ comp->backend->destroy(zstrm->private);
+ free_pages((unsigned long)zstrm->buffer, 1);
+ kfree(zstrm);
+}
+
+/*
+ * allocate new zcomp_strm structure with ->private initialized by
+ * backend, return NULL on error
+ */
+static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
+{
+ struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL);
+ if (!zstrm)
+ return NULL;
+
+ zstrm->private = comp->backend->create();
+ /*
+ * allocate 2 pages. 1 for compressed data, plus 1 extra for the
+ * case when compressed size is larger than the original one
+ */
+ zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
+ if (!zstrm->private || !zstrm->buffer) {
+ zcomp_strm_free(comp, zstrm);
+ zstrm = NULL;
+ }
+ return zstrm;
+}
+
+/*
+ * get idle zcomp_strm or wait until other process release
+ * (zcomp_strm_release()) one for us
+ */
+static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp)
+{
+ struct zcomp_strm_multi *zs = comp->stream;
+ struct zcomp_strm *zstrm;
+
+ while (1) {
+ spin_lock(&zs->strm_lock);
+ if (!list_empty(&zs->idle_strm)) {
+ zstrm = list_entry(zs->idle_strm.next,
+ struct zcomp_strm, list);
+ list_del(&zstrm->list);
+ spin_unlock(&zs->strm_lock);
+ return zstrm;
+ }
+ /* zstrm streams limit reached, wait for idle stream */
+ if (zs->avail_strm >= zs->max_strm) {
+ spin_unlock(&zs->strm_lock);
+ wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
+ continue;
+ }
+ /* allocate new zstrm stream */
+ zs->avail_strm++;
+ spin_unlock(&zs->strm_lock);
+
+ zstrm = zcomp_strm_alloc(comp);
+ if (!zstrm) {
+ spin_lock(&zs->strm_lock);
+ zs->avail_strm--;
+ spin_unlock(&zs->strm_lock);
+ wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
+ continue;
+ }
+ break;
+ }
+ return zstrm;
+}
+
+/* add stream back to idle list and wake up waiter or free the stream */
+static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstrm)
+{
+ struct zcomp_strm_multi *zs = comp->stream;
+
+ spin_lock(&zs->strm_lock);
+ if (zs->avail_strm <= zs->max_strm) {
+ list_add(&zstrm->list, &zs->idle_strm);
+ spin_unlock(&zs->strm_lock);
+ wake_up(&zs->strm_wait);
+ return;
+ }
+
+ zs->avail_strm--;
+ spin_unlock(&zs->strm_lock);
+ zcomp_strm_free(comp, zstrm);
+}
+
+/* change max_strm limit */
+static bool zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm)
+{
+ struct zcomp_strm_multi *zs = comp->stream;
+ struct zcomp_strm *zstrm;
+
+ spin_lock(&zs->strm_lock);
+ zs->max_strm = num_strm;
+ /*
+ * if user has lowered the limit and there are idle streams,
+ * immediately free as much streams (and memory) as we can.
+ */
+ while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) {
+ zstrm = list_entry(zs->idle_strm.next,
+ struct zcomp_strm, list);
+ list_del(&zstrm->list);
+ zcomp_strm_free(comp, zstrm);
+ zs->avail_strm--;
+ }
+ spin_unlock(&zs->strm_lock);
+ return true;
+}
+
+static void zcomp_strm_multi_destroy(struct zcomp *comp)
+{
+ struct zcomp_strm_multi *zs = comp->stream;
+ struct zcomp_strm *zstrm;
+
+ while (!list_empty(&zs->idle_strm)) {
+ zstrm = list_entry(zs->idle_strm.next,
+ struct zcomp_strm, list);
+ list_del(&zstrm->list);
+ zcomp_strm_free(comp, zstrm);
+ }
+ kfree(zs);
+}
+
+static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm)
+{
+ struct zcomp_strm *zstrm;
+ struct zcomp_strm_multi *zs;
+
+ comp->destroy = zcomp_strm_multi_destroy;
+ comp->strm_find = zcomp_strm_multi_find;
+ comp->strm_release = zcomp_strm_multi_release;
+ comp->set_max_streams = zcomp_strm_multi_set_max_streams;
+ zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL);
+ if (!zs)
+ return -ENOMEM;
+
+ comp->stream = zs;
+ spin_lock_init(&zs->strm_lock);
+ INIT_LIST_HEAD(&zs->idle_strm);
+ init_waitqueue_head(&zs->strm_wait);
+ zs->max_strm = max_strm;
+ zs->avail_strm = 1;
+
+ zstrm = zcomp_strm_alloc(comp);
+ if (!zstrm) {
+ kfree(zs);
+ return -ENOMEM;
+ }
+ list_add(&zstrm->list, &zs->idle_strm);
+ return 0;
+}
+
+static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp)
+{
+ struct zcomp_strm_single *zs = comp->stream;
+ mutex_lock(&zs->strm_lock);
+ return zs->zstrm;
+}
+
+static void zcomp_strm_single_release(struct zcomp *comp,
+ struct zcomp_strm *zstrm)
+{
+ struct zcomp_strm_single *zs = comp->stream;
+ mutex_unlock(&zs->strm_lock);
+}
+
+static bool zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm)
+{
+ /* zcomp_strm_single support only max_comp_streams == 1 */
+ return false;
+}
+
+static void zcomp_strm_single_destroy(struct zcomp *comp)
+{
+ struct zcomp_strm_single *zs = comp->stream;
+ zcomp_strm_free(comp, zs->zstrm);
+ kfree(zs);
+}
+
+static int zcomp_strm_single_create(struct zcomp *comp)
+{
+ struct zcomp_strm_single *zs;
+
+ comp->destroy = zcomp_strm_single_destroy;
+ comp->strm_find = zcomp_strm_single_find;
+ comp->strm_release = zcomp_strm_single_release;
+ comp->set_max_streams = zcomp_strm_single_set_max_streams;
+ zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL);
+ if (!zs)
+ return -ENOMEM;
+
+ comp->stream = zs;
+ mutex_init(&zs->strm_lock);
+ zs->zstrm = zcomp_strm_alloc(comp);
+ if (!zs->zstrm) {
+ kfree(zs);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* show available compressors */
+ssize_t zcomp_available_show(const char *comp, char *buf)
+{
+ ssize_t sz = 0;
+ int i = 0;
+
+ while (backends[i]) {
+ if (sysfs_streq(comp, backends[i]->name))
+ sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
+ "[%s] ", backends[i]->name);
+ else
+ sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
+ "%s ", backends[i]->name);
+ i++;
+ }
+ sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
+ return sz;
+}
+
+bool zcomp_set_max_streams(struct zcomp *comp, int num_strm)
+{
+ return comp->set_max_streams(comp, num_strm);
+}
+
+struct zcomp_strm *zcomp_strm_find(struct zcomp *comp)
+{
+ return comp->strm_find(comp);
+}
+
+void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm)
+{
+ comp->strm_release(comp, zstrm);
+}
+
+int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
+ const unsigned char *src, size_t *dst_len)
+{
+ return comp->backend->compress(src, zstrm->buffer, dst_len,
+ zstrm->private);
+}
+
+int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
+ size_t src_len, unsigned char *dst)
+{
+ return comp->backend->decompress(src, src_len, dst);
+}
+
+void zcomp_destroy(struct zcomp *comp)
+{
+ comp->destroy(comp);
+ kfree(comp);
+}
+
+/*
+ * search available compressors for requested algorithm.
+ * allocate new zcomp and initialize it. return compressing
+ * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL)
+ * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in
+ * case of allocation error.
+ */
+struct zcomp *zcomp_create(const char *compress, int max_strm)
+{
+ struct zcomp *comp;
+ struct zcomp_backend *backend;
+
+ backend = find_backend(compress);
+ if (!backend)
+ return ERR_PTR(-EINVAL);
+
+ comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
+ if (!comp)
+ return ERR_PTR(-ENOMEM);
+
+ comp->backend = backend;
+ if (max_strm > 1)
+ zcomp_strm_multi_create(comp, max_strm);
+ else
+ zcomp_strm_single_create(comp);
+ if (!comp->stream) {
+ kfree(comp);
+ return ERR_PTR(-ENOMEM);
+ }
+ return comp;
+}
diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
new file mode 100644
index 000000000000..c59d1fca72c0
--- /dev/null
+++ b/drivers/block/zram/zcomp.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * 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 _ZCOMP_H_
+#define _ZCOMP_H_
+
+#include <linux/mutex.h>
+
+struct zcomp_strm {
+ /* compression/decompression buffer */
+ void *buffer;
+ /*
+ * The private data of the compression stream, only compression
+ * stream backend can touch this (e.g. compression algorithm
+ * working memory)
+ */
+ void *private;
+ /* used in multi stream backend, protected by backend strm_lock */
+ struct list_head list;
+};
+
+/* static compression backend */
+struct zcomp_backend {
+ int (*compress)(const unsigned char *src, unsigned char *dst,
+ size_t *dst_len, void *private);
+
+ int (*decompress)(const unsigned char *src, size_t src_len,
+ unsigned char *dst);
+
+ void *(*create)(void);
+ void (*destroy)(void *private);
+
+ const char *name;
+};
+
+/* dynamic per-device compression frontend */
+struct zcomp {
+ void *stream;
+ struct zcomp_backend *backend;
+
+ struct zcomp_strm *(*strm_find)(struct zcomp *comp);
+ void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm);
+ bool (*set_max_streams)(struct zcomp *comp, int num_strm);
+ void (*destroy)(struct zcomp *comp);
+};
+
+ssize_t zcomp_available_show(const char *comp, char *buf);
+
+struct zcomp *zcomp_create(const char *comp, int max_strm);
+void zcomp_destroy(struct zcomp *comp);
+
+struct zcomp_strm *zcomp_strm_find(struct zcomp *comp);
+void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm);
+
+int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
+ const unsigned char *src, size_t *dst_len);
+
+int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
+ size_t src_len, unsigned char *dst);
+
+bool zcomp_set_max_streams(struct zcomp *comp, int num_strm);
+#endif /* _ZCOMP_H_ */
diff --git a/drivers/block/zram/zcomp_lz4.c b/drivers/block/zram/zcomp_lz4.c
new file mode 100644
index 000000000000..f2afb7e988c3
--- /dev/null
+++ b/drivers/block/zram/zcomp_lz4.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lz4.h>
+
+#include "zcomp_lz4.h"
+
+static void *zcomp_lz4_create(void)
+{
+ return kzalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
+}
+
+static void zcomp_lz4_destroy(void *private)
+{
+ kfree(private);
+}
+
+static int zcomp_lz4_compress(const unsigned char *src, unsigned char *dst,
+ size_t *dst_len, void *private)
+{
+ /* return : Success if return 0 */
+ return lz4_compress(src, PAGE_SIZE, dst, dst_len, private);
+}
+
+static int zcomp_lz4_decompress(const unsigned char *src, size_t src_len,
+ unsigned char *dst)
+{
+ size_t dst_len = PAGE_SIZE;
+ /* return : Success if return 0 */
+ return lz4_decompress_unknownoutputsize(src, src_len, dst, &dst_len);
+}
+
+struct zcomp_backend zcomp_lz4 = {
+ .compress = zcomp_lz4_compress,
+ .decompress = zcomp_lz4_decompress,
+ .create = zcomp_lz4_create,
+ .destroy = zcomp_lz4_destroy,
+ .name = "lz4",
+};
diff --git a/drivers/block/zram/zcomp_lz4.h b/drivers/block/zram/zcomp_lz4.h
new file mode 100644
index 000000000000..60613fb29dd8
--- /dev/null
+++ b/drivers/block/zram/zcomp_lz4.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * 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 _ZCOMP_LZ4_H_
+#define _ZCOMP_LZ4_H_
+
+#include "zcomp.h"
+
+extern struct zcomp_backend zcomp_lz4;
+
+#endif /* _ZCOMP_LZ4_H_ */
diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c
new file mode 100644
index 000000000000..da1bc47d588e
--- /dev/null
+++ b/drivers/block/zram/zcomp_lzo.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lzo.h>
+
+#include "zcomp_lzo.h"
+
+static void *lzo_create(void)
+{
+ return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+}
+
+static void lzo_destroy(void *private)
+{
+ kfree(private);
+}
+
+static int lzo_compress(const unsigned char *src, unsigned char *dst,
+ size_t *dst_len, void *private)
+{
+ int ret = lzo1x_1_compress(src, PAGE_SIZE, dst, dst_len, private);
+ return ret == LZO_E_OK ? 0 : ret;
+}
+
+static int lzo_decompress(const unsigned char *src, size_t src_len,
+ unsigned char *dst)
+{
+ size_t dst_len = PAGE_SIZE;
+ int ret = lzo1x_decompress_safe(src, src_len, dst, &dst_len);
+ return ret == LZO_E_OK ? 0 : ret;
+}
+
+struct zcomp_backend zcomp_lzo = {
+ .compress = lzo_compress,
+ .decompress = lzo_decompress,
+ .create = lzo_create,
+ .destroy = lzo_destroy,
+ .name = "lzo",
+};
diff --git a/drivers/block/zram/zcomp_lzo.h b/drivers/block/zram/zcomp_lzo.h
new file mode 100644
index 000000000000..128c5807fa14
--- /dev/null
+++ b/drivers/block/zram/zcomp_lzo.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * 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 _ZCOMP_LZO_H_
+#define _ZCOMP_LZO_H_
+
+#include "zcomp.h"
+
+extern struct zcomp_backend zcomp_lzo;
+
+#endif /* _ZCOMP_LZO_H_ */
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 51c557cfd92b..9849b5233bf4 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -29,19 +29,36 @@
#include <linux/genhd.h>
#include <linux/highmem.h>
#include <linux/slab.h>
-#include <linux/lzo.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
+#include <linux/err.h>
#include "zram_drv.h"
/* Globals */
static int zram_major;
static struct zram *zram_devices;
+static const char *default_compressor = "lzo";
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
+#define ZRAM_ATTR_RO(name) \
+static ssize_t zram_attr_##name##_show(struct device *d, \
+ struct device_attribute *attr, char *b) \
+{ \
+ struct zram *zram = dev_to_zram(d); \
+ return scnprintf(b, PAGE_SIZE, "%llu\n", \
+ (u64)atomic64_read(&zram->stats.name)); \
+} \
+static struct device_attribute dev_attr_##name = \
+ __ATTR(name, S_IRUGO, zram_attr_##name##_show, NULL);
+
+static inline int init_done(struct zram *zram)
+{
+ return zram->meta != NULL;
+}
+
static inline struct zram *dev_to_zram(struct device *dev)
{
return (struct zram *)dev_to_disk(dev)->private_data;
@@ -52,92 +69,114 @@ static ssize_t disksize_show(struct device *dev,
{
struct zram *zram = dev_to_zram(dev);
- return sprintf(buf, "%llu\n", zram->disksize);
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize);
}
static ssize_t initstate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ u32 val;
struct zram *zram = dev_to_zram(dev);
- return sprintf(buf, "%u\n", zram->init_done);
-}
-
-static ssize_t num_reads_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct zram *zram = dev_to_zram(dev);
+ down_read(&zram->init_lock);
+ val = init_done(zram);
+ up_read(&zram->init_lock);
- return sprintf(buf, "%llu\n",
- (u64)atomic64_read(&zram->stats.num_reads));
+ return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}
-static ssize_t num_writes_show(struct device *dev,
+static ssize_t orig_data_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct zram *zram = dev_to_zram(dev);
- return sprintf(buf, "%llu\n",
- (u64)atomic64_read(&zram->stats.num_writes));
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ (u64)(atomic64_read(&zram->stats.pages_stored)) << PAGE_SHIFT);
}
-static ssize_t invalid_io_show(struct device *dev,
+static ssize_t mem_used_total_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ u64 val = 0;
struct zram *zram = dev_to_zram(dev);
+ struct zram_meta *meta = zram->meta;
- return sprintf(buf, "%llu\n",
- (u64)atomic64_read(&zram->stats.invalid_io));
-}
-
-static ssize_t notify_free_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct zram *zram = dev_to_zram(dev);
+ down_read(&zram->init_lock);
+ if (init_done(zram))
+ val = zs_get_total_size_bytes(meta->mem_pool);
+ up_read(&zram->init_lock);
- return sprintf(buf, "%llu\n",
- (u64)atomic64_read(&zram->stats.notify_free));
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
-static ssize_t zero_pages_show(struct device *dev,
+static ssize_t max_comp_streams_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ int val;
struct zram *zram = dev_to_zram(dev);
- return sprintf(buf, "%u\n", atomic_read(&zram->stats.pages_zero));
+ down_read(&zram->init_lock);
+ val = zram->max_comp_streams;
+ up_read(&zram->init_lock);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", val);
}
-static ssize_t orig_data_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t max_comp_streams_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
{
+ int num;
struct zram *zram = dev_to_zram(dev);
+ int ret;
- return sprintf(buf, "%llu\n",
- (u64)(atomic_read(&zram->stats.pages_stored)) << PAGE_SHIFT);
-}
+ ret = kstrtoint(buf, 0, &num);
+ if (ret < 0)
+ return ret;
+ if (num < 1)
+ return -EINVAL;
-static ssize_t compr_data_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct zram *zram = dev_to_zram(dev);
+ down_write(&zram->init_lock);
+ if (init_done(zram)) {
+ if (!zcomp_set_max_streams(zram->comp, num)) {
+ pr_info("Cannot change max compression streams\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
- return sprintf(buf, "%llu\n",
- (u64)atomic64_read(&zram->stats.compr_size));
+ zram->max_comp_streams = num;
+ ret = len;
+out:
+ up_write(&zram->init_lock);
+ return ret;
}
-static ssize_t mem_used_total_show(struct device *dev,
+static ssize_t comp_algorithm_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u64 val = 0;
+ size_t sz;
struct zram *zram = dev_to_zram(dev);
- struct zram_meta *meta = zram->meta;
down_read(&zram->init_lock);
- if (zram->init_done)
- val = zs_get_total_size_bytes(meta->mem_pool);
+ sz = zcomp_available_show(zram->compressor, buf);
up_read(&zram->init_lock);
- return sprintf(buf, "%llu\n", val);
+ return sz;
+}
+
+static ssize_t comp_algorithm_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct zram *zram = dev_to_zram(dev);
+ down_write(&zram->init_lock);
+ if (init_done(zram)) {
+ up_write(&zram->init_lock);
+ pr_info("Can't change algorithm for initialized device\n");
+ return -EBUSY;
+ }
+ strlcpy(zram->compressor, buf, sizeof(zram->compressor));
+ up_write(&zram->init_lock);
+ return len;
}
/* flag operations needs meta->tb_lock */
@@ -192,8 +231,6 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio)
static void zram_meta_free(struct zram_meta *meta)
{
zs_destroy_pool(meta->mem_pool);
- kfree(meta->compress_workmem);
- free_pages((unsigned long)meta->compress_buffer, 1);
vfree(meta->table);
kfree(meta);
}
@@ -205,22 +242,11 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
if (!meta)
goto out;
- meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
- if (!meta->compress_workmem)
- goto free_meta;
-
- meta->compress_buffer =
- (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
- if (!meta->compress_buffer) {
- pr_err("Error allocating compressor buffer space\n");
- goto free_workmem;
- }
-
num_pages = disksize >> PAGE_SHIFT;
meta->table = vzalloc(num_pages * sizeof(*meta->table));
if (!meta->table) {
pr_err("Error allocating zram address table\n");
- goto free_buffer;
+ goto free_meta;
}
meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM);
@@ -230,15 +256,10 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
}
rwlock_init(&meta->tb_lock);
- mutex_init(&meta->buffer_lock);
return meta;
free_table:
vfree(meta->table);
-free_buffer:
- free_pages((unsigned long)meta->compress_buffer, 1);
-free_workmem:
- kfree(meta->compress_workmem);
free_meta:
kfree(meta);
meta = NULL;
@@ -288,7 +309,6 @@ static void zram_free_page(struct zram *zram, size_t index)
{
struct zram_meta *meta = zram->meta;
unsigned long handle = meta->table[index].handle;
- u16 size = meta->table[index].size;
if (unlikely(!handle)) {
/*
@@ -297,21 +317,15 @@ static void zram_free_page(struct zram *zram, size_t index)
*/
if (zram_test_flag(meta, index, ZRAM_ZERO)) {
zram_clear_flag(meta, index, ZRAM_ZERO);
- atomic_dec(&zram->stats.pages_zero);
+ atomic64_dec(&zram->stats.zero_pages);
}
return;
}
- if (unlikely(size > max_zpage_size))
- atomic_dec(&zram->stats.bad_compress);
-
zs_free(meta->mem_pool, handle);
- if (size <= PAGE_SIZE / 2)
- atomic_dec(&zram->stats.good_compress);
-
- atomic64_sub(meta->table[index].size, &zram->stats.compr_size);
- atomic_dec(&zram->stats.pages_stored);
+ atomic64_sub(meta->table[index].size, &zram->stats.compr_data_size);
+ atomic64_dec(&zram->stats.pages_stored);
meta->table[index].handle = 0;
meta->table[index].size = 0;
@@ -319,8 +333,7 @@ static void zram_free_page(struct zram *zram, size_t index)
static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
{
- int ret = LZO_E_OK;
- size_t clen = PAGE_SIZE;
+ int ret = 0;
unsigned char *cmem;
struct zram_meta *meta = zram->meta;
unsigned long handle;
@@ -340,12 +353,12 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
if (size == PAGE_SIZE)
copy_page(mem, cmem);
else
- ret = lzo1x_decompress_safe(cmem, size, mem, &clen);
+ ret = zcomp_decompress(zram->comp, cmem, size, mem);
zs_unmap_object(meta->mem_pool, handle);
read_unlock(&meta->tb_lock);
/* Should NEVER happen. Return bio error if it does. */
- if (unlikely(ret != LZO_E_OK)) {
+ if (unlikely(ret)) {
pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
atomic64_inc(&zram->stats.failed_reads);
return ret;
@@ -388,7 +401,7 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
ret = zram_decompress_page(zram, uncmem, index);
/* Should NEVER happen. Return bio error if it does. */
- if (unlikely(ret != LZO_E_OK))
+ if (unlikely(ret))
goto out_cleanup;
if (is_partial_io(bvec))
@@ -413,11 +426,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
struct page *page;
unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
struct zram_meta *meta = zram->meta;
+ struct zcomp_strm *zstrm;
bool locked = false;
page = bvec->bv_page;
- src = meta->compress_buffer;
-
if (is_partial_io(bvec)) {
/*
* This is a partial IO. We need to read the full page
@@ -433,7 +445,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
goto out;
}
- mutex_lock(&meta->buffer_lock);
+ zstrm = zcomp_strm_find(zram->comp);
locked = true;
user_mem = kmap_atomic(page);
@@ -454,28 +466,25 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
zram_set_flag(meta, index, ZRAM_ZERO);
write_unlock(&zram->meta->tb_lock);
- atomic_inc(&zram->stats.pages_zero);
+ atomic64_inc(&zram->stats.zero_pages);
ret = 0;
goto out;
}
- ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
- meta->compress_workmem);
+ ret = zcomp_compress(zram->comp, zstrm, uncmem, &clen);
if (!is_partial_io(bvec)) {
kunmap_atomic(user_mem);
user_mem = NULL;
uncmem = NULL;
}
- if (unlikely(ret != LZO_E_OK)) {
+ if (unlikely(ret)) {
pr_err("Compression failed! err=%d\n", ret);
goto out;
}
-
+ src = zstrm->buffer;
if (unlikely(clen > max_zpage_size)) {
- atomic_inc(&zram->stats.bad_compress);
clen = PAGE_SIZE;
- src = NULL;
if (is_partial_io(bvec))
src = uncmem;
}
@@ -497,6 +506,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
memcpy(cmem, src, clen);
}
+ zcomp_strm_release(zram->comp, zstrm);
+ locked = false;
zs_unmap_object(meta->mem_pool, handle);
/*
@@ -511,49 +522,88 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
write_unlock(&zram->meta->tb_lock);
/* Update stats */
- atomic64_add(clen, &zram->stats.compr_size);
- atomic_inc(&zram->stats.pages_stored);
- if (clen <= PAGE_SIZE / 2)
- atomic_inc(&zram->stats.good_compress);
-
+ atomic64_add(clen, &zram->stats.compr_data_size);
+ atomic64_inc(&zram->stats.pages_stored);
out:
if (locked)
- mutex_unlock(&meta->buffer_lock);
+ zcomp_strm_release(zram->comp, zstrm);
if (is_partial_io(bvec))
kfree(uncmem);
-
if (ret)
atomic64_inc(&zram->stats.failed_writes);
return ret;
}
static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
- int offset, struct bio *bio, int rw)
+ int offset, struct bio *bio)
{
int ret;
+ int rw = bio_data_dir(bio);
- if (rw == READ)
+ if (rw == READ) {
+ atomic64_inc(&zram->stats.num_reads);
ret = zram_bvec_read(zram, bvec, index, offset, bio);
- else
+ } else {
+ atomic64_inc(&zram->stats.num_writes);
ret = zram_bvec_write(zram, bvec, index, offset);
+ }
return ret;
}
+/*
+ * zram_bio_discard - handler on discard request
+ * @index: physical block index in PAGE_SIZE units
+ * @offset: byte offset within physical block
+ */
+static void zram_bio_discard(struct zram *zram, u32 index,
+ int offset, struct bio *bio)
+{
+ size_t n = bio->bi_iter.bi_size;
+
+ /*
+ * zram manages data in physical block size units. Because logical block
+ * size isn't identical with physical block size on some arch, we
+ * could get a discard request pointing to a specific offset within a
+ * certain physical block. Although we can handle this request by
+ * reading that physiclal block and decompressing and partially zeroing
+ * and re-compressing and then re-storing it, this isn't reasonable
+ * because our intent with a discard request is to save memory. So
+ * skipping this logical block is appropriate here.
+ */
+ if (offset) {
+ if (n < offset)
+ return;
+
+ n -= offset;
+ index++;
+ }
+
+ while (n >= PAGE_SIZE) {
+ /*
+ * Discard request can be large so the lock hold times could be
+ * lengthy. So take the lock once per page.
+ */
+ write_lock(&zram->meta->tb_lock);
+ zram_free_page(zram, index);
+ write_unlock(&zram->meta->tb_lock);
+ index++;
+ n -= PAGE_SIZE;
+ }
+}
+
static void zram_reset_device(struct zram *zram, bool reset_capacity)
{
size_t index;
struct zram_meta *meta;
down_write(&zram->init_lock);
- if (!zram->init_done) {
+ if (!init_done(zram)) {
up_write(&zram->init_lock);
return;
}
meta = zram->meta;
- zram->init_done = 0;
-
/* Free all pages that are still in this zram device */
for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) {
unsigned long handle = meta->table[index].handle;
@@ -563,6 +613,9 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity)
zs_free(meta->mem_pool, handle);
}
+ zcomp_destroy(zram->comp);
+ zram->max_comp_streams = 1;
+
zram_meta_free(zram->meta);
zram->meta = NULL;
/* Reset stats */
@@ -574,37 +627,14 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity)
up_write(&zram->init_lock);
}
-static void zram_init_device(struct zram *zram, struct zram_meta *meta)
-{
- if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) {
- pr_info(
- "There is little point creating a zram of greater than "
- "twice the size of memory since we expect a 2:1 compression "
- "ratio. Note that zram uses about 0.1%% of the size of "
- "the disk when not in use so a huge zram is "
- "wasteful.\n"
- "\tMemory Size: %lu kB\n"
- "\tSize you selected: %llu kB\n"
- "Continuing anyway ...\n",
- (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10
- );
- }
-
- /* zram devices sort of resembles non-rotational disks */
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
-
- zram->meta = meta;
- zram->init_done = 1;
-
- pr_debug("Initialization done!\n");
-}
-
static ssize_t disksize_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
u64 disksize;
+ struct zcomp *comp;
struct zram_meta *meta;
struct zram *zram = dev_to_zram(dev);
+ int err;
disksize = memparse(buf, NULL);
if (!disksize)
@@ -614,20 +644,35 @@ static ssize_t disksize_store(struct device *dev,
meta = zram_meta_alloc(disksize);
if (!meta)
return -ENOMEM;
+
+ comp = zcomp_create(zram->compressor, zram->max_comp_streams);
+ if (IS_ERR(comp)) {
+ pr_info("Cannot initialise %s compressing backend\n",
+ zram->compressor);
+ err = PTR_ERR(comp);
+ goto out_free_meta;
+ }
+
down_write(&zram->init_lock);
- if (zram->init_done) {
- up_write(&zram->init_lock);
- zram_meta_free(meta);
+ if (init_done(zram)) {
pr_info("Cannot change disksize for initialized device\n");
- return -EBUSY;
+ err = -EBUSY;
+ goto out_destroy_comp;
}
+ zram->meta = meta;
+ zram->comp = comp;
zram->disksize = disksize;
set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT);
- zram_init_device(zram, meta);
up_write(&zram->init_lock);
-
return len;
+
+out_destroy_comp:
+ up_write(&zram->init_lock);
+ zcomp_destroy(comp);
+out_free_meta:
+ zram_meta_free(meta);
+ return err;
}
static ssize_t reset_store(struct device *dev,
@@ -671,26 +716,23 @@ out:
return ret;
}
-static void __zram_make_request(struct zram *zram, struct bio *bio, int rw)
+static void __zram_make_request(struct zram *zram, struct bio *bio)
{
int offset;
u32 index;
struct bio_vec bvec;
struct bvec_iter iter;
- switch (rw) {
- case READ:
- atomic64_inc(&zram->stats.num_reads);
- break;
- case WRITE:
- atomic64_inc(&zram->stats.num_writes);
- break;
- }
-
index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT;
offset = (bio->bi_iter.bi_sector &
(SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT;
+ if (unlikely(bio->bi_rw & REQ_DISCARD)) {
+ zram_bio_discard(zram, index, offset, bio);
+ bio_endio(bio, 0);
+ return;
+ }
+
bio_for_each_segment(bvec, bio, iter) {
int max_transfer_size = PAGE_SIZE - offset;
@@ -705,16 +747,15 @@ static void __zram_make_request(struct zram *zram, struct bio *bio, int rw)
bv.bv_len = max_transfer_size;
bv.bv_offset = bvec.bv_offset;
- if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0)
+ if (zram_bvec_rw(zram, &bv, index, offset, bio) < 0)
goto out;
bv.bv_len = bvec.bv_len - max_transfer_size;
bv.bv_offset += max_transfer_size;
- if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0)
+ if (zram_bvec_rw(zram, &bv, index + 1, 0, bio) < 0)
goto out;
} else
- if (zram_bvec_rw(zram, &bvec, index, offset, bio, rw)
- < 0)
+ if (zram_bvec_rw(zram, &bvec, index, offset, bio) < 0)
goto out;
update_position(&index, &offset, &bvec);
@@ -736,7 +777,7 @@ static void zram_make_request(struct request_queue *queue, struct bio *bio)
struct zram *zram = queue->queuedata;
down_read(&zram->init_lock);
- if (unlikely(!zram->init_done))
+ if (unlikely(!init_done(zram)))
goto error;
if (!valid_io_request(zram, bio)) {
@@ -744,7 +785,7 @@ static void zram_make_request(struct request_queue *queue, struct bio *bio)
goto error;
}
- __zram_make_request(zram, bio, bio_data_dir(bio));
+ __zram_make_request(zram, bio);
up_read(&zram->init_lock);
return;
@@ -778,14 +819,21 @@ static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR,
disksize_show, disksize_store);
static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL);
static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store);
-static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL);
-static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL);
-static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL);
-static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL);
-static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL);
static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL);
-static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL);
static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL);
+static DEVICE_ATTR(max_comp_streams, S_IRUGO | S_IWUSR,
+ max_comp_streams_show, max_comp_streams_store);
+static DEVICE_ATTR(comp_algorithm, S_IRUGO | S_IWUSR,
+ comp_algorithm_show, comp_algorithm_store);
+
+ZRAM_ATTR_RO(num_reads);
+ZRAM_ATTR_RO(num_writes);
+ZRAM_ATTR_RO(failed_reads);
+ZRAM_ATTR_RO(failed_writes);
+ZRAM_ATTR_RO(invalid_io);
+ZRAM_ATTR_RO(notify_free);
+ZRAM_ATTR_RO(zero_pages);
+ZRAM_ATTR_RO(compr_data_size);
static struct attribute *zram_disk_attrs[] = {
&dev_attr_disksize.attr,
@@ -793,12 +841,16 @@ static struct attribute *zram_disk_attrs[] = {
&dev_attr_reset.attr,
&dev_attr_num_reads.attr,
&dev_attr_num_writes.attr,
+ &dev_attr_failed_reads.attr,
+ &dev_attr_failed_writes.attr,
&dev_attr_invalid_io.attr,
&dev_attr_notify_free.attr,
&dev_attr_zero_pages.attr,
&dev_attr_orig_data_size.attr,
&dev_attr_compr_data_size.attr,
&dev_attr_mem_used_total.attr,
+ &dev_attr_max_comp_streams.attr,
+ &dev_attr_comp_algorithm.attr,
NULL,
};
@@ -839,7 +891,8 @@ static int create_device(struct zram *zram, int device_id)
/* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
set_capacity(zram->disk, 0);
-
+ /* zram devices sort of resembles non-rotational disks */
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
/*
* To ensure that we always get PAGE_SIZE aligned
* and n*PAGE_SIZED sized I/O requests.
@@ -849,6 +902,21 @@ static int create_device(struct zram *zram, int device_id)
ZRAM_LOGICAL_BLOCK_SIZE);
blk_queue_io_min(zram->disk->queue, PAGE_SIZE);
blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
+ zram->disk->queue->limits.discard_granularity = PAGE_SIZE;
+ zram->disk->queue->limits.max_discard_sectors = UINT_MAX;
+ /*
+ * zram_bio_discard() will clear all logical blocks if logical block
+ * size is identical with physical block size(PAGE_SIZE). But if it is
+ * different, we will skip discarding some parts of logical blocks in
+ * the part of the request range which isn't aligned to physical block
+ * size. So we can't ensure that all discarded logical blocks are
+ * zeroed.
+ */
+ if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE)
+ zram->disk->queue->limits.discard_zeroes_data = 1;
+ else
+ zram->disk->queue->limits.discard_zeroes_data = 0;
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, zram->disk->queue);
add_disk(zram->disk);
@@ -858,8 +926,9 @@ static int create_device(struct zram *zram, int device_id)
pr_warn("Error creating sysfs group");
goto out_free_disk;
}
-
- zram->init_done = 0;
+ strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor));
+ zram->meta = NULL;
+ zram->max_comp_streams = 1;
return 0;
out_free_disk:
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index ad8aa35bae00..7f21c145e317 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -16,9 +16,10 @@
#define _ZRAM_DRV_H_
#include <linux/spinlock.h>
-#include <linux/mutex.h>
#include <linux/zsmalloc.h>
+#include "zcomp.h"
+
/*
* Some arbitrary value. This is just to catch
* invalid value for num_devices module parameter.
@@ -64,38 +65,33 @@ enum zram_pageflags {
struct table {
unsigned long handle;
u16 size; /* object size (excluding header) */
- u8 count; /* object ref count (not yet used) */
u8 flags;
} __aligned(4);
struct zram_stats {
- atomic64_t compr_size; /* compressed size of pages stored */
+ atomic64_t compr_data_size; /* compressed size of pages stored */
atomic64_t num_reads; /* failed + successful */
atomic64_t num_writes; /* --do-- */
atomic64_t failed_reads; /* should NEVER! happen */
atomic64_t failed_writes; /* can happen when memory is too low */
atomic64_t invalid_io; /* non-page-aligned I/O requests */
atomic64_t notify_free; /* no. of swap slot free notifications */
- atomic_t pages_zero; /* no. of zero filled pages */
- atomic_t pages_stored; /* no. of pages currently stored */
- atomic_t good_compress; /* % of pages with compression ratio<=50% */
- atomic_t bad_compress; /* % of pages with compression ratio>=75% */
+ atomic64_t zero_pages; /* no. of zero filled pages */
+ atomic64_t pages_stored; /* no. of pages currently stored */
};
struct zram_meta {
rwlock_t tb_lock; /* protect table */
- void *compress_workmem;
- void *compress_buffer;
struct table *table;
struct zs_pool *mem_pool;
- struct mutex buffer_lock; /* protect compress buffers */
};
struct zram {
struct zram_meta *meta;
struct request_queue *queue;
struct gendisk *disk;
- int init_done;
+ struct zcomp *comp;
+
/* Prevent concurrent execution of device init, reset and R/W request */
struct rw_semaphore init_lock;
/*
@@ -103,7 +99,8 @@ struct zram {
* we can store in a disk.
*/
u64 disksize; /* bytes */
-
+ int max_comp_streams;
struct zram_stats stats;
+ char compressor[10];
};
#endif
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index 962fd35cbd8d..5a86da97a70b 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -31,7 +31,6 @@
#define DRIVER_NAME "CCI-400"
#define DRIVER_NAME_PMU DRIVER_NAME " PMU"
-#define PMU_NAME "CCI_400"
#define CCI_PORT_CTRL 0x0
#define CCI_CTRL_STATUS 0xc
@@ -88,8 +87,7 @@ static unsigned long cci_ctrl_phys;
#define CCI_REV_R0 0
#define CCI_REV_R1 1
-#define CCI_REV_R0_P4 4
-#define CCI_REV_R1_P2 6
+#define CCI_REV_R1_PX 5
#define CCI_PMU_EVT_SEL 0x000
#define CCI_PMU_CNTR 0x004
@@ -163,6 +161,15 @@ static struct pmu_port_event_ranges port_event_range[] = {
},
};
+/*
+ * Export different PMU names for the different revisions so userspace knows
+ * because the event ids are different
+ */
+static char *const pmu_names[] = {
+ [CCI_REV_R0] = "CCI_400",
+ [CCI_REV_R1] = "CCI_400_r1",
+};
+
struct cci_pmu_drv_data {
void __iomem *base;
struct arm_pmu *cci_pmu;
@@ -193,21 +200,16 @@ static int probe_cci_revision(void)
rev = readl_relaxed(cci_ctrl_base + CCI_PID2) & CCI_PID2_REV_MASK;
rev >>= CCI_PID2_REV_SHIFT;
- if (rev <= CCI_REV_R0_P4)
+ if (rev < CCI_REV_R1_PX)
return CCI_REV_R0;
- else if (rev <= CCI_REV_R1_P2)
+ else
return CCI_REV_R1;
-
- return -ENOENT;
}
static struct pmu_port_event_ranges *port_range_by_rev(void)
{
int rev = probe_cci_revision();
- if (rev < 0)
- return NULL;
-
return &port_event_range[rev];
}
@@ -526,7 +528,7 @@ static void pmu_write_counter(struct perf_event *event, u32 value)
static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev)
{
*cci_pmu = (struct arm_pmu){
- .name = PMU_NAME,
+ .name = pmu_names[probe_cci_revision()],
.max_period = (1LLU << 32) - 1,
.get_hw_events = pmu_get_hw_events,
.get_event_idx = pmu_get_event_idx,
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 3ef58c8dbf11..f8ee13c7bf7b 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -11,6 +11,9 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/regmap.h>
struct imx_weim_devtype {
unsigned int cs_count;
@@ -56,6 +59,55 @@ static const struct of_device_id weim_id_table[] = {
};
MODULE_DEVICE_TABLE(of, weim_id_table);
+static int __init imx_weim_gpr_setup(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct property *prop;
+ const __be32 *p;
+ struct regmap *gpr;
+ u32 gprvals[4] = {
+ 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
+ 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */
+ 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */
+ 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */
+ };
+ u32 gprval = 0;
+ u32 val;
+ int cs = 0;
+ int i = 0;
+
+ gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
+ if (IS_ERR(gpr)) {
+ dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
+ return 0;
+ }
+
+ of_property_for_each_u32(np, "ranges", prop, p, val) {
+ if (i % 4 == 0) {
+ cs = val;
+ } else if (i % 4 == 3 && val) {
+ val = (val / SZ_32M) | 1;
+ gprval |= val << cs * 3;
+ }
+ i++;
+ }
+
+ if (i == 0 || i % 4)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
+ if (gprval == gprvals[i]) {
+ /* Found it. Set up IOMUXC_GPR1[11:0] with it. */
+ regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
+ return 0;
+ }
+ }
+
+err:
+ dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
+ return -EINVAL;
+}
+
/* Parse and set the timing for this device. */
static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
const struct imx_weim_devtype *devtype)
@@ -92,6 +144,12 @@ static int __init weim_parse_dt(struct platform_device *pdev,
struct device_node *child;
int ret;
+ if (devtype == &imx50_weim_devtype) {
+ ret = imx_weim_gpr_setup(pdev);
+ if (ret)
+ return ret;
+ }
+
for_each_child_of_node(pdev->dev.of_node, child) {
if (!child->name)
continue;
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 2ac754e18bcf..293e2e0a0a87 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -890,13 +890,12 @@ int __init mvebu_mbus_dt_init(void)
const __be32 *prop;
int ret;
- np = of_find_matching_node(NULL, of_mvebu_mbus_ids);
+ np = of_find_matching_node_and_match(NULL, of_mvebu_mbus_ids, &of_id);
if (!np) {
pr_err("could not find a matching SoC family\n");
return -ENODEV;
}
- of_id = of_match_node(of_mvebu_mbus_ids, np);
mbus_state.soc = of_id->data;
prop = of_get_property(np, "controller", NULL);
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 2f2b08457c67..244759bbd7b7 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -342,11 +342,11 @@ config HW_RANDOM_TPM
If unsure, say Y.
config HW_RANDOM_MSM
- tristate "Qualcomm MSM Random Number Generator support"
- depends on HW_RANDOM && ARCH_MSM
+ tristate "Qualcomm SoCs Random Number Generator support"
+ depends on HW_RANDOM && ARCH_QCOM
---help---
This driver provides kernel-side support for the Random Number
- Generator hardware found on Qualcomm MSM SoCs.
+ Generator hardware found on Qualcomm SoCs.
To compile this driver as a module, choose M here. the
module will be called msm-rng.
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 1a65838888cd..c54cac3f8bc8 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -74,7 +74,7 @@ config TCG_NSC
config TCG_ATMEL
tristate "Atmel TPM Interface"
- depends on PPC64 || HAS_IOPORT
+ depends on PPC64 || HAS_IOPORT_MAP
---help---
If you have a TPM security chip from Atmel say Yes and it
will be accessible from within Linux. To compile this driver
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 7641965d208d..6f56d3a4f010 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -65,10 +65,12 @@ config COMMON_CLK_SI570
clock generators.
config COMMON_CLK_S2MPS11
- tristate "Clock driver for S2MPS11 MFD"
+ tristate "Clock driver for S2MPS11/S5M8767 MFD"
depends on MFD_SEC_CORE
---help---
- This driver supports S2MPS11 crystal oscillator clock.
+ This driver supports S2MPS11/S5M8767 crystal oscillator clock. These
+ multi-function devices have 3 fixed-rate oscillators, clocked at
+ 32KHz each.
config CLK_TWL6040
tristate "External McPDM functional clock from twl6040"
@@ -111,4 +113,5 @@ source "drivers/clk/qcom/Kconfig"
endmenu
+source "drivers/clk/bcm/Kconfig"
source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a367a9831717..5f8a28735c96 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
@@ -29,7 +30,9 @@ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
+obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
+obj-$(CONFIG_ARCH_HIP04) += hisilicon/
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
@@ -43,6 +46,7 @@ obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/
obj-$(CONFIG_ARCH_SIRF) += sirf/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
+obj-$(CONFIG_ARCH_STI) += st/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index fd792b203eaf..62e2509f9df1 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -13,12 +13,9 @@
#include <linux/clk/at91_pmc.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
#include "pmc.h"
@@ -38,104 +35,59 @@ struct clk_programmable_layout {
struct clk_programmable {
struct clk_hw hw;
struct at91_pmc *pmc;
- unsigned int irq;
- wait_queue_head_t wait;
u8 id;
- u8 css;
- u8 pres;
- u8 slckmck;
const struct clk_programmable_layout *layout;
};
#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
-
-static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id)
-{
- struct clk_programmable *prog = (struct clk_programmable *)dev_id;
-
- wake_up(&prog->wait);
-
- return IRQ_HANDLED;
-}
-
-static int clk_programmable_prepare(struct clk_hw *hw)
+static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- u32 tmp;
+ u32 pres;
struct clk_programmable *prog = to_clk_programmable(hw);
struct at91_pmc *pmc = prog->pmc;
const struct clk_programmable_layout *layout = prog->layout;
- u8 id = prog->id;
- u32 mask = PROG_STATUS_MASK(id);
-
- tmp = prog->css | (prog->pres << layout->pres_shift);
- if (layout->have_slck_mck && prog->slckmck)
- tmp |= AT91_PMC_CSSMCK_MCK;
-
- pmc_write(pmc, AT91_PMC_PCKR(id), tmp);
-
- while (!(pmc_read(pmc, AT91_PMC_SR) & mask))
- wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask);
- return 0;
+ pres = (pmc_read(pmc, AT91_PMC_PCKR(prog->id)) >> layout->pres_shift) &
+ PROG_PRES_MASK;
+ return parent_rate >> pres;
}
-static int clk_programmable_is_ready(struct clk_hw *hw)
+static long clk_programmable_determine_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_clk)
{
- struct clk_programmable *prog = to_clk_programmable(hw);
- struct at91_pmc *pmc = prog->pmc;
-
- return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id));
-}
+ struct clk *parent = NULL;
+ long best_rate = -EINVAL;
+ unsigned long parent_rate;
+ unsigned long tmp_rate;
+ int shift;
+ int i;
-static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- u32 tmp;
- struct clk_programmable *prog = to_clk_programmable(hw);
- struct at91_pmc *pmc = prog->pmc;
- const struct clk_programmable_layout *layout = prog->layout;
+ for (i = 0; i < __clk_get_num_parents(hw->clk); i++) {
+ parent = clk_get_parent_by_index(hw->clk, i);
+ if (!parent)
+ continue;
- tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
- prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK;
+ parent_rate = __clk_get_rate(parent);
+ for (shift = 0; shift < PROG_PRES_MASK; shift++) {
+ tmp_rate = parent_rate >> shift;
+ if (tmp_rate <= rate)
+ break;
+ }
- return parent_rate >> prog->pres;
-}
+ if (tmp_rate > rate)
+ continue;
-static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
-{
- unsigned long best_rate = *parent_rate;
- unsigned long best_diff;
- unsigned long new_diff;
- unsigned long cur_rate;
- int shift = shift;
-
- if (rate > *parent_rate)
- return *parent_rate;
- else
- best_diff = *parent_rate - rate;
-
- if (!best_diff)
- return best_rate;
-
- for (shift = 1; shift < PROG_PRES_MASK; shift++) {
- cur_rate = *parent_rate >> shift;
-
- if (cur_rate > rate)
- new_diff = cur_rate - rate;
- else
- new_diff = rate - cur_rate;
-
- if (!new_diff)
- return cur_rate;
-
- if (new_diff < best_diff) {
- best_diff = new_diff;
- best_rate = cur_rate;
+ if (best_rate < 0 || (rate - tmp_rate) < (rate - best_rate)) {
+ best_rate = tmp_rate;
+ *best_parent_rate = parent_rate;
+ *best_parent_clk = parent;
}
- if (rate > cur_rate)
+ if (!best_rate)
break;
}
@@ -146,17 +98,22 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
+ struct at91_pmc *pmc = prog->pmc;
+ u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) & ~layout->css_mask;
+
+ if (layout->have_slck_mck)
+ tmp &= AT91_PMC_CSSMCK_MCK;
+
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) {
- prog->css = 0;
- prog->slckmck = 1;
+ tmp |= AT91_PMC_CSSMCK_MCK;
return 0;
} else {
return -EINVAL;
}
}
- prog->css = index;
+ pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | index);
return 0;
}
@@ -169,13 +126,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw)
const struct clk_programmable_layout *layout = prog->layout;
tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
- prog->css = tmp & layout->css_mask;
- ret = prog->css;
- if (layout->have_slck_mck) {
- prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK);
- if (prog->slckmck && !ret)
- ret = PROG_MAX_RM9200_CSS + 1;
- }
+ ret = tmp & layout->css_mask;
+ if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !ret)
+ ret = PROG_MAX_RM9200_CSS + 1;
return ret;
}
@@ -184,67 +137,47 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_programmable *prog = to_clk_programmable(hw);
- unsigned long best_rate = parent_rate;
- unsigned long best_diff;
- unsigned long new_diff;
- unsigned long cur_rate;
+ struct at91_pmc *pmc = prog->pmc;
+ const struct clk_programmable_layout *layout = prog->layout;
+ unsigned long div = parent_rate / rate;
int shift = 0;
+ u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) &
+ ~(PROG_PRES_MASK << layout->pres_shift);
- if (rate > parent_rate)
- return parent_rate;
- else
- best_diff = parent_rate - rate;
-
- if (!best_diff) {
- prog->pres = shift;
- return 0;
- }
+ if (!div)
+ return -EINVAL;
- for (shift = 1; shift < PROG_PRES_MASK; shift++) {
- cur_rate = parent_rate >> shift;
+ shift = fls(div) - 1;
- if (cur_rate > rate)
- new_diff = cur_rate - rate;
- else
- new_diff = rate - cur_rate;
+ if (div != (1<<shift))
+ return -EINVAL;
- if (!new_diff)
- break;
+ if (shift >= PROG_PRES_MASK)
+ return -EINVAL;
- if (new_diff < best_diff) {
- best_diff = new_diff;
- best_rate = cur_rate;
- }
+ pmc_write(pmc, AT91_PMC_PCKR(prog->id),
+ tmp | (shift << layout->pres_shift));
- if (rate > cur_rate)
- break;
- }
-
- prog->pres = shift;
return 0;
}
static const struct clk_ops programmable_ops = {
- .prepare = clk_programmable_prepare,
- .is_prepared = clk_programmable_is_ready,
.recalc_rate = clk_programmable_recalc_rate,
- .round_rate = clk_programmable_round_rate,
+ .determine_rate = clk_programmable_determine_rate,
.get_parent = clk_programmable_get_parent,
.set_parent = clk_programmable_set_parent,
.set_rate = clk_programmable_set_rate,
};
static struct clk * __init
-at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
+at91_clk_register_programmable(struct at91_pmc *pmc,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
const struct clk_programmable_layout *layout)
{
- int ret;
struct clk_programmable *prog;
struct clk *clk = NULL;
struct clk_init_data init;
- char irq_name[11];
if (id > PROG_ID_MAX)
return ERR_PTR(-EINVAL);
@@ -263,14 +196,6 @@ at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
prog->layout = layout;
prog->hw.init = &init;
prog->pmc = pmc;
- prog->irq = irq;
- init_waitqueue_head(&prog->wait);
- irq_set_status_flags(prog->irq, IRQ_NOAUTOEN);
- snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id);
- ret = request_irq(prog->irq, clk_programmable_irq_handler,
- IRQF_TRIGGER_HIGH, irq_name, prog);
- if (ret)
- return ERR_PTR(ret);
clk = clk_register(NULL, &prog->hw);
if (IS_ERR(clk))
@@ -304,7 +229,6 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
int num;
u32 id;
int i;
- unsigned int irq;
struct clk *clk;
int num_parents;
const char *parent_names[PROG_SOURCE_MAX];
@@ -332,11 +256,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
if (of_property_read_string(np, "clock-output-names", &name))
name = progclknp->name;
- irq = irq_of_parse_and_map(progclknp, 0);
- if (!irq)
- continue;
-
- clk = at91_clk_register_programmable(pmc, irq, name,
+ clk = at91_clk_register_programmable(pmc, name,
parent_names, num_parents,
id, layout);
if (IS_ERR(clk))
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 8f7c0434a09f..8c96307d7363 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -14,6 +14,11 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
#include "pmc.h"
@@ -25,19 +30,48 @@
struct clk_system {
struct clk_hw hw;
struct at91_pmc *pmc;
+ unsigned int irq;
+ wait_queue_head_t wait;
u8 id;
};
-static int clk_system_enable(struct clk_hw *hw)
+static inline int is_pck(int id)
+{
+ return (id >= 8) && (id <= 15);
+}
+static irqreturn_t clk_system_irq_handler(int irq, void *dev_id)
+{
+ struct clk_system *sys = (struct clk_system *)dev_id;
+
+ wake_up(&sys->wait);
+ disable_irq_nosync(sys->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int clk_system_prepare(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
struct at91_pmc *pmc = sys->pmc;
+ u32 mask = 1 << sys->id;
- pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id);
+ pmc_write(pmc, AT91_PMC_SCER, mask);
+
+ if (!is_pck(sys->id))
+ return 0;
+
+ while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) {
+ if (sys->irq) {
+ enable_irq(sys->irq);
+ wait_event(sys->wait,
+ pmc_read(pmc, AT91_PMC_SR) & mask);
+ } else
+ cpu_relax();
+ }
return 0;
}
-static void clk_system_disable(struct clk_hw *hw)
+static void clk_system_unprepare(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
struct at91_pmc *pmc = sys->pmc;
@@ -45,27 +79,34 @@ static void clk_system_disable(struct clk_hw *hw)
pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id);
}
-static int clk_system_is_enabled(struct clk_hw *hw)
+static int clk_system_is_prepared(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
struct at91_pmc *pmc = sys->pmc;
- return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id));
+ if (!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id)))
+ return 0;
+
+ if (!is_pck(sys->id))
+ return 1;
+
+ return !!(pmc_read(pmc, AT91_PMC_SR) & (1 << sys->id));
}
static const struct clk_ops system_ops = {
- .enable = clk_system_enable,
- .disable = clk_system_disable,
- .is_enabled = clk_system_is_enabled,
+ .prepare = clk_system_prepare,
+ .unprepare = clk_system_unprepare,
+ .is_prepared = clk_system_is_prepared,
};
static struct clk * __init
at91_clk_register_system(struct at91_pmc *pmc, const char *name,
- const char *parent_name, u8 id)
+ const char *parent_name, u8 id, int irq)
{
struct clk_system *sys;
struct clk *clk = NULL;
struct clk_init_data init;
+ int ret;
if (!parent_name || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
@@ -84,11 +125,20 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
* (see drivers/memory) which would request and enable the ddrck clock.
* When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
*/
- init.flags = CLK_IGNORE_UNUSED;
+ init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
sys->id = id;
sys->hw.init = &init;
sys->pmc = pmc;
+ sys->irq = irq;
+ if (irq) {
+ init_waitqueue_head(&sys->wait);
+ irq_set_status_flags(sys->irq, IRQ_NOAUTOEN);
+ ret = request_irq(sys->irq, clk_system_irq_handler,
+ IRQF_TRIGGER_HIGH, name, sys);
+ if (ret)
+ return ERR_PTR(ret);
+ }
clk = clk_register(NULL, &sys->hw);
if (IS_ERR(clk))
@@ -101,6 +151,7 @@ static void __init
of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
{
int num;
+ int irq = 0;
u32 id;
struct clk *clk;
const char *name;
@@ -118,9 +169,12 @@ of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
if (of_property_read_string(np, "clock-output-names", &name))
name = sysclknp->name;
+ if (is_pck(id))
+ irq = irq_of_parse_and_map(sysclknp, 0);
+
parent_name = of_clk_get_parent_name(sysclknp, 0);
- clk = at91_clk_register_system(pmc, name, parent_name, id);
+ clk = at91_clk_register_system(pmc, name, parent_name, id, irq);
if (IS_ERR(clk))
continue;
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
new file mode 100644
index 000000000000..a7262fb8ce55
--- /dev/null
+++ b/drivers/clk/bcm/Kconfig
@@ -0,0 +1,9 @@
+config CLK_BCM_KONA
+ bool "Broadcom Kona CCU clock support"
+ depends on ARCH_BCM_MOBILE
+ depends on COMMON_CLK
+ default y
+ help
+ Enable common clock framework support for Broadcom SoCs
+ using "Kona" style clock control units, including those
+ in the BCM281xx family.
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
new file mode 100644
index 000000000000..cf93359aa862
--- /dev/null
+++ b/drivers/clk/bcm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
+obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
+obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c
new file mode 100644
index 000000000000..3c66de696aeb
--- /dev/null
+++ b/drivers/clk/bcm/clk-bcm281xx.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "clk-kona.h"
+#include "dt-bindings/clock/bcm281xx.h"
+
+/* bcm11351 CCU device tree "compatible" strings */
+#define BCM11351_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu"
+#define BCM11351_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu"
+#define BCM11351_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu"
+#define BCM11351_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu"
+#define BCM11351_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu"
+
+/* Root CCU clocks */
+
+static struct peri_clk_data frac_1m_data = {
+ .gate = HW_SW_GATE(0x214, 16, 0, 1),
+ .trig = TRIGGER(0x0e04, 0),
+ .div = FRAC_DIVIDER(0x0e00, 0, 22, 16),
+ .clocks = CLOCKS("ref_crystal"),
+};
+
+/* AON CCU clocks */
+
+static struct peri_clk_data hub_timer_data = {
+ .gate = HW_SW_GATE(0x0414, 16, 0, 1),
+ .clocks = CLOCKS("bbl_32k",
+ "frac_1m",
+ "dft_19_5m"),
+ .sel = SELECTOR(0x0a10, 0, 2),
+ .trig = TRIGGER(0x0a40, 4),
+};
+
+static struct peri_clk_data pmu_bsc_data = {
+ .gate = HW_SW_GATE(0x0418, 16, 0, 1),
+ .clocks = CLOCKS("ref_crystal",
+ "pmu_bsc_var",
+ "bbl_32k"),
+ .sel = SELECTOR(0x0a04, 0, 2),
+ .div = DIVIDER(0x0a04, 3, 4),
+ .trig = TRIGGER(0x0a40, 0),
+};
+
+static struct peri_clk_data pmu_bsc_var_data = {
+ .clocks = CLOCKS("var_312m",
+ "ref_312m"),
+ .sel = SELECTOR(0x0a00, 0, 2),
+ .div = DIVIDER(0x0a00, 4, 5),
+ .trig = TRIGGER(0x0a40, 2),
+};
+
+/* Hub CCU clocks */
+
+static struct peri_clk_data tmon_1m_data = {
+ .gate = HW_SW_GATE(0x04a4, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "frac_1m"),
+ .sel = SELECTOR(0x0e74, 0, 2),
+ .trig = TRIGGER(0x0e84, 1),
+};
+
+/* Master CCU clocks */
+
+static struct peri_clk_data sdio1_data = {
+ .gate = HW_SW_GATE(0x0358, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a28, 0, 3),
+ .div = DIVIDER(0x0a28, 4, 14),
+ .trig = TRIGGER(0x0afc, 9),
+};
+
+static struct peri_clk_data sdio2_data = {
+ .gate = HW_SW_GATE(0x035c, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a2c, 0, 3),
+ .div = DIVIDER(0x0a2c, 4, 14),
+ .trig = TRIGGER(0x0afc, 10),
+};
+
+static struct peri_clk_data sdio3_data = {
+ .gate = HW_SW_GATE(0x0364, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a34, 0, 3),
+ .div = DIVIDER(0x0a34, 4, 14),
+ .trig = TRIGGER(0x0afc, 12),
+};
+
+static struct peri_clk_data sdio4_data = {
+ .gate = HW_SW_GATE(0x0360, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_52m",
+ "ref_52m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a30, 0, 3),
+ .div = DIVIDER(0x0a30, 4, 14),
+ .trig = TRIGGER(0x0afc, 11),
+};
+
+static struct peri_clk_data usb_ic_data = {
+ .gate = HW_SW_GATE(0x0354, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_96m",
+ "ref_96m"),
+ .div = FIXED_DIVIDER(2),
+ .sel = SELECTOR(0x0a24, 0, 2),
+ .trig = TRIGGER(0x0afc, 7),
+};
+
+/* also called usbh_48m */
+static struct peri_clk_data hsic2_48m_data = {
+ .gate = HW_SW_GATE(0x0370, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a38, 0, 2),
+ .div = FIXED_DIVIDER(2),
+ .trig = TRIGGER(0x0afc, 5),
+};
+
+/* also called usbh_12m */
+static struct peri_clk_data hsic2_12m_data = {
+ .gate = HW_SW_GATE(0x0370, 20, 4, 5),
+ .div = DIVIDER(0x0a38, 12, 2),
+ .clocks = CLOCKS("ref_crystal",
+ "var_96m",
+ "ref_96m"),
+ .pre_div = FIXED_DIVIDER(2),
+ .sel = SELECTOR(0x0a38, 0, 2),
+ .trig = TRIGGER(0x0afc, 5),
+};
+
+/* Slave CCU clocks */
+
+static struct peri_clk_data uartb_data = {
+ .gate = HW_SW_GATE(0x0400, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a10, 0, 2),
+ .div = FRAC_DIVIDER(0x0a10, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 2),
+};
+
+static struct peri_clk_data uartb2_data = {
+ .gate = HW_SW_GATE(0x0404, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a14, 0, 2),
+ .div = FRAC_DIVIDER(0x0a14, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 3),
+};
+
+static struct peri_clk_data uartb3_data = {
+ .gate = HW_SW_GATE(0x0408, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a18, 0, 2),
+ .div = FRAC_DIVIDER(0x0a18, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 4),
+};
+
+static struct peri_clk_data uartb4_data = {
+ .gate = HW_SW_GATE(0x0408, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_156m",
+ "ref_156m"),
+ .sel = SELECTOR(0x0a1c, 0, 2),
+ .div = FRAC_DIVIDER(0x0a1c, 4, 12, 8),
+ .trig = TRIGGER(0x0afc, 5),
+};
+
+static struct peri_clk_data ssp0_data = {
+ .gate = HW_SW_GATE(0x0410, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a20, 0, 3),
+ .div = DIVIDER(0x0a20, 4, 14),
+ .trig = TRIGGER(0x0afc, 6),
+};
+
+static struct peri_clk_data ssp2_data = {
+ .gate = HW_SW_GATE(0x0418, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_96m",
+ "ref_96m"),
+ .sel = SELECTOR(0x0a28, 0, 3),
+ .div = DIVIDER(0x0a28, 4, 14),
+ .trig = TRIGGER(0x0afc, 8),
+};
+
+static struct peri_clk_data bsc1_data = {
+ .gate = HW_SW_GATE(0x0458, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_13m",
+ "ref_13m"),
+ .sel = SELECTOR(0x0a64, 0, 3),
+ .trig = TRIGGER(0x0afc, 23),
+};
+
+static struct peri_clk_data bsc2_data = {
+ .gate = HW_SW_GATE(0x045c, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_13m",
+ "ref_13m"),
+ .sel = SELECTOR(0x0a68, 0, 3),
+ .trig = TRIGGER(0x0afc, 24),
+};
+
+static struct peri_clk_data bsc3_data = {
+ .gate = HW_SW_GATE(0x0484, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m",
+ "ref_104m",
+ "var_13m",
+ "ref_13m"),
+ .sel = SELECTOR(0x0a84, 0, 3),
+ .trig = TRIGGER(0x0b00, 2),
+};
+
+static struct peri_clk_data pwm_data = {
+ .gate = HW_SW_GATE(0x0468, 18, 2, 3),
+ .clocks = CLOCKS("ref_crystal",
+ "var_104m"),
+ .sel = SELECTOR(0x0a70, 0, 2),
+ .div = DIVIDER(0x0a70, 4, 3),
+ .trig = TRIGGER(0x0afc, 15),
+};
+
+/*
+ * CCU setup routines
+ *
+ * These are called from kona_dt_ccu_setup() to initialize the array
+ * of clocks provided by the CCU. Once allocated, the entries in
+ * the array are initialized by calling kona_clk_setup() with the
+ * initialization data for each clock. They return 0 if successful
+ * or an error code otherwise.
+ */
+static int __init bcm281xx_root_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_ROOT_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate root clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m);
+
+ return 0;
+}
+
+static int __init bcm281xx_aon_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_AON_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate aon clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var);
+
+ return 0;
+}
+
+static int __init bcm281xx_hub_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_HUB_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate hub clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m);
+
+ return 0;
+}
+
+static int __init bcm281xx_master_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_MASTER_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate master clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m);
+
+ return 0;
+}
+
+static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu)
+{
+ struct clk **clks;
+ size_t count = BCM281XX_SLAVE_CCU_CLOCK_COUNT;
+
+ clks = kzalloc(count * sizeof(*clks), GFP_KERNEL);
+ if (!clks) {
+ pr_err("%s: failed to allocate slave clocks\n", __func__);
+ return -ENOMEM;
+ }
+ ccu->data.clks = clks;
+ ccu->data.clk_num = count;
+
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB, uartb);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3);
+ PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_PWM, pwm);
+
+ return 0;
+}
+
+/* Device tree match table callback functions */
+
+static void __init kona_dt_root_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_root_ccu_clks_setup);
+}
+
+static void __init kona_dt_aon_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_aon_ccu_clks_setup);
+}
+
+static void __init kona_dt_hub_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_hub_ccu_clks_setup);
+}
+
+static void __init kona_dt_master_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_master_ccu_clks_setup);
+}
+
+static void __init kona_dt_slave_ccu_setup(struct device_node *node)
+{
+ kona_dt_ccu_setup(node, bcm281xx_slave_ccu_clks_setup);
+}
+
+CLK_OF_DECLARE(bcm11351_root_ccu, BCM11351_DT_ROOT_CCU_COMPAT,
+ kona_dt_root_ccu_setup);
+CLK_OF_DECLARE(bcm11351_aon_ccu, BCM11351_DT_AON_CCU_COMPAT,
+ kona_dt_aon_ccu_setup);
+CLK_OF_DECLARE(bcm11351_hub_ccu, BCM11351_DT_HUB_CCU_COMPAT,
+ kona_dt_hub_ccu_setup);
+CLK_OF_DECLARE(bcm11351_master_ccu, BCM11351_DT_MASTER_CCU_COMPAT,
+ kona_dt_master_ccu_setup);
+CLK_OF_DECLARE(bcm11351_slave_ccu, BCM11351_DT_SLAVE_CCU_COMPAT,
+ kona_dt_slave_ccu_setup);
diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
new file mode 100644
index 000000000000..c7607feb18dd
--- /dev/null
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of_address.h>
+
+#include "clk-kona.h"
+
+/* These are used when a selector or trigger is found to be unneeded */
+#define selector_clear_exists(sel) ((sel)->width = 0)
+#define trigger_clear_exists(trig) FLAG_CLEAR(trig, TRIG, EXISTS)
+
+LIST_HEAD(ccu_list); /* The list of set up CCUs */
+
+/* Validity checking */
+
+static bool clk_requires_trigger(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri = bcm_clk->peri;
+ struct bcm_clk_sel *sel;
+ struct bcm_clk_div *div;
+
+ if (bcm_clk->type != bcm_clk_peri)
+ return false;
+
+ sel = &peri->sel;
+ if (sel->parent_count && selector_exists(sel))
+ return true;
+
+ div = &peri->div;
+ if (!divider_exists(div))
+ return false;
+
+ /* Fixed dividers don't need triggers */
+ if (!divider_is_fixed(div))
+ return true;
+
+ div = &peri->pre_div;
+
+ return divider_exists(div) && !divider_is_fixed(div);
+}
+
+static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_div *div;
+ struct bcm_clk_sel *sel;
+ struct bcm_clk_trig *trig;
+ const char *name;
+ u32 range;
+ u32 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+ peri = bcm_clk->peri;
+ name = bcm_clk->name;
+ range = bcm_clk->ccu->range;
+
+ limit = range - sizeof(u32);
+ limit = round_down(limit, sizeof(u32));
+
+ gate = &peri->gate;
+ if (gate_exists(gate)) {
+ if (gate->offset > limit) {
+ pr_err("%s: bad gate offset for %s (%u > %u)\n",
+ __func__, name, gate->offset, limit);
+ return false;
+ }
+ }
+
+ div = &peri->div;
+ if (divider_exists(div)) {
+ if (div->offset > limit) {
+ pr_err("%s: bad divider offset for %s (%u > %u)\n",
+ __func__, name, div->offset, limit);
+ return false;
+ }
+ }
+
+ div = &peri->pre_div;
+ if (divider_exists(div)) {
+ if (div->offset > limit) {
+ pr_err("%s: bad pre-divider offset for %s "
+ "(%u > %u)\n",
+ __func__, name, div->offset, limit);
+ return false;
+ }
+ }
+
+ sel = &peri->sel;
+ if (selector_exists(sel)) {
+ if (sel->offset > limit) {
+ pr_err("%s: bad selector offset for %s (%u > %u)\n",
+ __func__, name, sel->offset, limit);
+ return false;
+ }
+ }
+
+ trig = &peri->trig;
+ if (trigger_exists(trig)) {
+ if (trig->offset > limit) {
+ pr_err("%s: bad trigger offset for %s (%u > %u)\n",
+ __func__, name, trig->offset, limit);
+ return false;
+ }
+ }
+
+ trig = &peri->pre_trig;
+ if (trigger_exists(trig)) {
+ if (trig->offset > limit) {
+ pr_err("%s: bad pre-trigger offset for %s (%u > %u)\n",
+ __func__, name, trig->offset, limit);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* A bit position must be less than the number of bits in a 32-bit register. */
+static bool bit_posn_valid(u32 bit_posn, const char *field_name,
+ const char *clock_name)
+{
+ u32 limit = BITS_PER_BYTE * sizeof(u32) - 1;
+
+ if (bit_posn > limit) {
+ pr_err("%s: bad %s bit for %s (%u > %u)\n", __func__,
+ field_name, clock_name, bit_posn, limit);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * A bitfield must be at least 1 bit wide. Both the low-order and
+ * high-order bits must lie within a 32-bit register. We require
+ * fields to be less than 32 bits wide, mainly because we use
+ * shifting to produce field masks, and shifting a full word width
+ * is not well-defined by the C standard.
+ */
+static bool bitfield_valid(u32 shift, u32 width, const char *field_name,
+ const char *clock_name)
+{
+ u32 limit = BITS_PER_BYTE * sizeof(u32);
+
+ if (!width) {
+ pr_err("%s: bad %s field width 0 for %s\n", __func__,
+ field_name, clock_name);
+ return false;
+ }
+ if (shift + width > limit) {
+ pr_err("%s: bad %s for %s (%u + %u > %u)\n", __func__,
+ field_name, clock_name, shift, width, limit);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * All gates, if defined, have a status bit, and for hardware-only
+ * gates, that's it. Gates that can be software controlled also
+ * have an enable bit. And a gate that can be hardware or software
+ * controlled will have a hardware/software select bit.
+ */
+static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name,
+ const char *clock_name)
+{
+ if (!bit_posn_valid(gate->status_bit, "gate status", clock_name))
+ return false;
+
+ if (gate_is_sw_controllable(gate)) {
+ if (!bit_posn_valid(gate->en_bit, "gate enable", clock_name))
+ return false;
+
+ if (gate_is_hw_controllable(gate)) {
+ if (!bit_posn_valid(gate->hw_sw_sel_bit,
+ "gate hw/sw select",
+ clock_name))
+ return false;
+ }
+ } else {
+ BUG_ON(!gate_is_hw_controllable(gate));
+ }
+
+ return true;
+}
+
+/*
+ * A selector bitfield must be valid. Its parent_sel array must
+ * also be reasonable for the field.
+ */
+static bool sel_valid(struct bcm_clk_sel *sel, const char *field_name,
+ const char *clock_name)
+{
+ if (!bitfield_valid(sel->shift, sel->width, field_name, clock_name))
+ return false;
+
+ if (sel->parent_count) {
+ u32 max_sel;
+ u32 limit;
+
+ /*
+ * Make sure the selector field can hold all the
+ * selector values we expect to be able to use. A
+ * clock only needs to have a selector defined if it
+ * has more than one parent. And in that case the
+ * highest selector value will be in the last entry
+ * in the array.
+ */
+ max_sel = sel->parent_sel[sel->parent_count - 1];
+ limit = (1 << sel->width) - 1;
+ if (max_sel > limit) {
+ pr_err("%s: bad selector for %s "
+ "(%u needs > %u bits)\n",
+ __func__, clock_name, max_sel,
+ sel->width);
+ return false;
+ }
+ } else {
+ pr_warn("%s: ignoring selector for %s (no parents)\n",
+ __func__, clock_name);
+ selector_clear_exists(sel);
+ kfree(sel->parent_sel);
+ sel->parent_sel = NULL;
+ }
+
+ return true;
+}
+
+/*
+ * A fixed divider just needs to be non-zero. A variable divider
+ * has to have a valid divider bitfield, and if it has a fraction,
+ * the width of the fraction must not be no more than the width of
+ * the divider as a whole.
+ */
+static bool div_valid(struct bcm_clk_div *div, const char *field_name,
+ const char *clock_name)
+{
+ if (divider_is_fixed(div)) {
+ /* Any fixed divider value but 0 is OK */
+ if (div->fixed == 0) {
+ pr_err("%s: bad %s fixed value 0 for %s\n", __func__,
+ field_name, clock_name);
+ return false;
+ }
+ return true;
+ }
+ if (!bitfield_valid(div->shift, div->width, field_name, clock_name))
+ return false;
+
+ if (divider_has_fraction(div))
+ if (div->frac_width > div->width) {
+ pr_warn("%s: bad %s fraction width for %s (%u > %u)\n",
+ __func__, field_name, clock_name,
+ div->frac_width, div->width);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * If a clock has two dividers, the combined number of fractional
+ * bits must be representable in a 32-bit unsigned value. This
+ * is because we scale up a dividend using both dividers before
+ * dividing to improve accuracy, and we need to avoid overflow.
+ */
+static bool kona_dividers_valid(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri = bcm_clk->peri;
+ struct bcm_clk_div *div;
+ struct bcm_clk_div *pre_div;
+ u32 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ if (!divider_exists(&peri->div) || !divider_exists(&peri->pre_div))
+ return true;
+
+ div = &peri->div;
+ pre_div = &peri->pre_div;
+ if (divider_is_fixed(div) || divider_is_fixed(pre_div))
+ return true;
+
+ limit = BITS_PER_BYTE * sizeof(u32);
+
+ return div->frac_width + pre_div->frac_width <= limit;
+}
+
+
+/* A trigger just needs to represent a valid bit position */
+static bool trig_valid(struct bcm_clk_trig *trig, const char *field_name,
+ const char *clock_name)
+{
+ return bit_posn_valid(trig->bit, field_name, clock_name);
+}
+
+/* Determine whether the set of peripheral clock registers are valid. */
+static bool
+peri_clk_data_valid(struct kona_clk *bcm_clk)
+{
+ struct peri_clk_data *peri;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_sel *sel;
+ struct bcm_clk_div *div;
+ struct bcm_clk_div *pre_div;
+ struct bcm_clk_trig *trig;
+ const char *name;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ /*
+ * First validate register offsets. This is the only place
+ * where we need something from the ccu, so we do these
+ * together.
+ */
+ if (!peri_clk_data_offsets_valid(bcm_clk))
+ return false;
+
+ peri = bcm_clk->peri;
+ name = bcm_clk->name;
+ gate = &peri->gate;
+ if (gate_exists(gate) && !gate_valid(gate, "gate", name))
+ return false;
+
+ sel = &peri->sel;
+ if (selector_exists(sel)) {
+ if (!sel_valid(sel, "selector", name))
+ return false;
+
+ } else if (sel->parent_count > 1) {
+ pr_err("%s: multiple parents but no selector for %s\n",
+ __func__, name);
+
+ return false;
+ }
+
+ div = &peri->div;
+ pre_div = &peri->pre_div;
+ if (divider_exists(div)) {
+ if (!div_valid(div, "divider", name))
+ return false;
+
+ if (divider_exists(pre_div))
+ if (!div_valid(pre_div, "pre-divider", name))
+ return false;
+ } else if (divider_exists(pre_div)) {
+ pr_err("%s: pre-divider but no divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ trig = &peri->trig;
+ if (trigger_exists(trig)) {
+ if (!trig_valid(trig, "trigger", name))
+ return false;
+
+ if (trigger_exists(&peri->pre_trig)) {
+ if (!trig_valid(trig, "pre-trigger", name)) {
+ return false;
+ }
+ }
+ if (!clk_requires_trigger(bcm_clk)) {
+ pr_warn("%s: ignoring trigger for %s (not needed)\n",
+ __func__, name);
+ trigger_clear_exists(trig);
+ }
+ } else if (trigger_exists(&peri->pre_trig)) {
+ pr_err("%s: pre-trigger but no trigger for %s\n", __func__,
+ name);
+ return false;
+ } else if (clk_requires_trigger(bcm_clk)) {
+ pr_err("%s: required trigger missing for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ return kona_dividers_valid(bcm_clk);
+}
+
+static bool kona_clk_valid(struct kona_clk *bcm_clk)
+{
+ switch (bcm_clk->type) {
+ case bcm_clk_peri:
+ if (!peri_clk_data_valid(bcm_clk))
+ return false;
+ break;
+ default:
+ pr_err("%s: unrecognized clock type (%d)\n", __func__,
+ (int)bcm_clk->type);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Scan an array of parent clock names to determine whether there
+ * are any entries containing BAD_CLK_NAME. Such entries are
+ * placeholders for non-supported clocks. Keep track of the
+ * position of each clock name in the original array.
+ *
+ * Allocates an array of pointers to to hold the names of all
+ * non-null entries in the original array, and returns a pointer to
+ * that array in *names. This will be used for registering the
+ * clock with the common clock code. On successful return,
+ * *count indicates how many entries are in that names array.
+ *
+ * If there is more than one entry in the resulting names array,
+ * another array is allocated to record the parent selector value
+ * for each (defined) parent clock. This is the value that
+ * represents this parent clock in the clock's source selector
+ * register. The position of the clock in the original parent array
+ * defines that selector value. The number of entries in this array
+ * is the same as the number of entries in the parent names array.
+ *
+ * The array of selector values is returned. If the clock has no
+ * parents, no selector is required and a null pointer is returned.
+ *
+ * Returns a null pointer if the clock names array supplied was
+ * null. (This is not an error.)
+ *
+ * Returns a pointer-coded error if an error occurs.
+ */
+static u32 *parent_process(const char *clocks[],
+ u32 *count, const char ***names)
+{
+ static const char **parent_names;
+ static u32 *parent_sel;
+ const char **clock;
+ u32 parent_count;
+ u32 bad_count = 0;
+ u32 orig_count;
+ u32 i;
+ u32 j;
+
+ *count = 0; /* In case of early return */
+ *names = NULL;
+ if (!clocks)
+ return NULL;
+
+ /*
+ * Count the number of names in the null-terminated array,
+ * and find out how many of those are actually clock names.
+ */
+ for (clock = clocks; *clock; clock++)
+ if (*clock == BAD_CLK_NAME)
+ bad_count++;
+ orig_count = (u32)(clock - clocks);
+ parent_count = orig_count - bad_count;
+
+ /* If all clocks are unsupported, we treat it as no clock */
+ if (!parent_count)
+ return NULL;
+
+ /* Avoid exceeding our parent clock limit */
+ if (parent_count > PARENT_COUNT_MAX) {
+ pr_err("%s: too many parents (%u > %u)\n", __func__,
+ parent_count, PARENT_COUNT_MAX);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /*
+ * There is one parent name for each defined parent clock.
+ * We also maintain an array containing the selector value
+ * for each defined clock. If there's only one clock, the
+ * selector is not required, but we allocate space for the
+ * array anyway to keep things simple.
+ */
+ parent_names = kmalloc(parent_count * sizeof(parent_names), GFP_KERNEL);
+ if (!parent_names) {
+ pr_err("%s: error allocating %u parent names\n", __func__,
+ parent_count);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* There is at least one parent, so allocate a selector array */
+
+ parent_sel = kmalloc(parent_count * sizeof(*parent_sel), GFP_KERNEL);
+ if (!parent_sel) {
+ pr_err("%s: error allocating %u parent selectors\n", __func__,
+ parent_count);
+ kfree(parent_names);
+
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Now fill in the parent names and selector arrays */
+ for (i = 0, j = 0; i < orig_count; i++) {
+ if (clocks[i] != BAD_CLK_NAME) {
+ parent_names[j] = clocks[i];
+ parent_sel[j] = i;
+ j++;
+ }
+ }
+ *names = parent_names;
+ *count = parent_count;
+
+ return parent_sel;
+}
+
+static int
+clk_sel_setup(const char **clocks, struct bcm_clk_sel *sel,
+ struct clk_init_data *init_data)
+{
+ const char **parent_names = NULL;
+ u32 parent_count = 0;
+ u32 *parent_sel;
+
+ /*
+ * If a peripheral clock has multiple parents, the value
+ * used by the hardware to select that parent is represented
+ * by the parent clock's position in the "clocks" list. Some
+ * values don't have defined or supported clocks; these will
+ * have BAD_CLK_NAME entries in the parents[] array. The
+ * list is terminated by a NULL entry.
+ *
+ * We need to supply (only) the names of defined parent
+ * clocks when registering a clock though, so we use an
+ * array of parent selector values to map between the
+ * indexes the common clock code uses and the selector
+ * values we need.
+ */
+ parent_sel = parent_process(clocks, &parent_count, &parent_names);
+ if (IS_ERR(parent_sel)) {
+ int ret = PTR_ERR(parent_sel);
+
+ pr_err("%s: error processing parent clocks for %s (%d)\n",
+ __func__, init_data->name, ret);
+
+ return ret;
+ }
+
+ init_data->parent_names = parent_names;
+ init_data->num_parents = parent_count;
+
+ sel->parent_count = parent_count;
+ sel->parent_sel = parent_sel;
+
+ return 0;
+}
+
+static void clk_sel_teardown(struct bcm_clk_sel *sel,
+ struct clk_init_data *init_data)
+{
+ kfree(sel->parent_sel);
+ sel->parent_sel = NULL;
+ sel->parent_count = 0;
+
+ init_data->num_parents = 0;
+ kfree(init_data->parent_names);
+ init_data->parent_names = NULL;
+}
+
+static void peri_clk_teardown(struct peri_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ clk_sel_teardown(&data->sel, init_data);
+ init_data->ops = NULL;
+}
+
+/*
+ * Caller is responsible for freeing the parent_names[] and
+ * parent_sel[] arrays in the peripheral clock's "data" structure
+ * that can be assigned if the clock has one or more parent clocks
+ * associated with it.
+ */
+static int peri_clk_setup(struct ccu_data *ccu, struct peri_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ init_data->ops = &kona_peri_clk_ops;
+ init_data->flags = CLK_IGNORE_UNUSED;
+
+ return clk_sel_setup(data->clocks, &data->sel, init_data);
+}
+
+static void bcm_clk_teardown(struct kona_clk *bcm_clk)
+{
+ switch (bcm_clk->type) {
+ case bcm_clk_peri:
+ peri_clk_teardown(bcm_clk->data, &bcm_clk->init_data);
+ break;
+ default:
+ break;
+ }
+ bcm_clk->data = NULL;
+ bcm_clk->type = bcm_clk_none;
+}
+
+static void kona_clk_teardown(struct clk *clk)
+{
+ struct clk_hw *hw;
+ struct kona_clk *bcm_clk;
+
+ if (!clk)
+ return;
+
+ hw = __clk_get_hw(clk);
+ if (!hw) {
+ pr_err("%s: clk %p has null hw pointer\n", __func__, clk);
+ return;
+ }
+ clk_unregister(clk);
+
+ bcm_clk = to_kona_clk(hw);
+ bcm_clk_teardown(bcm_clk);
+}
+
+struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
+ enum bcm_clk_type type, void *data)
+{
+ struct kona_clk *bcm_clk;
+ struct clk_init_data *init_data;
+ struct clk *clk = NULL;
+
+ bcm_clk = kzalloc(sizeof(*bcm_clk), GFP_KERNEL);
+ if (!bcm_clk) {
+ pr_err("%s: failed to allocate bcm_clk for %s\n", __func__,
+ name);
+ return NULL;
+ }
+ bcm_clk->ccu = ccu;
+ bcm_clk->name = name;
+
+ init_data = &bcm_clk->init_data;
+ init_data->name = name;
+ switch (type) {
+ case bcm_clk_peri:
+ if (peri_clk_setup(ccu, data, init_data))
+ goto out_free;
+ break;
+ default:
+ data = NULL;
+ break;
+ }
+ bcm_clk->type = type;
+ bcm_clk->data = data;
+
+ /* Make sure everything makes sense before we set it up */
+ if (!kona_clk_valid(bcm_clk)) {
+ pr_err("%s: clock data invalid for %s\n", __func__, name);
+ goto out_teardown;
+ }
+
+ bcm_clk->hw.init = init_data;
+ clk = clk_register(NULL, &bcm_clk->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: error registering clock %s (%ld)\n", __func__,
+ name, PTR_ERR(clk));
+ goto out_teardown;
+ }
+ BUG_ON(!clk);
+
+ return clk;
+out_teardown:
+ bcm_clk_teardown(bcm_clk);
+out_free:
+ kfree(bcm_clk);
+
+ return NULL;
+}
+
+static void ccu_clks_teardown(struct ccu_data *ccu)
+{
+ u32 i;
+
+ for (i = 0; i < ccu->data.clk_num; i++)
+ kona_clk_teardown(ccu->data.clks[i]);
+ kfree(ccu->data.clks);
+}
+
+static void kona_ccu_teardown(struct ccu_data *ccu)
+{
+ if (!ccu)
+ return;
+
+ if (!ccu->base)
+ goto done;
+
+ of_clk_del_provider(ccu->node); /* safe if never added */
+ ccu_clks_teardown(ccu);
+ list_del(&ccu->links);
+ of_node_put(ccu->node);
+ iounmap(ccu->base);
+done:
+ kfree(ccu->name);
+ kfree(ccu);
+}
+
+/*
+ * Set up a CCU. Call the provided ccu_clks_setup callback to
+ * initialize the array of clocks provided by the CCU.
+ */
+void __init kona_dt_ccu_setup(struct device_node *node,
+ int (*ccu_clks_setup)(struct ccu_data *))
+{
+ struct ccu_data *ccu;
+ struct resource res = { 0 };
+ resource_size_t range;
+ int ret;
+
+ ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
+ if (ccu)
+ ccu->name = kstrdup(node->name, GFP_KERNEL);
+ if (!ccu || !ccu->name) {
+ pr_err("%s: unable to allocate CCU struct for %s\n",
+ __func__, node->name);
+ kfree(ccu);
+
+ return;
+ }
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ pr_err("%s: no valid CCU registers found for %s\n", __func__,
+ node->name);
+ goto out_err;
+ }
+
+ range = resource_size(&res);
+ if (range > (resource_size_t)U32_MAX) {
+ pr_err("%s: address range too large for %s\n", __func__,
+ node->name);
+ goto out_err;
+ }
+
+ ccu->range = (u32)range;
+ ccu->base = ioremap(res.start, ccu->range);
+ if (!ccu->base) {
+ pr_err("%s: unable to map CCU registers for %s\n", __func__,
+ node->name);
+ goto out_err;
+ }
+
+ spin_lock_init(&ccu->lock);
+ INIT_LIST_HEAD(&ccu->links);
+ ccu->node = of_node_get(node);
+
+ list_add_tail(&ccu->links, &ccu_list);
+
+ /* Set up clocks array (in ccu->data) */
+ if (ccu_clks_setup(ccu))
+ goto out_err;
+
+ ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->data);
+ if (ret) {
+ pr_err("%s: error adding ccu %s as provider (%d)\n", __func__,
+ node->name, ret);
+ goto out_err;
+ }
+
+ if (!kona_ccu_init(ccu))
+ pr_err("Broadcom %s initialization had errors\n", node->name);
+
+ return;
+out_err:
+ kona_ccu_teardown(ccu);
+ pr_err("Broadcom %s setup aborted\n", node->name);
+}
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
new file mode 100644
index 000000000000..e3d339e08309
--- /dev/null
+++ b/drivers/clk/bcm/clk-kona.c
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "clk-kona.h"
+
+#include <linux/delay.h>
+
+#define CCU_ACCESS_PASSWORD 0xA5A500
+#define CLK_GATE_DELAY_LOOP 2000
+
+/* Bitfield operations */
+
+/* Produces a mask of set bits covering a range of a 32-bit value */
+static inline u32 bitfield_mask(u32 shift, u32 width)
+{
+ return ((1 << width) - 1) << shift;
+}
+
+/* Extract the value of a bitfield found within a given register value */
+static inline u32 bitfield_extract(u32 reg_val, u32 shift, u32 width)
+{
+ return (reg_val & bitfield_mask(shift, width)) >> shift;
+}
+
+/* Replace the value of a bitfield found within a given register value */
+static inline u32 bitfield_replace(u32 reg_val, u32 shift, u32 width, u32 val)
+{
+ u32 mask = bitfield_mask(shift, width);
+
+ return (reg_val & ~mask) | (val << shift);
+}
+
+/* Divider and scaling helpers */
+
+/*
+ * Implement DIV_ROUND_CLOSEST() for 64-bit dividend and both values
+ * unsigned. Note that unlike do_div(), the remainder is discarded
+ * and the return value is the quotient (not the remainder).
+ */
+u64 do_div_round_closest(u64 dividend, unsigned long divisor)
+{
+ u64 result;
+
+ result = dividend + ((u64)divisor >> 1);
+ (void)do_div(result, divisor);
+
+ return result;
+}
+
+/* Convert a divider into the scaled divisor value it represents. */
+static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div)
+{
+ return (u64)reg_div + ((u64)1 << div->frac_width);
+}
+
+/*
+ * Build a scaled divider value as close as possible to the
+ * given whole part (div_value) and fractional part (expressed
+ * in billionths).
+ */
+u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths)
+{
+ u64 combined;
+
+ BUG_ON(!div_value);
+ BUG_ON(billionths >= BILLION);
+
+ combined = (u64)div_value * BILLION + billionths;
+ combined <<= div->frac_width;
+
+ return do_div_round_closest(combined, BILLION);
+}
+
+/* The scaled minimum divisor representable by a divider */
+static inline u64
+scaled_div_min(struct bcm_clk_div *div)
+{
+ if (divider_is_fixed(div))
+ return (u64)div->fixed;
+
+ return scaled_div_value(div, 0);
+}
+
+/* The scaled maximum divisor representable by a divider */
+u64 scaled_div_max(struct bcm_clk_div *div)
+{
+ u32 reg_div;
+
+ if (divider_is_fixed(div))
+ return (u64)div->fixed;
+
+ reg_div = ((u32)1 << div->width) - 1;
+
+ return scaled_div_value(div, reg_div);
+}
+
+/*
+ * Convert a scaled divisor into its divider representation as
+ * stored in a divider register field.
+ */
+static inline u32
+divider(struct bcm_clk_div *div, u64 scaled_div)
+{
+ BUG_ON(scaled_div < scaled_div_min(div));
+ BUG_ON(scaled_div > scaled_div_max(div));
+
+ return (u32)(scaled_div - ((u64)1 << div->frac_width));
+}
+
+/* Return a rate scaled for use when dividing by a scaled divisor. */
+static inline u64
+scale_rate(struct bcm_clk_div *div, u32 rate)
+{
+ if (divider_is_fixed(div))
+ return (u64)rate;
+
+ return (u64)rate << div->frac_width;
+}
+
+/* CCU access */
+
+/* Read a 32-bit register value from a CCU's address space. */
+static inline u32 __ccu_read(struct ccu_data *ccu, u32 reg_offset)
+{
+ return readl(ccu->base + reg_offset);
+}
+
+/* Write a 32-bit register value into a CCU's address space. */
+static inline void
+__ccu_write(struct ccu_data *ccu, u32 reg_offset, u32 reg_val)
+{
+ writel(reg_val, ccu->base + reg_offset);
+}
+
+static inline unsigned long ccu_lock(struct ccu_data *ccu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccu->lock, flags);
+
+ return flags;
+}
+static inline void ccu_unlock(struct ccu_data *ccu, unsigned long flags)
+{
+ spin_unlock_irqrestore(&ccu->lock, flags);
+}
+
+/*
+ * Enable/disable write access to CCU protected registers. The
+ * WR_ACCESS register for all CCUs is at offset 0.
+ */
+static inline void __ccu_write_enable(struct ccu_data *ccu)
+{
+ if (ccu->write_enabled) {
+ pr_err("%s: access already enabled for %s\n", __func__,
+ ccu->name);
+ return;
+ }
+ ccu->write_enabled = true;
+ __ccu_write(ccu, 0, CCU_ACCESS_PASSWORD | 1);
+}
+
+static inline void __ccu_write_disable(struct ccu_data *ccu)
+{
+ if (!ccu->write_enabled) {
+ pr_err("%s: access wasn't enabled for %s\n", __func__,
+ ccu->name);
+ return;
+ }
+
+ __ccu_write(ccu, 0, CCU_ACCESS_PASSWORD);
+ ccu->write_enabled = false;
+}
+
+/*
+ * Poll a register in a CCU's address space, returning when the
+ * specified bit in that register's value is set (or clear). Delay
+ * a microsecond after each read of the register. Returns true if
+ * successful, or false if we gave up trying.
+ *
+ * Caller must ensure the CCU lock is held.
+ */
+static inline bool
+__ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want)
+{
+ unsigned int tries;
+ u32 bit_mask = 1 << bit;
+
+ for (tries = 0; tries < CLK_GATE_DELAY_LOOP; tries++) {
+ u32 val;
+ bool bit_val;
+
+ val = __ccu_read(ccu, reg_offset);
+ bit_val = (val & bit_mask) != 0;
+ if (bit_val == want)
+ return true;
+ udelay(1);
+ }
+ return false;
+}
+
+/* Gate operations */
+
+/* Determine whether a clock is gated. CCU lock must be held. */
+static bool
+__is_clk_gate_enabled(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ u32 bit_mask;
+ u32 reg_val;
+
+ /* If there is no gate we can assume it's enabled. */
+ if (!gate_exists(gate))
+ return true;
+
+ bit_mask = 1 << gate->status_bit;
+ reg_val = __ccu_read(ccu, gate->offset);
+
+ return (reg_val & bit_mask) != 0;
+}
+
+/* Determine whether a clock is gated. */
+static bool
+is_clk_gate_enabled(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ long flags;
+ bool ret;
+
+ /* Avoid taking the lock if we can */
+ if (!gate_exists(gate))
+ return true;
+
+ flags = ccu_lock(ccu);
+ ret = __is_clk_gate_enabled(ccu, gate);
+ ccu_unlock(ccu, flags);
+
+ return ret;
+}
+
+/*
+ * Commit our desired gate state to the hardware.
+ * Returns true if successful, false otherwise.
+ */
+static bool
+__gate_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ u32 reg_val;
+ u32 mask;
+ bool enabled = false;
+
+ BUG_ON(!gate_exists(gate));
+ if (!gate_is_sw_controllable(gate))
+ return true; /* Nothing we can change */
+
+ reg_val = __ccu_read(ccu, gate->offset);
+
+ /* For a hardware/software gate, set which is in control */
+ if (gate_is_hw_controllable(gate)) {
+ mask = (u32)1 << gate->hw_sw_sel_bit;
+ if (gate_is_sw_managed(gate))
+ reg_val |= mask;
+ else
+ reg_val &= ~mask;
+ }
+
+ /*
+ * If software is in control, enable or disable the gate.
+ * If hardware is, clear the enabled bit for good measure.
+ * If a software controlled gate can't be disabled, we're
+ * required to write a 0 into the enable bit (but the gate
+ * will be enabled).
+ */
+ mask = (u32)1 << gate->en_bit;
+ if (gate_is_sw_managed(gate) && (enabled = gate_is_enabled(gate)) &&
+ !gate_is_no_disable(gate))
+ reg_val |= mask;
+ else
+ reg_val &= ~mask;
+
+ __ccu_write(ccu, gate->offset, reg_val);
+
+ /* For a hardware controlled gate, we're done */
+ if (!gate_is_sw_managed(gate))
+ return true;
+
+ /* Otherwise wait for the gate to be in desired state */
+ return __ccu_wait_bit(ccu, gate->offset, gate->status_bit, enabled);
+}
+
+/*
+ * Initialize a gate. Our desired state (hardware/software select,
+ * and if software, its enable state) is committed to hardware
+ * without the usual checks to see if it's already set up that way.
+ * Returns true if successful, false otherwise.
+ */
+static bool gate_init(struct ccu_data *ccu, struct bcm_clk_gate *gate)
+{
+ if (!gate_exists(gate))
+ return true;
+ return __gate_commit(ccu, gate);
+}
+
+/*
+ * Set a gate to enabled or disabled state. Does nothing if the
+ * gate is not currently under software control, or if it is already
+ * in the requested state. Returns true if successful, false
+ * otherwise. CCU lock must be held.
+ */
+static bool
+__clk_gate(struct ccu_data *ccu, struct bcm_clk_gate *gate, bool enable)
+{
+ bool ret;
+
+ if (!gate_exists(gate) || !gate_is_sw_managed(gate))
+ return true; /* Nothing to do */
+
+ if (!enable && gate_is_no_disable(gate)) {
+ pr_warn("%s: invalid gate disable request (ignoring)\n",
+ __func__);
+ return true;
+ }
+
+ if (enable == gate_is_enabled(gate))
+ return true; /* No change */
+
+ gate_flip_enabled(gate);
+ ret = __gate_commit(ccu, gate);
+ if (!ret)
+ gate_flip_enabled(gate); /* Revert the change */
+
+ return ret;
+}
+
+/* Enable or disable a gate. Returns 0 if successful, -EIO otherwise */
+static int clk_gate(struct ccu_data *ccu, const char *name,
+ struct bcm_clk_gate *gate, bool enable)
+{
+ unsigned long flags;
+ bool success;
+
+ /*
+ * Avoid taking the lock if we can. We quietly ignore
+ * requests to change state that don't make sense.
+ */
+ if (!gate_exists(gate) || !gate_is_sw_managed(gate))
+ return 0;
+ if (!enable && gate_is_no_disable(gate))
+ return 0;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ success = __clk_gate(ccu, gate, enable);
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+
+ if (success)
+ return 0;
+
+ pr_err("%s: failed to %s gate for %s\n", __func__,
+ enable ? "enable" : "disable", name);
+
+ return -EIO;
+}
+
+/* Trigger operations */
+
+/*
+ * Caller must ensure CCU lock is held and access is enabled.
+ * Returns true if successful, false otherwise.
+ */
+static bool __clk_trigger(struct ccu_data *ccu, struct bcm_clk_trig *trig)
+{
+ /* Trigger the clock and wait for it to finish */
+ __ccu_write(ccu, trig->offset, 1 << trig->bit);
+
+ return __ccu_wait_bit(ccu, trig->offset, trig->bit, false);
+}
+
+/* Divider operations */
+
+/* Read a divider value and return the scaled divisor it represents. */
+static u64 divider_read_scaled(struct ccu_data *ccu, struct bcm_clk_div *div)
+{
+ unsigned long flags;
+ u32 reg_val;
+ u32 reg_div;
+
+ if (divider_is_fixed(div))
+ return (u64)div->fixed;
+
+ flags = ccu_lock(ccu);
+ reg_val = __ccu_read(ccu, div->offset);
+ ccu_unlock(ccu, flags);
+
+ /* Extract the full divider field from the register value */
+ reg_div = bitfield_extract(reg_val, div->shift, div->width);
+
+ /* Return the scaled divisor value it represents */
+ return scaled_div_value(div, reg_div);
+}
+
+/*
+ * Convert a divider's scaled divisor value into its recorded form
+ * and commit it into the hardware divider register.
+ *
+ * Returns 0 on success. Returns -EINVAL for invalid arguments.
+ * Returns -ENXIO if gating failed, and -EIO if a trigger failed.
+ */
+static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_div *div, struct bcm_clk_trig *trig)
+{
+ bool enabled;
+ u32 reg_div;
+ u32 reg_val;
+ int ret = 0;
+
+ BUG_ON(divider_is_fixed(div));
+
+ /*
+ * If we're just initializing the divider, and no initial
+ * state was defined in the device tree, we just find out
+ * what its current value is rather than updating it.
+ */
+ if (div->scaled_div == BAD_SCALED_DIV_VALUE) {
+ reg_val = __ccu_read(ccu, div->offset);
+ reg_div = bitfield_extract(reg_val, div->shift, div->width);
+ div->scaled_div = scaled_div_value(div, reg_div);
+
+ return 0;
+ }
+
+ /* Convert the scaled divisor to the value we need to record */
+ reg_div = divider(div, div->scaled_div);
+
+ /* Clock needs to be enabled before changing the rate */
+ enabled = __is_clk_gate_enabled(ccu, gate);
+ if (!enabled && !__clk_gate(ccu, gate, true)) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ /* Replace the divider value and record the result */
+ reg_val = __ccu_read(ccu, div->offset);
+ reg_val = bitfield_replace(reg_val, div->shift, div->width, reg_div);
+ __ccu_write(ccu, div->offset, reg_val);
+
+ /* If the trigger fails we still want to disable the gate */
+ if (!__clk_trigger(ccu, trig))
+ ret = -EIO;
+
+ /* Disable the clock again if it was disabled to begin with */
+ if (!enabled && !__clk_gate(ccu, gate, false))
+ ret = ret ? ret : -ENXIO; /* return first error */
+out:
+ return ret;
+}
+
+/*
+ * Initialize a divider by committing our desired state to hardware
+ * without the usual checks to see if it's already set up that way.
+ * Returns true if successful, false otherwise.
+ */
+static bool div_init(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_div *div, struct bcm_clk_trig *trig)
+{
+ if (!divider_exists(div) || divider_is_fixed(div))
+ return true;
+ return !__div_commit(ccu, gate, div, trig);
+}
+
+static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_div *div, struct bcm_clk_trig *trig,
+ u64 scaled_div)
+{
+ unsigned long flags;
+ u64 previous;
+ int ret;
+
+ BUG_ON(divider_is_fixed(div));
+
+ previous = div->scaled_div;
+ if (previous == scaled_div)
+ return 0; /* No change */
+
+ div->scaled_div = scaled_div;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ ret = __div_commit(ccu, gate, div, trig);
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+
+ if (ret)
+ div->scaled_div = previous; /* Revert the change */
+
+ return ret;
+
+}
+
+/* Common clock rate helpers */
+
+/*
+ * Implement the common clock framework recalc_rate method, taking
+ * into account a divider and an optional pre-divider. The
+ * pre-divider register pointer may be NULL.
+ */
+static unsigned long clk_recalc_rate(struct ccu_data *ccu,
+ struct bcm_clk_div *div, struct bcm_clk_div *pre_div,
+ unsigned long parent_rate)
+{
+ u64 scaled_parent_rate;
+ u64 scaled_div;
+ u64 result;
+
+ if (!divider_exists(div))
+ return parent_rate;
+
+ if (parent_rate > (unsigned long)LONG_MAX)
+ return 0; /* actually this would be a caller bug */
+
+ /*
+ * If there is a pre-divider, divide the scaled parent rate
+ * by the pre-divider value first. In this case--to improve
+ * accuracy--scale the parent rate by *both* the pre-divider
+ * value and the divider before actually computing the
+ * result of the pre-divider.
+ *
+ * If there's only one divider, just scale the parent rate.
+ */
+ if (pre_div && divider_exists(pre_div)) {
+ u64 scaled_rate;
+
+ scaled_rate = scale_rate(pre_div, parent_rate);
+ scaled_rate = scale_rate(div, scaled_rate);
+ scaled_div = divider_read_scaled(ccu, pre_div);
+ scaled_parent_rate = do_div_round_closest(scaled_rate,
+ scaled_div);
+ } else {
+ scaled_parent_rate = scale_rate(div, parent_rate);
+ }
+
+ /*
+ * Get the scaled divisor value, and divide the scaled
+ * parent rate by that to determine this clock's resulting
+ * rate.
+ */
+ scaled_div = divider_read_scaled(ccu, div);
+ result = do_div_round_closest(scaled_parent_rate, scaled_div);
+
+ return (unsigned long)result;
+}
+
+/*
+ * Compute the output rate produced when a given parent rate is fed
+ * into two dividers. The pre-divider can be NULL, and even if it's
+ * non-null it may be nonexistent. It's also OK for the divider to
+ * be nonexistent, and in that case the pre-divider is also ignored.
+ *
+ * If scaled_div is non-null, it is used to return the scaled divisor
+ * value used by the (downstream) divider to produce that rate.
+ */
+static long round_rate(struct ccu_data *ccu, struct bcm_clk_div *div,
+ struct bcm_clk_div *pre_div,
+ unsigned long rate, unsigned long parent_rate,
+ u64 *scaled_div)
+{
+ u64 scaled_parent_rate;
+ u64 min_scaled_div;
+ u64 max_scaled_div;
+ u64 best_scaled_div;
+ u64 result;
+
+ BUG_ON(!divider_exists(div));
+ BUG_ON(!rate);
+ BUG_ON(parent_rate > (u64)LONG_MAX);
+
+ /*
+ * If there is a pre-divider, divide the scaled parent rate
+ * by the pre-divider value first. In this case--to improve
+ * accuracy--scale the parent rate by *both* the pre-divider
+ * value and the divider before actually computing the
+ * result of the pre-divider.
+ *
+ * If there's only one divider, just scale the parent rate.
+ *
+ * For simplicity we treat the pre-divider as fixed (for now).
+ */
+ if (divider_exists(pre_div)) {
+ u64 scaled_rate;
+ u64 scaled_pre_div;
+
+ scaled_rate = scale_rate(pre_div, parent_rate);
+ scaled_rate = scale_rate(div, scaled_rate);
+ scaled_pre_div = divider_read_scaled(ccu, pre_div);
+ scaled_parent_rate = do_div_round_closest(scaled_rate,
+ scaled_pre_div);
+ } else {
+ scaled_parent_rate = scale_rate(div, parent_rate);
+ }
+
+ /*
+ * Compute the best possible divider and ensure it is in
+ * range. A fixed divider can't be changed, so just report
+ * the best we can do.
+ */
+ if (!divider_is_fixed(div)) {
+ best_scaled_div = do_div_round_closest(scaled_parent_rate,
+ rate);
+ min_scaled_div = scaled_div_min(div);
+ max_scaled_div = scaled_div_max(div);
+ if (best_scaled_div > max_scaled_div)
+ best_scaled_div = max_scaled_div;
+ else if (best_scaled_div < min_scaled_div)
+ best_scaled_div = min_scaled_div;
+ } else {
+ best_scaled_div = divider_read_scaled(ccu, div);
+ }
+
+ /* OK, figure out the resulting rate */
+ result = do_div_round_closest(scaled_parent_rate, best_scaled_div);
+
+ if (scaled_div)
+ *scaled_div = best_scaled_div;
+
+ return (long)result;
+}
+
+/* Common clock parent helpers */
+
+/*
+ * For a given parent selector (register field) value, find the
+ * index into a selector's parent_sel array that contains it.
+ * Returns the index, or BAD_CLK_INDEX if it's not found.
+ */
+static u8 parent_index(struct bcm_clk_sel *sel, u8 parent_sel)
+{
+ u8 i;
+
+ BUG_ON(sel->parent_count > (u32)U8_MAX);
+ for (i = 0; i < sel->parent_count; i++)
+ if (sel->parent_sel[i] == parent_sel)
+ return i;
+ return BAD_CLK_INDEX;
+}
+
+/*
+ * Fetch the current value of the selector, and translate that into
+ * its corresponding index in the parent array we registered with
+ * the clock framework.
+ *
+ * Returns parent array index that corresponds with the value found,
+ * or BAD_CLK_INDEX if the found value is out of range.
+ */
+static u8 selector_read_index(struct ccu_data *ccu, struct bcm_clk_sel *sel)
+{
+ unsigned long flags;
+ u32 reg_val;
+ u32 parent_sel;
+ u8 index;
+
+ /* If there's no selector, there's only one parent */
+ if (!selector_exists(sel))
+ return 0;
+
+ /* Get the value in the selector register */
+ flags = ccu_lock(ccu);
+ reg_val = __ccu_read(ccu, sel->offset);
+ ccu_unlock(ccu, flags);
+
+ parent_sel = bitfield_extract(reg_val, sel->shift, sel->width);
+
+ /* Look up that selector's parent array index and return it */
+ index = parent_index(sel, parent_sel);
+ if (index == BAD_CLK_INDEX)
+ pr_err("%s: out-of-range parent selector %u (%s 0x%04x)\n",
+ __func__, parent_sel, ccu->name, sel->offset);
+
+ return index;
+}
+
+/*
+ * Commit our desired selector value to the hardware.
+ *
+ * Returns 0 on success. Returns -EINVAL for invalid arguments.
+ * Returns -ENXIO if gating failed, and -EIO if a trigger failed.
+ */
+static int
+__sel_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_sel *sel, struct bcm_clk_trig *trig)
+{
+ u32 parent_sel;
+ u32 reg_val;
+ bool enabled;
+ int ret = 0;
+
+ BUG_ON(!selector_exists(sel));
+
+ /*
+ * If we're just initializing the selector, and no initial
+ * state was defined in the device tree, we just find out
+ * what its current value is rather than updating it.
+ */
+ if (sel->clk_index == BAD_CLK_INDEX) {
+ u8 index;
+
+ reg_val = __ccu_read(ccu, sel->offset);
+ parent_sel = bitfield_extract(reg_val, sel->shift, sel->width);
+ index = parent_index(sel, parent_sel);
+ if (index == BAD_CLK_INDEX)
+ return -EINVAL;
+ sel->clk_index = index;
+
+ return 0;
+ }
+
+ BUG_ON((u32)sel->clk_index >= sel->parent_count);
+ parent_sel = sel->parent_sel[sel->clk_index];
+
+ /* Clock needs to be enabled before changing the parent */
+ enabled = __is_clk_gate_enabled(ccu, gate);
+ if (!enabled && !__clk_gate(ccu, gate, true))
+ return -ENXIO;
+
+ /* Replace the selector value and record the result */
+ reg_val = __ccu_read(ccu, sel->offset);
+ reg_val = bitfield_replace(reg_val, sel->shift, sel->width, parent_sel);
+ __ccu_write(ccu, sel->offset, reg_val);
+
+ /* If the trigger fails we still want to disable the gate */
+ if (!__clk_trigger(ccu, trig))
+ ret = -EIO;
+
+ /* Disable the clock again if it was disabled to begin with */
+ if (!enabled && !__clk_gate(ccu, gate, false))
+ ret = ret ? ret : -ENXIO; /* return first error */
+
+ return ret;
+}
+
+/*
+ * Initialize a selector by committing our desired state to hardware
+ * without the usual checks to see if it's already set up that way.
+ * Returns true if successful, false otherwise.
+ */
+static bool sel_init(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_sel *sel, struct bcm_clk_trig *trig)
+{
+ if (!selector_exists(sel))
+ return true;
+ return !__sel_commit(ccu, gate, sel, trig);
+}
+
+/*
+ * Write a new value into a selector register to switch to a
+ * different parent clock. Returns 0 on success, or an error code
+ * (from __sel_commit()) otherwise.
+ */
+static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
+ struct bcm_clk_sel *sel, struct bcm_clk_trig *trig,
+ u8 index)
+{
+ unsigned long flags;
+ u8 previous;
+ int ret;
+
+ previous = sel->clk_index;
+ if (previous == index)
+ return 0; /* No change */
+
+ sel->clk_index = index;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ ret = __sel_commit(ccu, gate, sel, trig);
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+
+ if (ret)
+ sel->clk_index = previous; /* Revert the change */
+
+ return ret;
+}
+
+/* Clock operations */
+
+static int kona_peri_clk_enable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+
+ return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true);
+}
+
+static void kona_peri_clk_disable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+
+ (void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false);
+}
+
+static int kona_peri_clk_is_enabled(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+
+ return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0;
+}
+
+static unsigned long kona_peri_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+
+ return clk_recalc_rate(bcm_clk->ccu, &data->div, &data->pre_div,
+ parent_rate);
+}
+
+static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_div *div = &bcm_clk->peri->div;
+
+ if (!divider_exists(div))
+ return __clk_get_rate(hw->clk);
+
+ /* Quietly avoid a zero rate */
+ return round_rate(bcm_clk->ccu, div, &bcm_clk->peri->pre_div,
+ rate ? rate : 1, *parent_rate, NULL);
+}
+
+static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+ struct bcm_clk_sel *sel = &data->sel;
+ struct bcm_clk_trig *trig;
+ int ret;
+
+ BUG_ON(index >= sel->parent_count);
+
+ /* If there's only one parent we don't require a selector */
+ if (!selector_exists(sel))
+ return 0;
+
+ /*
+ * The regular trigger is used by default, but if there's a
+ * pre-trigger we want to use that instead.
+ */
+ trig = trigger_exists(&data->pre_trig) ? &data->pre_trig
+ : &data->trig;
+
+ ret = selector_write(bcm_clk->ccu, &data->gate, sel, trig, index);
+ if (ret == -ENXIO) {
+ pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name);
+ ret = -EIO; /* Don't proliferate weird errors */
+ } else if (ret == -EIO) {
+ pr_err("%s: %strigger failed for %s\n", __func__,
+ trig == &data->pre_trig ? "pre-" : "",
+ bcm_clk->name);
+ }
+
+ return ret;
+}
+
+static u8 kona_peri_clk_get_parent(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+ u8 index;
+
+ index = selector_read_index(bcm_clk->ccu, &data->sel);
+
+ /* Not all callers would handle an out-of-range value gracefully */
+ return index == BAD_CLK_INDEX ? 0 : index;
+}
+
+static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct peri_clk_data *data = bcm_clk->peri;
+ struct bcm_clk_div *div = &data->div;
+ u64 scaled_div = 0;
+ int ret;
+
+ if (parent_rate > (unsigned long)LONG_MAX)
+ return -EINVAL;
+
+ if (rate == __clk_get_rate(hw->clk))
+ return 0;
+
+ if (!divider_exists(div))
+ return rate == parent_rate ? 0 : -EINVAL;
+
+ /*
+ * A fixed divider can't be changed. (Nor can a fixed
+ * pre-divider be, but for now we never actually try to
+ * change that.) Tolerate a request for a no-op change.
+ */
+ if (divider_is_fixed(&data->div))
+ return rate == parent_rate ? 0 : -EINVAL;
+
+ /*
+ * Get the scaled divisor value needed to achieve a clock
+ * rate as close as possible to what was requested, given
+ * the parent clock rate supplied.
+ */
+ (void)round_rate(bcm_clk->ccu, div, &data->pre_div,
+ rate ? rate : 1, parent_rate, &scaled_div);
+
+ /*
+ * We aren't updating any pre-divider at this point, so
+ * we'll use the regular trigger.
+ */
+ ret = divider_write(bcm_clk->ccu, &data->gate, &data->div,
+ &data->trig, scaled_div);
+ if (ret == -ENXIO) {
+ pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name);
+ ret = -EIO; /* Don't proliferate weird errors */
+ } else if (ret == -EIO) {
+ pr_err("%s: trigger failed for %s\n", __func__, bcm_clk->name);
+ }
+
+ return ret;
+}
+
+struct clk_ops kona_peri_clk_ops = {
+ .enable = kona_peri_clk_enable,
+ .disable = kona_peri_clk_disable,
+ .is_enabled = kona_peri_clk_is_enabled,
+ .recalc_rate = kona_peri_clk_recalc_rate,
+ .round_rate = kona_peri_clk_round_rate,
+ .set_parent = kona_peri_clk_set_parent,
+ .get_parent = kona_peri_clk_get_parent,
+ .set_rate = kona_peri_clk_set_rate,
+};
+
+/* Put a peripheral clock into its initial state */
+static bool __peri_clk_init(struct kona_clk *bcm_clk)
+{
+ struct ccu_data *ccu = bcm_clk->ccu;
+ struct peri_clk_data *peri = bcm_clk->peri;
+ const char *name = bcm_clk->name;
+ struct bcm_clk_trig *trig;
+
+ BUG_ON(bcm_clk->type != bcm_clk_peri);
+
+ if (!gate_init(ccu, &peri->gate)) {
+ pr_err("%s: error initializing gate for %s\n", __func__, name);
+ return false;
+ }
+ if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) {
+ pr_err("%s: error initializing divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ /*
+ * For the pre-divider and selector, the pre-trigger is used
+ * if it's present, otherwise we just use the regular trigger.
+ */
+ trig = trigger_exists(&peri->pre_trig) ? &peri->pre_trig
+ : &peri->trig;
+
+ if (!div_init(ccu, &peri->gate, &peri->pre_div, trig)) {
+ pr_err("%s: error initializing pre-divider for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ if (!sel_init(ccu, &peri->gate, &peri->sel, trig)) {
+ pr_err("%s: error initializing selector for %s\n", __func__,
+ name);
+ return false;
+ }
+
+ return true;
+}
+
+static bool __kona_clk_init(struct kona_clk *bcm_clk)
+{
+ switch (bcm_clk->type) {
+ case bcm_clk_peri:
+ return __peri_clk_init(bcm_clk);
+ default:
+ BUG();
+ }
+ return -EINVAL;
+}
+
+/* Set a CCU and all its clocks into their desired initial state */
+bool __init kona_ccu_init(struct ccu_data *ccu)
+{
+ unsigned long flags;
+ unsigned int which;
+ struct clk **clks = ccu->data.clks;
+ bool success = true;
+
+ flags = ccu_lock(ccu);
+ __ccu_write_enable(ccu);
+
+ for (which = 0; which < ccu->data.clk_num; which++) {
+ struct kona_clk *bcm_clk;
+
+ if (!clks[which])
+ continue;
+ bcm_clk = to_kona_clk(__clk_get_hw(clks[which]));
+ success &= __kona_clk_init(bcm_clk);
+ }
+
+ __ccu_write_disable(ccu);
+ ccu_unlock(ccu, flags);
+ return success;
+}
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
new file mode 100644
index 000000000000..5e139adc3dc5
--- /dev/null
+++ b/drivers/clk/bcm/clk-kona.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ * Copyright 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CLK_KONA_H
+#define _CLK_KONA_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+
+#define BILLION 1000000000
+
+/* The common clock framework uses u8 to represent a parent index */
+#define PARENT_COUNT_MAX ((u32)U8_MAX)
+
+#define BAD_CLK_INDEX U8_MAX /* Can't ever be valid */
+#define BAD_CLK_NAME ((const char *)-1)
+
+#define BAD_SCALED_DIV_VALUE U64_MAX
+
+/*
+ * Utility macros for object flag management. If possible, flags
+ * should be defined such that 0 is the desired default value.
+ */
+#define FLAG(type, flag) BCM_CLK_ ## type ## _FLAGS_ ## flag
+#define FLAG_SET(obj, type, flag) ((obj)->flags |= FLAG(type, flag))
+#define FLAG_CLEAR(obj, type, flag) ((obj)->flags &= ~(FLAG(type, flag)))
+#define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag))
+#define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag)))
+
+/* Clock field state tests */
+
+#define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS)
+#define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED)
+#define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW)
+#define gate_is_sw_controllable(gate) FLAG_TEST(gate, GATE, SW)
+#define gate_is_sw_managed(gate) FLAG_TEST(gate, GATE, SW_MANAGED)
+#define gate_is_no_disable(gate) FLAG_TEST(gate, GATE, NO_DISABLE)
+
+#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED)
+
+#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
+#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
+#define divider_has_fraction(div) (!divider_is_fixed(div) && \
+ (div)->frac_width > 0)
+
+#define selector_exists(sel) ((sel)->width != 0)
+#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS)
+
+/* Clock type, used to tell common block what it's part of */
+enum bcm_clk_type {
+ bcm_clk_none, /* undefined clock type */
+ bcm_clk_bus,
+ bcm_clk_core,
+ bcm_clk_peri
+};
+
+/*
+ * Each CCU defines a mapped area of memory containing registers
+ * used to manage clocks implemented by the CCU. Access to memory
+ * within the CCU's space is serialized by a spinlock. Before any
+ * (other) address can be written, a special access "password" value
+ * must be written to its WR_ACCESS register (located at the base
+ * address of the range). We keep track of the name of each CCU as
+ * it is set up, and maintain them in a list.
+ */
+struct ccu_data {
+ void __iomem *base; /* base of mapped address space */
+ spinlock_t lock; /* serialization lock */
+ bool write_enabled; /* write access is currently enabled */
+ struct list_head links; /* for ccu_list */
+ struct device_node *node;
+ struct clk_onecell_data data;
+ const char *name;
+ u32 range; /* byte range of address space */
+};
+
+/*
+ * Gating control and status is managed by a 32-bit gate register.
+ *
+ * There are several types of gating available:
+ * - (no gate)
+ * A clock with no gate is assumed to be always enabled.
+ * - hardware-only gating (auto-gating)
+ * Enabling or disabling clocks with this type of gate is
+ * managed automatically by the hardware. Such clocks can be
+ * considered by the software to be enabled. The current status
+ * of auto-gated clocks can be read from the gate status bit.
+ * - software-only gating
+ * Auto-gating is not available for this type of clock.
+ * Instead, software manages whether it's enabled by setting or
+ * clearing the enable bit. The current gate status of a gate
+ * under software control can be read from the gate status bit.
+ * To ensure a change to the gating status is complete, the
+ * status bit can be polled to verify that the gate has entered
+ * the desired state.
+ * - selectable hardware or software gating
+ * Gating for this type of clock can be configured to be either
+ * under software or hardware control. Which type is in use is
+ * determined by the hw_sw_sel bit of the gate register.
+ */
+struct bcm_clk_gate {
+ u32 offset; /* gate register offset */
+ u32 status_bit; /* 0: gate is disabled; 0: gatge is enabled */
+ u32 en_bit; /* 0: disable; 1: enable */
+ u32 hw_sw_sel_bit; /* 0: hardware gating; 1: software gating */
+ u32 flags; /* BCM_CLK_GATE_FLAGS_* below */
+};
+
+/*
+ * Gate flags:
+ * HW means this gate can be auto-gated
+ * SW means the state of this gate can be software controlled
+ * NO_DISABLE means this gate is (only) enabled if under software control
+ * SW_MANAGED means the status of this gate is under software control
+ * ENABLED means this software-managed gate is *supposed* to be enabled
+ */
+#define BCM_CLK_GATE_FLAGS_EXISTS ((u32)1 << 0) /* Gate is valid */
+#define BCM_CLK_GATE_FLAGS_HW ((u32)1 << 1) /* Can auto-gate */
+#define BCM_CLK_GATE_FLAGS_SW ((u32)1 << 2) /* Software control */
+#define BCM_CLK_GATE_FLAGS_NO_DISABLE ((u32)1 << 3) /* HW or enabled */
+#define BCM_CLK_GATE_FLAGS_SW_MANAGED ((u32)1 << 4) /* SW now in control */
+#define BCM_CLK_GATE_FLAGS_ENABLED ((u32)1 << 5) /* If SW_MANAGED */
+
+/*
+ * Gate initialization macros.
+ *
+ * Any gate initially under software control will be enabled.
+ */
+
+/* A hardware/software gate initially under software control */
+#define HW_SW_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .hw_sw_sel_bit = (_hw_sw_sel_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
+ FLAG(GATE, SW_MANAGED)|FLAG(GATE, ENABLED)| \
+ FLAG(GATE, EXISTS), \
+ }
+
+/* A hardware/software gate initially under hardware control */
+#define HW_SW_GATE_AUTO(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .hw_sw_sel_bit = (_hw_sw_sel_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
+ FLAG(GATE, EXISTS), \
+ }
+
+/* A hardware-or-enabled gate (enabled if not under hardware control) */
+#define HW_ENABLE_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .hw_sw_sel_bit = (_hw_sw_sel_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
+ FLAG(GATE, NO_DISABLE)|FLAG(GATE, EXISTS), \
+ }
+
+/* A software-only gate */
+#define SW_ONLY_GATE(_offset, _status_bit, _en_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .en_bit = (_en_bit), \
+ .flags = FLAG(GATE, SW)|FLAG(GATE, SW_MANAGED)| \
+ FLAG(GATE, ENABLED)|FLAG(GATE, EXISTS), \
+ }
+
+/* A hardware-only gate */
+#define HW_ONLY_GATE(_offset, _status_bit) \
+ { \
+ .offset = (_offset), \
+ .status_bit = (_status_bit), \
+ .flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
+ }
+
+/*
+ * Each clock can have zero, one, or two dividers which change the
+ * output rate of the clock. Each divider can be either fixed or
+ * variable. If there are two dividers, they are the "pre-divider"
+ * and the "regular" or "downstream" divider. If there is only one,
+ * there is no pre-divider.
+ *
+ * A fixed divider is any non-zero (positive) value, and it
+ * indicates how the input rate is affected by the divider.
+ *
+ * The value of a variable divider is maintained in a sub-field of a
+ * 32-bit divider register. The position of the field in the
+ * register is defined by its offset and width. The value recorded
+ * in this field is always 1 less than the value it represents.
+ *
+ * In addition, a variable divider can indicate that some subset
+ * of its bits represent a "fractional" part of the divider. Such
+ * bits comprise the low-order portion of the divider field, and can
+ * be viewed as representing the portion of the divider that lies to
+ * the right of the decimal point. Most variable dividers have zero
+ * fractional bits. Variable dividers with non-zero fraction width
+ * still record a value 1 less than the value they represent; the
+ * added 1 does *not* affect the low-order bit in this case, it
+ * affects the bits above the fractional part only. (Often in this
+ * code a divider field value is distinguished from the value it
+ * represents by referring to the latter as a "divisor".)
+ *
+ * In order to avoid dealing with fractions, divider arithmetic is
+ * performed using "scaled" values. A scaled value is one that's
+ * been left-shifted by the fractional width of a divider. Dividing
+ * a scaled value by a scaled divisor produces the desired quotient
+ * without loss of precision and without any other special handling
+ * for fractions.
+ *
+ * The recorded value of a variable divider can be modified. To
+ * modify either divider (or both), a clock must be enabled (i.e.,
+ * using its gate). In addition, a trigger register (described
+ * below) must be used to commit the change, and polled to verify
+ * the change is complete.
+ */
+struct bcm_clk_div {
+ union {
+ struct { /* variable divider */
+ u32 offset; /* divider register offset */
+ u32 shift; /* field shift */
+ u32 width; /* field width */
+ u32 frac_width; /* field fraction width */
+
+ u64 scaled_div; /* scaled divider value */
+ };
+ u32 fixed; /* non-zero fixed divider value */
+ };
+ u32 flags; /* BCM_CLK_DIV_FLAGS_* below */
+};
+
+/*
+ * Divider flags:
+ * EXISTS means this divider exists
+ * FIXED means it is a fixed-rate divider
+ */
+#define BCM_CLK_DIV_FLAGS_EXISTS ((u32)1 << 0) /* Divider is valid */
+#define BCM_CLK_DIV_FLAGS_FIXED ((u32)1 << 1) /* Fixed-value */
+
+/* Divider initialization macros */
+
+/* A fixed (non-zero) divider */
+#define FIXED_DIVIDER(_value) \
+ { \
+ .fixed = (_value), \
+ .flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \
+ }
+
+/* A divider with an integral divisor */
+#define DIVIDER(_offset, _shift, _width) \
+ { \
+ .offset = (_offset), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .scaled_div = BAD_SCALED_DIV_VALUE, \
+ .flags = FLAG(DIV, EXISTS), \
+ }
+
+/* A divider whose divisor has an integer and fractional part */
+#define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \
+ { \
+ .offset = (_offset), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .frac_width = (_frac_width), \
+ .scaled_div = BAD_SCALED_DIV_VALUE, \
+ .flags = FLAG(DIV, EXISTS), \
+ }
+
+/*
+ * Clocks may have multiple "parent" clocks. If there is more than
+ * one, a selector must be specified to define which of the parent
+ * clocks is currently in use. The selected clock is indicated in a
+ * sub-field of a 32-bit selector register. The range of
+ * representable selector values typically exceeds the number of
+ * available parent clocks. Occasionally the reset value of a
+ * selector field is explicitly set to a (specific) value that does
+ * not correspond to a defined input clock.
+ *
+ * We register all known parent clocks with the common clock code
+ * using a packed array (i.e., no empty slots) of (parent) clock
+ * names, and refer to them later using indexes into that array.
+ * We maintain an array of selector values indexed by common clock
+ * index values in order to map between these common clock indexes
+ * and the selector values used by the hardware.
+ *
+ * Like dividers, a selector can be modified, but to do so a clock
+ * must be enabled, and a trigger must be used to commit the change.
+ */
+struct bcm_clk_sel {
+ u32 offset; /* selector register offset */
+ u32 shift; /* field shift */
+ u32 width; /* field width */
+
+ u32 parent_count; /* number of entries in parent_sel[] */
+ u32 *parent_sel; /* array of parent selector values */
+ u8 clk_index; /* current selected index in parent_sel[] */
+};
+
+/* Selector initialization macro */
+#define SELECTOR(_offset, _shift, _width) \
+ { \
+ .offset = (_offset), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .clk_index = BAD_CLK_INDEX, \
+ }
+
+/*
+ * Making changes to a variable divider or a selector for a clock
+ * requires the use of a trigger. A trigger is defined by a single
+ * bit within a register. To signal a change, a 1 is written into
+ * that bit. To determine when the change has been completed, that
+ * trigger bit is polled; the read value will be 1 while the change
+ * is in progress, and 0 when it is complete.
+ *
+ * Occasionally a clock will have more than one trigger. In this
+ * case, the "pre-trigger" will be used when changing a clock's
+ * selector and/or its pre-divider.
+ */
+struct bcm_clk_trig {
+ u32 offset; /* trigger register offset */
+ u32 bit; /* trigger bit */
+ u32 flags; /* BCM_CLK_TRIG_FLAGS_* below */
+};
+
+/*
+ * Trigger flags:
+ * EXISTS means this trigger exists
+ */
+#define BCM_CLK_TRIG_FLAGS_EXISTS ((u32)1 << 0) /* Trigger is valid */
+
+/* Trigger initialization macro */
+#define TRIGGER(_offset, _bit) \
+ { \
+ .offset = (_offset), \
+ .bit = (_bit), \
+ .flags = FLAG(TRIG, EXISTS), \
+ }
+
+struct peri_clk_data {
+ struct bcm_clk_gate gate;
+ struct bcm_clk_trig pre_trig;
+ struct bcm_clk_div pre_div;
+ struct bcm_clk_trig trig;
+ struct bcm_clk_div div;
+ struct bcm_clk_sel sel;
+ const char *clocks[]; /* must be last; use CLOCKS() to declare */
+};
+#define CLOCKS(...) { __VA_ARGS__, NULL, }
+#define NO_CLOCKS { NULL, } /* Must use of no parent clocks */
+
+struct kona_clk {
+ struct clk_hw hw;
+ struct clk_init_data init_data;
+ const char *name; /* name of this clock */
+ struct ccu_data *ccu; /* ccu this clock is associated with */
+ enum bcm_clk_type type;
+ union {
+ void *data;
+ struct peri_clk_data *peri;
+ };
+};
+#define to_kona_clk(_hw) \
+ container_of(_hw, struct kona_clk, hw)
+
+/* Exported globals */
+
+extern struct clk_ops kona_peri_clk_ops;
+
+/* Help functions */
+
+#define PERI_CLK_SETUP(clks, ccu, id, name) \
+ clks[id] = kona_clk_setup(ccu, #name, bcm_clk_peri, &name ## _data)
+
+/* Externally visible functions */
+
+extern u64 do_div_round_closest(u64 dividend, unsigned long divisor);
+extern u64 scaled_div_max(struct bcm_clk_div *div);
+extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
+ u32 billionths);
+
+extern struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
+ enum bcm_clk_type type, void *data);
+extern void __init kona_dt_ccu_setup(struct device_node *node,
+ int (*ccu_clks_setup)(struct ccu_data *));
+extern bool __init kona_ccu_init(struct ccu_data *ccu);
+
+#endif /* _CLK_KONA_H */
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 8137327847c3..1127ee46b802 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -17,23 +17,75 @@
#include <linux/module.h>
#include <linux/err.h>
-#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
-#define AXI_CLKGEN_REG_CLK_OUT1 0x08
-#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
-#define AXI_CLKGEN_REG_CLK_DIV 0x10
-#define AXI_CLKGEN_REG_CLK_FB1 0x14
-#define AXI_CLKGEN_REG_CLK_FB2 0x18
-#define AXI_CLKGEN_REG_LOCK1 0x1c
-#define AXI_CLKGEN_REG_LOCK2 0x20
-#define AXI_CLKGEN_REG_LOCK3 0x24
-#define AXI_CLKGEN_REG_FILTER1 0x28
-#define AXI_CLKGEN_REG_FILTER2 0x2c
+#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
+#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
+#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
+#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
+#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
+#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
+#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
+#define AXI_CLKGEN_V1_REG_LOCK2 0x20
+#define AXI_CLKGEN_V1_REG_LOCK3 0x24
+#define AXI_CLKGEN_V1_REG_FILTER1 0x28
+#define AXI_CLKGEN_V1_REG_FILTER2 0x2c
+
+#define AXI_CLKGEN_V2_REG_RESET 0x40
+#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
+#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
+
+#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
+#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
+
+#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
+#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
+
+#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
+
+#define MMCM_REG_CLKOUT0_1 0x08
+#define MMCM_REG_CLKOUT0_2 0x09
+#define MMCM_REG_CLK_FB1 0x14
+#define MMCM_REG_CLK_FB2 0x15
+#define MMCM_REG_CLK_DIV 0x16
+#define MMCM_REG_LOCK1 0x18
+#define MMCM_REG_LOCK2 0x19
+#define MMCM_REG_LOCK3 0x1a
+#define MMCM_REG_FILTER1 0x4e
+#define MMCM_REG_FILTER2 0x4f
+
+struct axi_clkgen;
+
+struct axi_clkgen_mmcm_ops {
+ void (*enable)(struct axi_clkgen *axi_clkgen, bool enable);
+ int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg,
+ unsigned int val, unsigned int mask);
+ int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg,
+ unsigned int *val);
+};
struct axi_clkgen {
void __iomem *base;
+ const struct axi_clkgen_mmcm_ops *mmcm_ops;
struct clk_hw clk_hw;
};
+static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
+ bool enable)
+{
+ axi_clkgen->mmcm_ops->enable(axi_clkgen, enable);
+}
+
+static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask);
+}
+
+static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val);
+}
+
static uint32_t axi_clkgen_lookup_filter(unsigned int m)
{
switch (m) {
@@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
*val = readl(axi_clkgen->base + reg);
}
+static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg)
+{
+ switch (reg) {
+ case MMCM_REG_CLKOUT0_1:
+ return AXI_CLKGEN_V1_REG_CLK_OUT1;
+ case MMCM_REG_CLKOUT0_2:
+ return AXI_CLKGEN_V1_REG_CLK_OUT2;
+ case MMCM_REG_CLK_FB1:
+ return AXI_CLKGEN_V1_REG_CLK_FB1;
+ case MMCM_REG_CLK_FB2:
+ return AXI_CLKGEN_V1_REG_CLK_FB2;
+ case MMCM_REG_CLK_DIV:
+ return AXI_CLKGEN_V1_REG_CLK_DIV;
+ case MMCM_REG_LOCK1:
+ return AXI_CLKGEN_V1_REG_LOCK1;
+ case MMCM_REG_LOCK2:
+ return AXI_CLKGEN_V1_REG_LOCK2;
+ case MMCM_REG_LOCK3:
+ return AXI_CLKGEN_V1_REG_LOCK3;
+ case MMCM_REG_FILTER1:
+ return AXI_CLKGEN_V1_REG_FILTER1;
+ case MMCM_REG_FILTER2:
+ return AXI_CLKGEN_V1_REG_FILTER2;
+ default:
+ return 0;
+ }
+}
+
+static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ reg = axi_clkgen_v1_map_mmcm_reg(reg);
+ if (reg == 0)
+ return -EINVAL;
+
+ axi_clkgen_write(axi_clkgen, reg, val);
+
+ return 0;
+}
+
+static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ reg = axi_clkgen_v1_map_mmcm_reg(reg);
+ if (reg == 0)
+ return -EINVAL;
+
+ axi_clkgen_read(axi_clkgen, reg, val);
+
+ return 0;
+}
+
+static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen,
+ bool enable)
+{
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable);
+}
+
+static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = {
+ .write = axi_clkgen_v1_mmcm_write,
+ .read = axi_clkgen_v1_mmcm_read,
+ .enable = axi_clkgen_v1_mmcm_enable,
+};
+
+static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
+{
+ unsigned int timeout = 10000;
+ unsigned int val;
+
+ do {
+ axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
+ } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
+
+ if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
+ return -EIO;
+
+ return val & 0xffff;
+}
+
+static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int *val)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = axi_clkgen_wait_non_busy(axi_clkgen);
+ if (ret < 0)
+ return ret;
+
+ reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
+ reg_val |= (reg << 16);
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
+
+ ret = axi_clkgen_wait_non_busy(axi_clkgen);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen,
+ unsigned int reg, unsigned int val, unsigned int mask)
+{
+ unsigned int reg_val = 0;
+ int ret;
+
+ ret = axi_clkgen_wait_non_busy(axi_clkgen);
+ if (ret < 0)
+ return ret;
+
+ if (mask != 0xffff) {
+ axi_clkgen_v2_mmcm_read(axi_clkgen, reg, &reg_val);
+ reg_val &= ~mask;
+ }
+
+ reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
+
+ return 0;
+}
+
+static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen,
+ bool enable)
+{
+ unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
+
+ if (enable)
+ val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
+
+ axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
+}
+
+static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = {
+ .write = axi_clkgen_v2_mmcm_write,
+ .read = axi_clkgen_v2_mmcm_read,
+ .enable = axi_clkgen_v2_mmcm_enable,
+};
+
static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
{
return container_of(clk_hw, struct axi_clkgen, clk_hw);
@@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
filter = axi_clkgen_lookup_filter(m - 1);
lock = axi_clkgen_lookup_lock(m - 1);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
-
axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
- (high << 6) | low);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
- (edge << 7) | (nocount << 6));
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
+ (high << 6) | low, 0xefff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
+ (edge << 7) | (nocount << 6), 0x03ff);
axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
- (edge << 13) | (nocount << 12) | (high << 6) | low);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
+ (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
- (high << 6) | low);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
- (edge << 7) | (nocount << 6));
-
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
- (((lock >> 16) & 0x1f) << 10) | 0x1);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
- (((lock >> 24) & 0x1f) << 10) | 0x3e9);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
-
- axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
+ (high << 6) | low, 0xefff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
+ (edge << 7) | (nocount << 6), 0x03ff);
+
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
+ (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
+ (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
return 0;
}
@@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
unsigned int reg;
unsigned long long tmp;
- axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
- axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
- axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
if (d == 0 || dout == 0)
@@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
return tmp;
}
+static int axi_clkgen_enable(struct clk_hw *clk_hw)
+{
+ struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+
+ axi_clkgen_mmcm_enable(axi_clkgen, true);
+
+ return 0;
+}
+
+static void axi_clkgen_disable(struct clk_hw *clk_hw)
+{
+ struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
+
+ axi_clkgen_mmcm_enable(axi_clkgen, false);
+}
+
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate,
.set_rate = axi_clkgen_set_rate,
+ .enable = axi_clkgen_enable,
+ .disable = axi_clkgen_disable,
};
+static const struct of_device_id axi_clkgen_ids[] = {
+ {
+ .compatible = "adi,axi-clkgen-1.00.a",
+ .data = &axi_clkgen_v1_mmcm_ops
+ }, {
+ .compatible = "adi,axi-clkgen-2.00.a",
+ .data = &axi_clkgen_v2_mmcm_ops,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
+
static int axi_clkgen_probe(struct platform_device *pdev)
{
+ const struct of_device_id *id;
struct axi_clkgen *axi_clkgen;
struct clk_init_data init;
const char *parent_name;
@@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev)
struct resource *mem;
struct clk *clk;
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
+ if (!id)
+ return -ENODEV;
+
axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
if (!axi_clkgen)
return -ENOMEM;
+ axi_clkgen->mmcm_ops = id->data;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(axi_clkgen->base))
@@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
init.name = clk_name;
init.ops = &axi_clkgen_ops;
- init.flags = 0;
+ init.flags = CLK_SET_RATE_GATE;
init.parent_names = &parent_name;
init.num_parents = 1;
+ axi_clkgen_mmcm_enable(axi_clkgen, false);
+
axi_clkgen->clk_hw.init = &init;
clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
if (IS_ERR(clk))
@@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id axi_clkgen_ids[] = {
- { .compatible = "adi,axi-clkgen-1.00.a" },
- { },
-};
-MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
-
static struct platform_driver axi_clkgen_driver = {
.driver = {
.name = "adi-axi-clkgen",
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 5543b7df8e16..ec22112e569f 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -24,7 +24,7 @@
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
- * rate - rate is adjustable. clk->rate = parent->rate / divisor
+ * rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor)
* parent - fixed parent. No clk_set_parent support
*/
@@ -115,7 +115,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
- return parent_rate / div;
+ return DIV_ROUND_UP(parent_rate, div);
}
/*
@@ -185,7 +185,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
- now = parent_rate / i;
+ now = DIV_ROUND_UP(parent_rate, i);
if (now <= rate && now > best) {
bestdiv = i;
best = now;
@@ -207,7 +207,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
int div;
div = clk_divider_bestdiv(hw, rate, prate);
- return *prate / div;
+ return DIV_ROUND_UP(*prate, div);
}
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -218,7 +218,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long flags = 0;
u32 val;
- div = parent_rate / rate;
+ div = DIV_ROUND_UP(parent_rate, rate);
value = _get_val(divider, div);
if (value > div_mask(divider))
diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c
new file mode 100644
index 000000000000..30a3b6999e10
--- /dev/null
+++ b/drivers/clk/clk-moxart.c
@@ -0,0 +1,97 @@
+/*
+ * MOXA ART SoCs clock driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/clkdev.h>
+
+void __init moxart_of_pll_clk_init(struct device_node *node)
+{
+ static void __iomem *base;
+ struct clk *clk, *ref_clk;
+ unsigned int mul;
+ const char *name = node->name;
+ const char *parent_name;
+
+ of_property_read_string(node, "clock-output-names", &name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s: of_iomap failed\n", node->full_name);
+ return;
+ }
+
+ mul = readl(base + 0x30) >> 3 & 0x3f;
+ iounmap(base);
+
+ ref_clk = of_clk_get(node, 0);
+ if (IS_ERR(ref_clk)) {
+ pr_err("%s: of_clk_get failed\n", node->full_name);
+ return;
+ }
+
+ clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock\n", node->full_name);
+ return;
+ }
+
+ clk_register_clkdev(clk, NULL, name);
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
+ moxart_of_pll_clk_init);
+
+void __init moxart_of_apb_clk_init(struct device_node *node)
+{
+ static void __iomem *base;
+ struct clk *clk, *pll_clk;
+ unsigned int div, val;
+ unsigned int div_idx[] = { 2, 3, 4, 6, 8};
+ const char *name = node->name;
+ const char *parent_name;
+
+ of_property_read_string(node, "clock-output-names", &name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s: of_iomap failed\n", node->full_name);
+ return;
+ }
+
+ val = readl(base + 0xc) >> 4 & 0x7;
+ iounmap(base);
+
+ if (val > 4)
+ val = 0;
+ div = div_idx[val] * 2;
+
+ pll_clk = of_clk_get(node, 0);
+ if (IS_ERR(pll_clk)) {
+ pr_err("%s: of_clk_get failed\n", node->full_name);
+ return;
+ }
+
+ clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock\n", node->full_name);
+ return;
+ }
+
+ clk_register_clkdev(clk, NULL, name);
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock",
+ moxart_of_apb_clk_init);
diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c
index c4f76ed914b0..8b284be4efa4 100644
--- a/drivers/clk/clk-ppc-corenet.c
+++ b/drivers/clk/clk-ppc-corenet.c
@@ -27,7 +27,6 @@ struct cmux_clk {
#define CLKSEL_ADJUST BIT(0)
#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw)
-static void __iomem *base;
static unsigned int clocks_per_pll;
static int cmux_set_parent(struct clk_hw *hw, u8 idx)
@@ -100,7 +99,11 @@ static void __init core_mux_init(struct device_node *np)
pr_err("%s: could not allocate cmux_clk\n", __func__);
goto err_name;
}
- cmux_clk->reg = base + offset;
+ cmux_clk->reg = of_iomap(np, 0);
+ if (!cmux_clk->reg) {
+ pr_err("%s: could not map register\n", __func__);
+ goto err_clk;
+ }
node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen");
if (node && (offset >= 0x80))
@@ -143,38 +146,39 @@ err_name:
static void __init core_pll_init(struct device_node *np)
{
- u32 offset, mult;
+ u32 mult;
int i, rc, count;
const char *clk_name, *parent_name;
struct clk_onecell_data *onecell_data;
struct clk **subclks;
+ void __iomem *base;
- rc = of_property_read_u32(np, "reg", &offset);
- if (rc) {
- pr_err("%s: could not get reg property\n", np->name);
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("clk-ppc: iomap error\n");
return;
}
/* get the multiple of PLL */
- mult = ioread32be(base + offset);
+ mult = ioread32be(base);
/* check if this PLL is disabled */
if (mult & PLL_KILL) {
pr_debug("PLL:%s is disabled\n", np->name);
- return;
+ goto err_map;
}
mult = (mult >> 1) & 0x3f;
parent_name = of_clk_get_parent_name(np, 0);
if (!parent_name) {
pr_err("PLL: %s must have a parent\n", np->name);
- return;
+ goto err_map;
}
count = of_property_count_strings(np, "clock-output-names");
if (count < 0 || count > 4) {
pr_err("%s: clock is not supported\n", np->name);
- return;
+ goto err_map;
}
/* output clock number per PLL */
@@ -183,7 +187,7 @@ static void __init core_pll_init(struct device_node *np)
subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL);
if (!subclks) {
pr_err("%s: could not allocate subclks\n", __func__);
- return;
+ goto err_map;
}
onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
@@ -230,30 +234,52 @@ static void __init core_pll_init(struct device_node *np)
goto err_cell;
}
+ iounmap(base);
return;
err_cell:
kfree(onecell_data);
err_clks:
kfree(subclks);
+err_map:
+ iounmap(base);
+}
+
+static void __init sysclk_init(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ struct device_node *np = of_get_parent(node);
+ u32 rate;
+
+ if (!np) {
+ pr_err("ppc-clk: could not get parent node\n");
+ return;
+ }
+
+ if (of_property_read_u32(np, "clock-frequency", &rate)) {
+ of_node_put(node);
+ return;
+ }
+
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
static const struct of_device_id clk_match[] __initconst = {
- { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
- { .compatible = "fsl,core-pll-clock", .data = core_pll_init, },
- { .compatible = "fsl,core-mux-clock", .data = core_mux_init, },
+ { .compatible = "fsl,qoriq-sysclk-1.0", .data = sysclk_init, },
+ { .compatible = "fsl,qoriq-sysclk-2.0", .data = sysclk_init, },
+ { .compatible = "fsl,qoriq-core-pll-1.0", .data = core_pll_init, },
+ { .compatible = "fsl,qoriq-core-pll-2.0", .data = core_pll_init, },
+ { .compatible = "fsl,qoriq-core-mux-1.0", .data = core_mux_init, },
+ { .compatible = "fsl,qoriq-core-mux-2.0", .data = core_mux_init, },
{}
};
static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
{
- struct device_node *np;
-
- np = pdev->dev.of_node;
- base = of_iomap(np, 0);
- if (!base) {
- dev_err(&pdev->dev, "iomap error\n");
- return -ENOMEM;
- }
of_clk_init(clk_match);
return 0;
diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c
index 00a3abe103a5..f2f62a1bf61a 100644
--- a/drivers/clk/clk-s2mps11.c
+++ b/drivers/clk/clk-s2mps11.c
@@ -27,6 +27,7 @@
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s5m8767.h>
#include <linux/mfd/samsung/core.h>
#define s2mps11_name(a) (a->hw.init->name)
@@ -48,6 +49,7 @@ struct s2mps11_clk {
struct clk_lookup *lookup;
u32 mask;
bool enabled;
+ unsigned int reg;
};
static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
@@ -61,7 +63,7 @@ static int s2mps11_clk_prepare(struct clk_hw *hw)
int ret;
ret = regmap_update_bits(s2mps11->iodev->regmap_pmic,
- S2MPS11_REG_RTC_CTRL,
+ s2mps11->reg,
s2mps11->mask, s2mps11->mask);
if (!ret)
s2mps11->enabled = true;
@@ -74,7 +76,7 @@ static void s2mps11_clk_unprepare(struct clk_hw *hw)
struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
int ret;
- ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, S2MPS11_REG_RTC_CTRL,
+ ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
s2mps11->mask, ~s2mps11->mask);
if (!ret)
@@ -130,9 +132,9 @@ static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev)
int i;
if (!iodev->dev->of_node)
- return NULL;
+ return ERR_PTR(-EINVAL);
- clk_np = of_find_node_by_name(iodev->dev->of_node, "clocks");
+ clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
if (!clk_np) {
dev_err(&pdev->dev, "could not find clock sub-node\n");
return ERR_PTR(-EINVAL);
@@ -155,6 +157,7 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct s2mps11_clk *s2mps11_clks, *s2mps11_clk;
struct device_node *clk_np = NULL;
+ unsigned int s2mps11_reg;
int i, ret = 0;
u32 val;
@@ -169,13 +172,26 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
if (IS_ERR(clk_np))
return PTR_ERR(clk_np);
+ switch(platform_get_device_id(pdev)->driver_data) {
+ case S2MPS11X:
+ s2mps11_reg = S2MPS11_REG_RTC_CTRL;
+ break;
+ case S5M8767X:
+ s2mps11_reg = S5M8767_REG_CTRL1;
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device type\n");
+ return -EINVAL;
+ };
+
for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) {
s2mps11_clk->iodev = iodev;
s2mps11_clk->hw.init = &s2mps11_clks_init[i];
s2mps11_clk->mask = 1 << i;
+ s2mps11_clk->reg = s2mps11_reg;
ret = regmap_read(s2mps11_clk->iodev->regmap_pmic,
- S2MPS11_REG_RTC_CTRL, &val);
+ s2mps11_clk->reg, &val);
if (ret < 0)
goto err_reg;
@@ -241,7 +257,8 @@ static int s2mps11_clk_remove(struct platform_device *pdev)
}
static const struct platform_device_id s2mps11_clk_id[] = {
- { "s2mps11-clk", 0},
+ { "s2mps11-clk", S2MPS11X},
+ { "s5m8767-clk", S5M8767X},
{ },
};
MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c42e608af6bb..dff0373f53c1 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -277,6 +277,10 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
if (!d)
goto err_out;
+ if (clk->ops->debug_init)
+ if (clk->ops->debug_init(clk->hw, clk->dentry))
+ goto err_out;
+
ret = 0;
goto out;
@@ -1339,8 +1343,11 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
if (clk->notifier_count)
ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
- if (ret & NOTIFY_STOP_MASK)
+ if (ret & NOTIFY_STOP_MASK) {
+ pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n",
+ __func__, clk->name, ret);
goto out;
+ }
hlist_for_each_entry(child, &clk->children, child_node) {
ret = __clk_speculate_rates(child, new_rate);
@@ -1588,7 +1595,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
/* notify that we are about to change rates */
fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
if (fail_clk) {
- pr_warn("%s: failed to set %s rate\n", __func__,
+ pr_debug("%s: failed to set %s rate\n", __func__,
fail_clk->name);
clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
ret = -EBUSY;
@@ -2260,20 +2267,11 @@ void __clk_put(struct clk *clk)
* re-enter into the clk framework by calling any top-level clk APIs;
* this will cause a nested prepare_lock mutex.
*
- * Pre-change notifier callbacks will be passed the current, pre-change
- * rate of the clk via struct clk_notifier_data.old_rate. The new,
- * post-change rate of the clk is passed via struct
- * clk_notifier_data.new_rate.
- *
- * Post-change notifiers will pass the now-current, post-change rate of
- * the clk in both struct clk_notifier_data.old_rate and struct
+ * In all notification cases cases (pre, post and abort rate change) the
+ * original clock rate is passed to the callback via struct
+ * clk_notifier_data.old_rate and the new frequency is passed via struct
* clk_notifier_data.new_rate.
*
- * Abort-change notifiers are effectively the opposite of pre-change
- * notifiers: the original pre-change clk rate is passed in via struct
- * clk_notifier_data.new_rate and the failed post-change rate is passed
- * in via struct clk_notifier_data.old_rate.
- *
* clk_notifier_register() must be called from non-atomic context.
* Returns -EINVAL if called with null arguments, -ENOMEM upon
* allocation failure; otherwise, passes along the return value of
@@ -2473,7 +2471,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider);
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
struct of_clk_provider *provider;
- struct clk *clk = ERR_PTR(-ENOENT);
+ struct clk *clk = ERR_PTR(-EPROBE_DEFER);
/* Check if we have such a provider in our array */
list_for_each_entry(provider, &of_clk_providers, link) {
@@ -2506,8 +2504,12 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count);
const char *of_clk_get_parent_name(struct device_node *np, int index)
{
struct of_phandle_args clkspec;
+ struct property *prop;
const char *clk_name;
+ const __be32 *vp;
+ u32 pv;
int rc;
+ int count;
if (index < 0)
return NULL;
@@ -2517,8 +2519,22 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
if (rc)
return NULL;
+ index = clkspec.args_count ? clkspec.args[0] : 0;
+ count = 0;
+
+ /* if there is an indices property, use it to transfer the index
+ * specified into an array offset for the clock-output-names property.
+ */
+ of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) {
+ if (index == pv) {
+ index = count;
+ break;
+ }
+ count++;
+ }
+
if (of_property_read_string_index(clkspec.np, "clock-output-names",
- clkspec.args_count ? clkspec.args[0] : 0,
+ index,
&clk_name) < 0)
clk_name = clkspec.np->name;
@@ -2527,24 +2543,99 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
}
EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
+struct clock_provider {
+ of_clk_init_cb_t clk_init_cb;
+ struct device_node *np;
+ struct list_head node;
+};
+
+static LIST_HEAD(clk_provider_list);
+
+/*
+ * This function looks for a parent clock. If there is one, then it
+ * checks that the provider for this parent clock was initialized, in
+ * this case the parent clock will be ready.
+ */
+static int parent_ready(struct device_node *np)
+{
+ int i = 0;
+
+ while (true) {
+ struct clk *clk = of_clk_get(np, i);
+
+ /* this parent is ready we can check the next one */
+ if (!IS_ERR(clk)) {
+ clk_put(clk);
+ i++;
+ continue;
+ }
+
+ /* at least one parent is not ready, we exit now */
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return 0;
+
+ /*
+ * Here we make assumption that the device tree is
+ * written correctly. So an error means that there is
+ * no more parent. As we didn't exit yet, then the
+ * previous parent are ready. If there is no clock
+ * parent, no need to wait for them, then we can
+ * consider their absence as being ready
+ */
+ return 1;
+ }
+}
+
/**
* of_clk_init() - Scan and init clock providers from the DT
* @matches: array of compatible values and init functions for providers.
*
- * This function scans the device tree for matching clock providers and
- * calls their initialization functions
+ * This function scans the device tree for matching clock providers
+ * and calls their initialization functions. It also does it by trying
+ * to follow the dependencies.
*/
void __init of_clk_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np;
+ struct clock_provider *clk_provider, *next;
+ bool is_init_done;
+ bool force = false;
if (!matches)
matches = &__clk_of_table;
+ /* First prepare the list of the clocks providers */
for_each_matching_node_and_match(np, matches, &match) {
- of_clk_init_cb_t clk_init_cb = match->data;
- clk_init_cb(np);
+ struct clock_provider *parent =
+ kzalloc(sizeof(struct clock_provider), GFP_KERNEL);
+
+ parent->clk_init_cb = match->data;
+ parent->np = np;
+ list_add_tail(&parent->node, &clk_provider_list);
+ }
+
+ while (!list_empty(&clk_provider_list)) {
+ is_init_done = false;
+ list_for_each_entry_safe(clk_provider, next,
+ &clk_provider_list, node) {
+ if (force || parent_ready(clk_provider->np)) {
+ clk_provider->clk_init_cb(clk_provider->np);
+ list_del(&clk_provider->node);
+ kfree(clk_provider);
+ is_init_done = true;
+ }
+ }
+
+ /*
+ * We didn't manage to initialize any of the
+ * remaining providers during the last loop, so now we
+ * initialize all the remaining ones unconditionally
+ * in case the clock parent was not mandatory
+ */
+ if (!is_init_done)
+ force = true;
+
}
}
#endif
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 48f67218247c..a360b2eca5cb 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id)
clk = of_clk_get_by_name(dev->of_node, con_id);
if (!IS_ERR(clk))
return clk;
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return clk;
}
return clk_get_sys(dev_id, con_id);
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index a049108341fc..40b33c6a8257 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -2,4 +2,7 @@
# Hisilicon Clock specific Makefile
#
-obj-y += clk.o clkgate-separated.o clk-hi3620.o
+obj-y += clk.o clkgate-separated.o
+
+obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
+obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
index f24ad6a3a797..339945d2503b 100644
--- a/drivers/clk/hisilicon/clk-hi3620.c
+++ b/drivers/clk/hisilicon/clk-hi3620.c
@@ -210,33 +210,297 @@ static struct hisi_gate_clock hi3620_seperated_gate_clks[] __initdata = {
static void __init hi3620_clk_init(struct device_node *np)
{
- void __iomem *base;
+ struct hisi_clock_data *clk_data;
- if (np) {
- base = of_iomap(np, 0);
- if (!base) {
- pr_err("failed to map Hi3620 clock registers\n");
- return;
- }
- } else {
- pr_err("failed to find Hi3620 clock node in DTS\n");
+ clk_data = hisi_clk_init(np, HI3620_NR_CLKS);
+ if (!clk_data)
return;
- }
-
- hisi_clk_init(np, HI3620_NR_CLKS);
hisi_clk_register_fixed_rate(hi3620_fixed_rate_clks,
ARRAY_SIZE(hi3620_fixed_rate_clks),
- base);
+ clk_data);
hisi_clk_register_fixed_factor(hi3620_fixed_factor_clks,
ARRAY_SIZE(hi3620_fixed_factor_clks),
- base);
+ clk_data);
hisi_clk_register_mux(hi3620_mux_clks, ARRAY_SIZE(hi3620_mux_clks),
- base);
+ clk_data);
hisi_clk_register_divider(hi3620_div_clks, ARRAY_SIZE(hi3620_div_clks),
- base);
+ clk_data);
hisi_clk_register_gate_sep(hi3620_seperated_gate_clks,
ARRAY_SIZE(hi3620_seperated_gate_clks),
- base);
+ clk_data);
}
CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
+
+struct hisi_mmc_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent_name;
+ unsigned long flags;
+ u32 clken_reg;
+ u32 clken_bit;
+ u32 div_reg;
+ u32 div_off;
+ u32 div_bits;
+ u32 drv_reg;
+ u32 drv_off;
+ u32 drv_bits;
+ u32 sam_reg;
+ u32 sam_off;
+ u32 sam_bits;
+};
+
+struct clk_mmc {
+ struct clk_hw hw;
+ u32 id;
+ void __iomem *clken_reg;
+ u32 clken_bit;
+ void __iomem *div_reg;
+ u32 div_off;
+ u32 div_bits;
+ void __iomem *drv_reg;
+ u32 drv_off;
+ u32 drv_bits;
+ void __iomem *sam_reg;
+ u32 sam_off;
+ u32 sam_bits;
+};
+
+#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
+
+static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
+ { HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
+ { HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
+ { HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
+ { HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
+};
+
+static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ switch (parent_rate) {
+ case 26000000:
+ return 13000000;
+ case 180000000:
+ return 25000000;
+ case 360000000:
+ return 50000000;
+ case 720000000:
+ return 100000000;
+ case 1440000000:
+ return 180000000;
+ default:
+ return parent_rate;
+ }
+}
+
+static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_p)
+{
+ struct clk_mmc *mclk = to_mmc(hw);
+ unsigned long best = 0;
+
+ if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
+ rate = 13000000;
+ best = 26000000;
+ } else if (rate <= 26000000) {
+ rate = 25000000;
+ best = 180000000;
+ } else if (rate <= 52000000) {
+ rate = 50000000;
+ best = 360000000;
+ } else if (rate <= 100000000) {
+ rate = 100000000;
+ best = 720000000;
+ } else {
+ /* max is 180M */
+ rate = 180000000;
+ best = 1440000000;
+ }
+ *best_parent_rate = best;
+ return rate;
+}
+
+static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
+{
+ u32 i;
+
+ for (i = 0; i < len; i++) {
+ if (para % 2)
+ val |= 1 << (off + i);
+ else
+ val &= ~(1 << (off + i));
+ para = para >> 1;
+ }
+
+ return val;
+}
+
+static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_mmc *mclk = to_mmc(hw);
+ unsigned long flags;
+ u32 sam, drv, div, val;
+ static DEFINE_SPINLOCK(mmc_clk_lock);
+
+ switch (rate) {
+ case 13000000:
+ sam = 3;
+ drv = 1;
+ div = 1;
+ break;
+ case 25000000:
+ sam = 13;
+ drv = 6;
+ div = 6;
+ break;
+ case 50000000:
+ sam = 3;
+ drv = 6;
+ div = 6;
+ break;
+ case 100000000:
+ sam = 6;
+ drv = 4;
+ div = 6;
+ break;
+ case 180000000:
+ sam = 6;
+ drv = 4;
+ div = 7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&mmc_clk_lock, flags);
+
+ val = readl_relaxed(mclk->clken_reg);
+ val &= ~(1 << mclk->clken_bit);
+ writel_relaxed(val, mclk->clken_reg);
+
+ val = readl_relaxed(mclk->sam_reg);
+ val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
+ writel_relaxed(val, mclk->sam_reg);
+
+ val = readl_relaxed(mclk->drv_reg);
+ val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
+ writel_relaxed(val, mclk->drv_reg);
+
+ val = readl_relaxed(mclk->div_reg);
+ val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
+ writel_relaxed(val, mclk->div_reg);
+
+ val = readl_relaxed(mclk->clken_reg);
+ val |= 1 << mclk->clken_bit;
+ writel_relaxed(val, mclk->clken_reg);
+
+ spin_unlock_irqrestore(&mmc_clk_lock, flags);
+
+ return 0;
+}
+
+static int mmc_clk_prepare(struct clk_hw *hw)
+{
+ struct clk_mmc *mclk = to_mmc(hw);
+ unsigned long rate;
+
+ if (mclk->id == HI3620_MMC_CIUCLK1)
+ rate = 13000000;
+ else
+ rate = 25000000;
+
+ return mmc_clk_set_timing(hw, rate);
+}
+
+static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return mmc_clk_set_timing(hw, rate);
+}
+
+static struct clk_ops clk_mmc_ops = {
+ .prepare = mmc_clk_prepare,
+ .determine_rate = mmc_clk_determine_rate,
+ .set_rate = mmc_clk_set_rate,
+ .recalc_rate = mmc_clk_recalc_rate,
+};
+
+static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
+ void __iomem *base, struct device_node *np)
+{
+ struct clk_mmc *mclk;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
+ if (!mclk) {
+ pr_err("%s: fail to allocate mmc clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = mmc_clk->name;
+ init.ops = &clk_mmc_ops;
+ init.flags = mmc_clk->flags | CLK_IS_BASIC;
+ init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
+ init.num_parents = (mmc_clk->parent_name ? 1 : 0);
+ mclk->hw.init = &init;
+
+ mclk->id = mmc_clk->id;
+ mclk->clken_reg = base + mmc_clk->clken_reg;
+ mclk->clken_bit = mmc_clk->clken_bit;
+ mclk->div_reg = base + mmc_clk->div_reg;
+ mclk->div_off = mmc_clk->div_off;
+ mclk->div_bits = mmc_clk->div_bits;
+ mclk->drv_reg = base + mmc_clk->drv_reg;
+ mclk->drv_off = mmc_clk->drv_off;
+ mclk->drv_bits = mmc_clk->drv_bits;
+ mclk->sam_reg = base + mmc_clk->sam_reg;
+ mclk->sam_off = mmc_clk->sam_off;
+ mclk->sam_bits = mmc_clk->sam_bits;
+
+ clk = clk_register(NULL, &mclk->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ kfree(mclk);
+ return clk;
+}
+
+static void __init hi3620_mmc_clk_init(struct device_node *node)
+{
+ void __iomem *base;
+ int i, num = ARRAY_SIZE(hi3620_mmc_clks);
+ struct clk_onecell_data *clk_data;
+
+ if (!node) {
+ pr_err("failed to find pctrl node in DTS\n");
+ return;
+ }
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("failed to map pctrl\n");
+ return;
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (WARN_ON(!clk_data))
+ return;
+
+ clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
+ if (!clk_data->clks) {
+ pr_err("%s: fail to allocate mmc clk\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < num; i++) {
+ struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
+ clk_data->clks[mmc_clk->id] =
+ hisi_register_clk_mmc(mmc_clk, base, node);
+ }
+
+ clk_data->clk_num = num;
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c
new file mode 100644
index 000000000000..132b57a0ce09
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hip04.c
@@ -0,0 +1,58 @@
+/*
+ * Hisilicon HiP04 clock driver
+ *
+ * Copyright (c) 2013-2014 Hisilicon Limited.
+ * Copyright (c) 2013-2014 Linaro Limited.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <dt-bindings/clock/hip04-clock.h>
+
+#include "clk.h"
+
+/* fixed rate clocks */
+static struct hisi_fixed_rate_clock hip04_fixed_rate_clks[] __initdata = {
+ { HIP04_OSC50M, "osc50m", NULL, CLK_IS_ROOT, 50000000, },
+ { HIP04_CLK_50M, "clk50m", NULL, CLK_IS_ROOT, 50000000, },
+ { HIP04_CLK_168M, "clk168m", NULL, CLK_IS_ROOT, 168750000, },
+};
+
+static void __init hip04_clk_init(struct device_node *np)
+{
+ struct hisi_clock_data *clk_data;
+
+ clk_data = hisi_clk_init(np, HIP04_NR_CLKS);
+ if (!clk_data)
+ return;
+
+ hisi_clk_register_fixed_rate(hip04_fixed_rate_clks,
+ ARRAY_SIZE(hip04_fixed_rate_clks),
+ clk_data);
+}
+CLK_OF_DECLARE(hip04_clk, "hisilicon,hip04-clock", hip04_clk_init);
diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c
index a3a7152c92d9..276f672e7b1a 100644
--- a/drivers/clk/hisilicon/clk.c
+++ b/drivers/clk/hisilicon/clk.c
@@ -37,23 +37,49 @@
#include "clk.h"
static DEFINE_SPINLOCK(hisi_clk_lock);
-static struct clk **clk_table;
-static struct clk_onecell_data clk_data;
-void __init hisi_clk_init(struct device_node *np, int nr_clks)
+struct hisi_clock_data __init *hisi_clk_init(struct device_node *np,
+ int nr_clks)
{
+ struct hisi_clock_data *clk_data;
+ struct clk **clk_table;
+ void __iomem *base;
+
+ if (np) {
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("failed to map Hisilicon clock registers\n");
+ goto err;
+ }
+ } else {
+ pr_err("failed to find Hisilicon clock node in DTS\n");
+ goto err;
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data) {
+ pr_err("%s: could not allocate clock data\n", __func__);
+ goto err;
+ }
+ clk_data->base = base;
+
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table) {
pr_err("%s: could not allocate clock lookup table\n", __func__);
- return;
+ goto err_data;
}
- clk_data.clks = clk_table;
- clk_data.clk_num = nr_clks;
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ clk_data->clk_data.clks = clk_table;
+ clk_data->clk_data.clk_num = nr_clks;
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);
+ return clk_data;
+err_data:
+ kfree(clk_data);
+err:
+ return NULL;
}
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
int i;
@@ -68,11 +94,13 @@ void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
__func__, clks[i].name);
continue;
}
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
- int nums, void __iomem *base)
+ int nums,
+ struct hisi_clock_data *data)
{
struct clk *clk;
int i;
@@ -87,13 +115,15 @@ void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
__func__, clks[i].name);
continue;
}
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
+ void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
@@ -111,14 +141,15 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
- clk_table[clks[i].id] = clk;
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
+ void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
@@ -139,14 +170,15 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
- clk_table[clks[i].id] = clk;
+ data->clk_data.clks[clks[i].id] = clk;
}
}
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
- int nums, void __iomem *base)
+ int nums, struct hisi_clock_data *data)
{
struct clk *clk;
+ void __iomem *base = data->base;
int i;
for (i = 0; i < nums; i++) {
@@ -166,6 +198,6 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
- clk_table[clks[i].id] = clk;
+ data->clk_data.clks[clks[i].id] = clk;
}
}
diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h
index 4a6beebefb7a..43fa5da88f02 100644
--- a/drivers/clk/hisilicon/clk.h
+++ b/drivers/clk/hisilicon/clk.h
@@ -30,6 +30,11 @@
#include <linux/io.h>
#include <linux/spinlock.h>
+struct hisi_clock_data {
+ struct clk_onecell_data clk_data;
+ void __iomem *base;
+};
+
struct hisi_fixed_rate_clock {
unsigned int id;
char *name;
@@ -89,15 +94,15 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *,
void __iomem *, u8,
u8, spinlock_t *);
-void __init hisi_clk_init(struct device_node *, int);
+struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int);
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
- void __iomem *);
+ struct hisi_clock_data *);
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
- int, void __iomem *);
+ int, struct hisi_clock_data *);
#endif /* __HISI_CLK_H */
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c
index 80c1dd15d15c..23a56f561812 100644
--- a/drivers/clk/mmp/clk-frac.c
+++ b/drivers/clk/mmp/clk-frac.c
@@ -40,15 +40,19 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
for (i = 0; i < factor->ftbl_cnt; i++) {
prev_rate = rate;
- rate = (((*prate / 10000) * factor->ftbl[i].num) /
- (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ rate = (((*prate / 10000) * factor->ftbl[i].den) /
+ (factor->ftbl[i].num * factor->masks->factor)) * 10000;
if (rate > drate)
break;
}
- if (i == 0)
+ if ((i == 0) || (i == factor->ftbl_cnt)) {
return rate;
- else
- return prev_rate;
+ } else {
+ if ((drate - prev_rate) > (rate - drate))
+ return rate;
+ else
+ return prev_rate;
+ }
}
static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
@@ -64,7 +68,7 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
num = (val >> masks->num_shift) & masks->num_mask;
/* calculate denominator */
- den = (val >> masks->den_shift) & masks->num_mask;
+ den = (val >> masks->den_shift) & masks->den_mask;
if (!den)
return 0;
@@ -85,8 +89,8 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
for (i = 0; i < factor->ftbl_cnt; i++) {
prev_rate = rate;
- rate = (((prate / 10000) * factor->ftbl[i].num) /
- (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ rate = (((prate / 10000) * factor->ftbl[i].den) /
+ (factor->ftbl[i].num * factor->masks->factor)) * 10000;
if (rate > drate)
break;
}
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
index c339b829d3e3..693f7be129f1 100644
--- a/drivers/clk/mvebu/Kconfig
+++ b/drivers/clk/mvebu/Kconfig
@@ -13,6 +13,14 @@ config ARMADA_370_CLK
select MVEBU_CLK_CPU
select MVEBU_CLK_COREDIV
+config ARMADA_375_CLK
+ bool
+ select MVEBU_CLK_COMMON
+
+config ARMADA_38X_CLK
+ bool
+ select MVEBU_CLK_COMMON
+
config ARMADA_XP_CLK
bool
select MVEBU_CLK_COMMON
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
index 21bbfb4a9f42..4c66162fb0b4 100644
--- a/drivers/clk/mvebu/Makefile
+++ b/drivers/clk/mvebu/Makefile
@@ -3,6 +3,8 @@ obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o
obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o
+obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o
+obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
obj-$(CONFIG_DOVE_CLK) += dove.o
obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o
diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c
new file mode 100644
index 000000000000..c991a4d95e10
--- /dev/null
+++ b/drivers/clk/mvebu/armada-375.c
@@ -0,0 +1,184 @@
+/*
+ * Marvell Armada 375 SoC clocks
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include "common.h"
+
+/*
+ * Core Clocks
+ */
+
+/*
+ * For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are
+ * all modified at the same time, and not separately as for the Armada
+ * 370 or the Armada XP SoCs.
+ *
+ * SAR0[21:17] : CPU frequency DDR frequency L2 frequency
+ * 6 = 400 MHz 400 MHz 200 MHz
+ * 15 = 600 MHz 600 MHz 300 MHz
+ * 21 = 800 MHz 534 MHz 400 MHz
+ * 25 = 1000 MHz 500 MHz 500 MHz
+ * others reserved.
+ *
+ * SAR0[22] : TCLK frequency
+ * 0 = 166 MHz
+ * 1 = 200 MHz
+ */
+
+#define SAR1_A375_TCLK_FREQ_OPT 22
+#define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1
+#define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17
+#define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F
+
+static const u32 armada_375_tclk_frequencies[] __initconst = {
+ 166000000,
+ 200000000,
+};
+
+static u32 __init armada_375_get_tclk_freq(void __iomem *sar)
+{
+ u8 tclk_freq_select;
+
+ tclk_freq_select = ((readl(sar) >> SAR1_A375_TCLK_FREQ_OPT) &
+ SAR1_A375_TCLK_FREQ_OPT_MASK);
+ return armada_375_tclk_frequencies[tclk_freq_select];
+}
+
+
+static const u32 armada_375_cpu_frequencies[] __initconst = {
+ 0, 0, 0, 0, 0, 0,
+ 400000000,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 600000000,
+ 0, 0, 0, 0, 0,
+ 800000000,
+ 0, 0, 0,
+ 1000000000,
+};
+
+static u32 __init armada_375_get_cpu_freq(void __iomem *sar)
+{
+ u8 cpu_freq_select;
+
+ cpu_freq_select = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) &
+ SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) {
+ pr_err("Selected CPU frequency (%d) unsupported\n",
+ cpu_freq_select);
+ return 0;
+ } else
+ return armada_375_cpu_frequencies[cpu_freq_select];
+}
+
+enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 };
+
+static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = {
+ { .id = A375_CPU_TO_L2, .name = "l2clk" },
+ { .id = A375_CPU_TO_DDR, .name = "ddrclk" },
+};
+
+static const int armada_375_cpu_l2_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {1, 2}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 2},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {1, 2}, {0, 1}, {0, 1},
+ {0, 1}, {1, 2}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int armada_375_cpu_ddr_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {1, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {2, 3},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {2, 3}, {0, 1}, {0, 1},
+ {0, 1}, {1, 2}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static void __init armada_375_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ u32 opt = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) &
+ SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK);
+
+ switch (id) {
+ case A375_CPU_TO_L2:
+ *mult = armada_375_cpu_l2_ratios[opt][0];
+ *div = armada_375_cpu_l2_ratios[opt][1];
+ break;
+ case A375_CPU_TO_DDR:
+ *mult = armada_375_cpu_ddr_ratios[opt][0];
+ *div = armada_375_cpu_ddr_ratios[opt][1];
+ break;
+ }
+}
+
+static const struct coreclk_soc_desc armada_375_coreclks = {
+ .get_tclk_freq = armada_375_get_tclk_freq,
+ .get_cpu_freq = armada_375_get_cpu_freq,
+ .get_clk_ratio = armada_375_get_clk_ratio,
+ .ratios = armada_375_coreclk_ratios,
+ .num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios),
+};
+
+static void __init armada_375_coreclk_init(struct device_node *np)
+{
+ mvebu_coreclk_setup(np, &armada_375_coreclks);
+}
+CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock",
+ armada_375_coreclk_init);
+
+/*
+ * Clock Gating Control
+ */
+static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = {
+ { "mu", NULL, 2 },
+ { "pp", NULL, 3 },
+ { "ptp", NULL, 4 },
+ { "pex0", NULL, 5 },
+ { "pex1", NULL, 6 },
+ { "audio", NULL, 8 },
+ { "nd_clk", "nand", 11 },
+ { "sata0_link", "sata0_core", 14 },
+ { "sata0_core", NULL, 15 },
+ { "usb3", NULL, 16 },
+ { "sdio", NULL, 17 },
+ { "usb", NULL, 18 },
+ { "gop", NULL, 19 },
+ { "sata1_link", "sata1_core", 20 },
+ { "sata1_core", NULL, 21 },
+ { "xor0", NULL, 22 },
+ { "xor1", NULL, 23 },
+ { "copro", NULL, 24 },
+ { "tdm", NULL, 25 },
+ { "crypto0_enc", NULL, 28 },
+ { "crypto0_core", NULL, 29 },
+ { "crypto1_enc", NULL, 30 },
+ { "crypto1_core", NULL, 31 },
+ { }
+};
+
+static void __init armada_375_clk_gating_init(struct device_node *np)
+{
+ mvebu_clk_gating_setup(np, armada_375_gating_desc);
+}
+CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock",
+ armada_375_clk_gating_init);
diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c
new file mode 100644
index 000000000000..8bccf4ecdab6
--- /dev/null
+++ b/drivers/clk/mvebu/armada-38x.c
@@ -0,0 +1,167 @@
+/*
+ * Marvell Armada 380/385 SoC clocks
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include "common.h"
+
+/*
+ * SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks
+ *
+ * SAR[15] : TCLK frequency
+ * 0 = 250 MHz
+ * 1 = 200 MHz
+ */
+
+#define SAR_A380_TCLK_FREQ_OPT 15
+#define SAR_A380_TCLK_FREQ_OPT_MASK 0x1
+#define SAR_A380_CPU_DDR_L2_FREQ_OPT 10
+#define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F
+
+static const u32 armada_38x_tclk_frequencies[] __initconst = {
+ 250000000,
+ 200000000,
+};
+
+static u32 __init armada_38x_get_tclk_freq(void __iomem *sar)
+{
+ u8 tclk_freq_select;
+
+ tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) &
+ SAR_A380_TCLK_FREQ_OPT_MASK);
+ return armada_38x_tclk_frequencies[tclk_freq_select];
+}
+
+static const u32 armada_38x_cpu_frequencies[] __initconst = {
+ 0, 0, 0, 0,
+ 1066 * 1000 * 1000, 0, 0, 0,
+ 1332 * 1000 * 1000, 0, 0, 0,
+ 1600 * 1000 * 1000,
+};
+
+static u32 __init armada_38x_get_cpu_freq(void __iomem *sar)
+{
+ u8 cpu_freq_select;
+
+ cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) &
+ SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) {
+ pr_err("Selected CPU frequency (%d) unsupported\n",
+ cpu_freq_select);
+ return 0;
+ }
+
+ return armada_38x_cpu_frequencies[cpu_freq_select];
+}
+
+enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 };
+
+static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = {
+ { .id = A380_CPU_TO_L2, .name = "l2clk" },
+ { .id = A380_CPU_TO_DDR, .name = "ddrclk" },
+};
+
+static const int armada_38x_cpu_l2_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = {
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static void __init armada_38x_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) &
+ SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK);
+
+ switch (id) {
+ case A380_CPU_TO_L2:
+ *mult = armada_38x_cpu_l2_ratios[opt][0];
+ *div = armada_38x_cpu_l2_ratios[opt][1];
+ break;
+ case A380_CPU_TO_DDR:
+ *mult = armada_38x_cpu_ddr_ratios[opt][0];
+ *div = armada_38x_cpu_ddr_ratios[opt][1];
+ break;
+ }
+}
+
+static const struct coreclk_soc_desc armada_38x_coreclks = {
+ .get_tclk_freq = armada_38x_get_tclk_freq,
+ .get_cpu_freq = armada_38x_get_cpu_freq,
+ .get_clk_ratio = armada_38x_get_clk_ratio,
+ .ratios = armada_38x_coreclk_ratios,
+ .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios),
+};
+
+static void __init armada_38x_coreclk_init(struct device_node *np)
+{
+ mvebu_coreclk_setup(np, &armada_38x_coreclks);
+}
+CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock",
+ armada_38x_coreclk_init);
+
+/*
+ * Clock Gating Control
+ */
+static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = {
+ { "audio", NULL, 0 },
+ { "ge2", NULL, 2 },
+ { "ge1", NULL, 3 },
+ { "ge0", NULL, 4 },
+ { "pex1", NULL, 5 },
+ { "pex2", NULL, 6 },
+ { "pex3", NULL, 7 },
+ { "pex0", NULL, 8 },
+ { "usb3h0", NULL, 9 },
+ { "usb3h1", NULL, 10 },
+ { "usb3d", NULL, 11 },
+ { "bm", NULL, 13 },
+ { "crypto0z", NULL, 14 },
+ { "sata0", NULL, 15 },
+ { "crypto1z", NULL, 16 },
+ { "sdio", NULL, 17 },
+ { "usb2", NULL, 18 },
+ { "crypto1", NULL, 21 },
+ { "xor0", NULL, 22 },
+ { "crypto0", NULL, 23 },
+ { "tdm", NULL, 25 },
+ { "xor1", NULL, 28 },
+ { "sata1", NULL, 30 },
+ { }
+};
+
+static void __init armada_38x_clk_gating_init(struct device_node *np)
+{
+ mvebu_clk_gating_setup(np, armada_38x_gating_desc);
+}
+CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock",
+ armada_38x_clk_gating_init);
diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c
index 7162615bcdcd..d1e5863d3375 100644
--- a/drivers/clk/mvebu/clk-corediv.c
+++ b/drivers/clk/mvebu/clk-corediv.c
@@ -18,26 +18,56 @@
#include "common.h"
#define CORE_CLK_DIV_RATIO_MASK 0xff
-#define CORE_CLK_DIV_RATIO_RELOAD BIT(8)
-#define CORE_CLK_DIV_ENABLE_OFFSET 24
-#define CORE_CLK_DIV_RATIO_OFFSET 0x8
+/*
+ * This structure describes the hardware details (bit offset and mask)
+ * to configure one particular core divider clock. Those hardware
+ * details may differ from one SoC to another. This structure is
+ * therefore typically instantiated statically to describe the
+ * hardware details.
+ */
struct clk_corediv_desc {
unsigned int mask;
unsigned int offset;
unsigned int fieldbit;
};
+/*
+ * This structure describes the hardware details to configure the core
+ * divider clocks on a given SoC. Amongst others, it points to the
+ * array of core divider clock descriptors for this SoC, as well as
+ * the corresponding operations to manipulate them.
+ */
+struct clk_corediv_soc_desc {
+ const struct clk_corediv_desc *descs;
+ unsigned int ndescs;
+ const struct clk_ops ops;
+ u32 ratio_reload;
+ u32 enable_bit_offset;
+ u32 ratio_offset;
+};
+
+/*
+ * This structure represents one core divider clock for the clock
+ * framework, and is dynamically allocated for each core divider clock
+ * existing in the current SoC.
+ */
struct clk_corediv {
struct clk_hw hw;
void __iomem *reg;
- struct clk_corediv_desc desc;
+ const struct clk_corediv_desc *desc;
+ const struct clk_corediv_soc_desc *soc_desc;
spinlock_t lock;
};
static struct clk_onecell_data clk_data;
-static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
+/*
+ * Description of the core divider clocks available. For now, we
+ * support only NAND, and it is available at the same register
+ * locations regardless of the SoC.
+ */
+static const struct clk_corediv_desc mvebu_corediv_desc[] = {
{ .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */
};
@@ -46,8 +76,9 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = {
static int clk_corediv_is_enabled(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
- u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
+ u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset;
return !!(readl(corediv->reg) & enable_mask);
}
@@ -55,14 +86,15 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk)
static int clk_corediv_enable(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
unsigned long flags = 0;
u32 reg;
spin_lock_irqsave(&corediv->lock, flags);
reg = readl(corediv->reg);
- reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
+ reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
writel(reg, corediv->reg);
spin_unlock_irqrestore(&corediv->lock, flags);
@@ -73,14 +105,15 @@ static int clk_corediv_enable(struct clk_hw *hwclk)
static void clk_corediv_disable(struct clk_hw *hwclk)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
unsigned long flags = 0;
u32 reg;
spin_lock_irqsave(&corediv->lock, flags);
reg = readl(corediv->reg);
- reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET);
+ reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset);
writel(reg, corediv->reg);
spin_unlock_irqrestore(&corediv->lock, flags);
@@ -90,10 +123,11 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
u32 reg, div;
- reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
+ reg = readl(corediv->reg + soc_desc->ratio_offset);
div = (reg >> desc->offset) & desc->mask;
return parent_rate / div;
}
@@ -117,7 +151,8 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
unsigned long parent_rate)
{
struct clk_corediv *corediv = to_corediv_clk(hwclk);
- struct clk_corediv_desc *desc = &corediv->desc;
+ const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc;
+ const struct clk_corediv_desc *desc = corediv->desc;
unsigned long flags = 0;
u32 reg, div;
@@ -126,17 +161,17 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
spin_lock_irqsave(&corediv->lock, flags);
/* Write new divider to the divider ratio register */
- reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
+ reg = readl(corediv->reg + soc_desc->ratio_offset);
reg &= ~(desc->mask << desc->offset);
reg |= (div & desc->mask) << desc->offset;
- writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET);
+ writel(reg, corediv->reg + soc_desc->ratio_offset);
/* Set reload-force for this clock */
reg = readl(corediv->reg) | BIT(desc->fieldbit);
writel(reg, corediv->reg);
/* Now trigger the clock update */
- reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD;
+ reg = readl(corediv->reg) | soc_desc->ratio_reload;
writel(reg, corediv->reg);
/*
@@ -144,7 +179,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
* ratios request and the reload request.
*/
udelay(1000);
- reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD);
+ reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload);
writel(reg, corediv->reg);
udelay(1000);
@@ -153,16 +188,53 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
return 0;
}
-static const struct clk_ops corediv_ops = {
- .enable = clk_corediv_enable,
- .disable = clk_corediv_disable,
- .is_enabled = clk_corediv_is_enabled,
- .recalc_rate = clk_corediv_recalc_rate,
- .round_rate = clk_corediv_round_rate,
- .set_rate = clk_corediv_set_rate,
+static const struct clk_corediv_soc_desc armada370_corediv_soc = {
+ .descs = mvebu_corediv_desc,
+ .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
+ .ops = {
+ .enable = clk_corediv_enable,
+ .disable = clk_corediv_disable,
+ .is_enabled = clk_corediv_is_enabled,
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(8),
+ .enable_bit_offset = 24,
+ .ratio_offset = 0x8,
+};
+
+static const struct clk_corediv_soc_desc armada380_corediv_soc = {
+ .descs = mvebu_corediv_desc,
+ .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
+ .ops = {
+ .enable = clk_corediv_enable,
+ .disable = clk_corediv_disable,
+ .is_enabled = clk_corediv_is_enabled,
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(8),
+ .enable_bit_offset = 16,
+ .ratio_offset = 0x4,
};
-static void __init mvebu_corediv_clk_init(struct device_node *node)
+static const struct clk_corediv_soc_desc armada375_corediv_soc = {
+ .descs = mvebu_corediv_desc,
+ .ndescs = ARRAY_SIZE(mvebu_corediv_desc),
+ .ops = {
+ .recalc_rate = clk_corediv_recalc_rate,
+ .round_rate = clk_corediv_round_rate,
+ .set_rate = clk_corediv_set_rate,
+ },
+ .ratio_reload = BIT(8),
+ .ratio_offset = 0x4,
+};
+
+static void __init
+mvebu_corediv_clk_init(struct device_node *node,
+ const struct clk_corediv_soc_desc *soc_desc)
{
struct clk_init_data init;
struct clk_corediv *corediv;
@@ -178,7 +250,7 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
parent_name = of_clk_get_parent_name(node, 0);
- clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc);
+ clk_data.clk_num = soc_desc->ndescs;
/* clks holds the clock array */
clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
@@ -199,10 +271,11 @@ static void __init mvebu_corediv_clk_init(struct device_node *node)
init.num_parents = 1;
init.parent_names = &parent_name;
init.name = clk_name;
- init.ops = &corediv_ops;
+ init.ops = &soc_desc->ops;
init.flags = 0;
- corediv[i].desc = mvebu_corediv_desc[i];
+ corediv[i].soc_desc = soc_desc;
+ corediv[i].desc = soc_desc->descs + i;
corediv[i].reg = base;
corediv[i].hw.init = &init;
@@ -219,5 +292,24 @@ err_free_clks:
err_unmap:
iounmap(base);
}
-CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock",
- mvebu_corediv_clk_init);
+
+static void __init armada370_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &armada370_corediv_soc);
+}
+CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock",
+ armada370_corediv_clk_init);
+
+static void __init armada375_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &armada375_corediv_soc);
+}
+CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock",
+ armada375_corediv_clk_init);
+
+static void __init armada380_corediv_clk_init(struct device_node *node)
+{
+ return mvebu_corediv_clk_init(node, &armada380_corediv_soc);
+}
+CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock",
+ armada380_corediv_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index 884187fbfe00..13eae14c2cc2 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -17,7 +17,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <dt-bindings/clk/exynos-audss-clk.h>
+#include <dt-bindings/clock/exynos-audss-clk.h>
enum exynos_audss_clk_type {
TYPE_EXYNOS4210,
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 010f071af883..b4f967210175 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -16,6 +16,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "clk.h"
@@ -130,6 +131,17 @@ enum exynos4_plls {
nr_plls /* number of PLLs */
};
+static void __iomem *reg_base;
+static enum exynos4_soc exynos4_soc;
+
+/*
+ * Support for CMU save/restore across system suspends
+ */
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos4_save_common;
+static struct samsung_clk_reg_dump *exynos4_save_soc;
+static struct samsung_clk_reg_dump *exynos4_save_pll;
+
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
@@ -154,6 +166,17 @@ static unsigned long exynos4x12_clk_save[] __initdata = {
E4X12_MPLL_CON0,
};
+static unsigned long exynos4_clk_pll_regs[] __initdata = {
+ EPLL_LOCK,
+ VPLL_LOCK,
+ EPLL_CON0,
+ EPLL_CON1,
+ EPLL_CON2,
+ VPLL_CON0,
+ VPLL_CON1,
+ VPLL_CON2,
+};
+
static unsigned long exynos4_clk_regs[] __initdata = {
SRC_LEFTBUS,
DIV_LEFTBUS,
@@ -161,12 +184,6 @@ static unsigned long exynos4_clk_regs[] __initdata = {
SRC_RIGHTBUS,
DIV_RIGHTBUS,
GATE_IP_RIGHTBUS,
- EPLL_CON0,
- EPLL_CON1,
- EPLL_CON2,
- VPLL_CON0,
- VPLL_CON1,
- VPLL_CON2,
SRC_TOP0,
SRC_TOP1,
SRC_CAM,
@@ -227,6 +244,124 @@ static unsigned long exynos4_clk_regs[] __initdata = {
GATE_IP_CPU,
};
+static const struct samsung_clk_reg_dump src_mask_suspend[] = {
+ { .offset = SRC_MASK_TOP, .value = 0x00000001, },
+ { .offset = SRC_MASK_CAM, .value = 0x11111111, },
+ { .offset = SRC_MASK_TV, .value = 0x00000111, },
+ { .offset = SRC_MASK_LCD0, .value = 0x00001111, },
+ { .offset = SRC_MASK_MAUDIO, .value = 0x00000001, },
+ { .offset = SRC_MASK_FSYS, .value = 0x01011111, },
+ { .offset = SRC_MASK_PERIL0, .value = 0x01111111, },
+ { .offset = SRC_MASK_PERIL1, .value = 0x01110111, },
+ { .offset = SRC_MASK_DMC, .value = 0x00010000, },
+};
+
+static const struct samsung_clk_reg_dump src_mask_suspend_e4210[] = {
+ { .offset = E4210_SRC_MASK_LCD1, .value = 0x00001111, },
+};
+
+#define PLL_ENABLED (1 << 31)
+#define PLL_LOCKED (1 << 29)
+
+static void exynos4_clk_wait_for_pll(u32 reg)
+{
+ u32 pll_con;
+
+ pll_con = readl(reg_base + reg);
+ if (!(pll_con & PLL_ENABLED))
+ return;
+
+ while (!(pll_con & PLL_LOCKED)) {
+ cpu_relax();
+ pll_con = readl(reg_base + reg);
+ }
+}
+
+static int exynos4_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, exynos4_save_common,
+ ARRAY_SIZE(exynos4_clk_regs));
+ samsung_clk_save(reg_base, exynos4_save_pll,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+
+ if (exynos4_soc == EXYNOS4210) {
+ samsung_clk_save(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4210_clk_save));
+ samsung_clk_restore(reg_base, src_mask_suspend_e4210,
+ ARRAY_SIZE(src_mask_suspend_e4210));
+ } else {
+ samsung_clk_save(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4x12_clk_save));
+ }
+
+ samsung_clk_restore(reg_base, src_mask_suspend,
+ ARRAY_SIZE(src_mask_suspend));
+
+ return 0;
+}
+
+static void exynos4_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, exynos4_save_pll,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+
+ exynos4_clk_wait_for_pll(EPLL_CON0);
+ exynos4_clk_wait_for_pll(VPLL_CON0);
+
+ samsung_clk_restore(reg_base, exynos4_save_common,
+ ARRAY_SIZE(exynos4_clk_regs));
+
+ if (exynos4_soc == EXYNOS4210)
+ samsung_clk_restore(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4210_clk_save));
+ else
+ samsung_clk_restore(reg_base, exynos4_save_soc,
+ ARRAY_SIZE(exynos4x12_clk_save));
+}
+
+static struct syscore_ops exynos4_clk_syscore_ops = {
+ .suspend = exynos4_clk_suspend,
+ .resume = exynos4_clk_resume,
+};
+
+static void exynos4_clk_sleep_init(void)
+{
+ exynos4_save_common = samsung_clk_alloc_reg_dump(exynos4_clk_regs,
+ ARRAY_SIZE(exynos4_clk_regs));
+ if (!exynos4_save_common)
+ goto err_warn;
+
+ if (exynos4_soc == EXYNOS4210)
+ exynos4_save_soc = samsung_clk_alloc_reg_dump(
+ exynos4210_clk_save,
+ ARRAY_SIZE(exynos4210_clk_save));
+ else
+ exynos4_save_soc = samsung_clk_alloc_reg_dump(
+ exynos4x12_clk_save,
+ ARRAY_SIZE(exynos4x12_clk_save));
+ if (!exynos4_save_soc)
+ goto err_common;
+
+ exynos4_save_pll = samsung_clk_alloc_reg_dump(exynos4_clk_pll_regs,
+ ARRAY_SIZE(exynos4_clk_pll_regs));
+ if (!exynos4_save_pll)
+ goto err_soc;
+
+ register_syscore_ops(&exynos4_clk_syscore_ops);
+ return;
+
+err_soc:
+ kfree(exynos4_save_soc);
+err_common:
+ kfree(exynos4_save_common);
+err_warn:
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+}
+#else
+static void exynos4_clk_sleep_init(void) {}
+#endif
+
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", };
@@ -908,12 +1043,13 @@ static unsigned long exynos4_get_xom(void)
return xom;
}
-static void __init exynos4_clk_register_finpll(unsigned long xom)
+static void __init exynos4_clk_register_finpll(void)
{
struct samsung_fixed_rate_clock fclk;
struct clk *clk;
unsigned long finpll_f = 24000000;
char *parent_name;
+ unsigned int xom = exynos4_get_xom();
parent_name = xom & 1 ? "xusbxti" : "xxti";
clk = clk_get(NULL, parent_name);
@@ -1038,27 +1174,21 @@ static struct samsung_pll_clock exynos4x12_plls[nr_plls] __initdata = {
/* register exynos4 clocks */
static void __init exynos4_clk_init(struct device_node *np,
- enum exynos4_soc exynos4_soc,
- void __iomem *reg_base, unsigned long xom)
+ enum exynos4_soc soc)
{
+ exynos4_soc = soc;
+
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
- if (exynos4_soc == EXYNOS4210)
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
- exynos4210_clk_save, ARRAY_SIZE(exynos4210_clk_save));
- else
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos4_clk_regs, ARRAY_SIZE(exynos4_clk_regs),
- exynos4x12_clk_save, ARRAY_SIZE(exynos4x12_clk_save));
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos4_fixed_rate_ext_clks,
ARRAY_SIZE(exynos4_fixed_rate_ext_clks),
ext_clk_match);
- exynos4_clk_register_finpll(xom);
+ exynos4_clk_register_finpll();
if (exynos4_soc == EXYNOS4210) {
samsung_clk_register_mux(exynos4210_mux_early,
@@ -1125,6 +1255,8 @@ static void __init exynos4_clk_init(struct device_node *np,
samsung_clk_register_alias(exynos4_aliases,
ARRAY_SIZE(exynos4_aliases));
+ exynos4_clk_sleep_init();
+
pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n"
"\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n",
exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12",
@@ -1136,12 +1268,12 @@ static void __init exynos4_clk_init(struct device_node *np,
static void __init exynos4210_clk_init(struct device_node *np)
{
- exynos4_clk_init(np, EXYNOS4210, NULL, exynos4_get_xom());
+ exynos4_clk_init(np, EXYNOS4210);
}
CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
static void __init exynos4412_clk_init(struct device_node *np)
{
- exynos4_clk_init(np, EXYNOS4X12, NULL, exynos4_get_xom());
+ exynos4_clk_init(np, EXYNOS4X12);
}
CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index ff4beebe1f0b..e7ee4420da81 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -16,6 +16,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "clk.h"
@@ -85,6 +86,11 @@ enum exynos5250_plls {
nr_plls /* number of PLLs */
};
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos5250_save;
+
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
@@ -137,6 +143,41 @@ static unsigned long exynos5250_clk_regs[] __initdata = {
GATE_IP_ACP,
};
+static int exynos5250_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, exynos5250_save,
+ ARRAY_SIZE(exynos5250_clk_regs));
+
+ return 0;
+}
+
+static void exynos5250_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, exynos5250_save,
+ ARRAY_SIZE(exynos5250_clk_regs));
+}
+
+static struct syscore_ops exynos5250_clk_syscore_ops = {
+ .suspend = exynos5250_clk_suspend,
+ .resume = exynos5250_clk_resume,
+};
+
+static void exynos5250_clk_sleep_init(void)
+{
+ exynos5250_save = samsung_clk_alloc_reg_dump(exynos5250_clk_regs,
+ ARRAY_SIZE(exynos5250_clk_regs));
+ if (!exynos5250_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&exynos5250_clk_syscore_ops);
+}
+#else
+static void exynos5250_clk_sleep_init(void) {}
+#endif
+
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", };
@@ -645,8 +686,6 @@ static struct of_device_id ext_clk_match[] __initdata = {
/* register exynox5250 clocks */
static void __init exynos5250_clk_init(struct device_node *np)
{
- void __iomem *reg_base;
-
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
@@ -655,9 +694,7 @@ static void __init exynos5250_clk_init(struct device_node *np)
panic("%s: unable to determine soc\n", __func__);
}
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs),
- NULL, 0);
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5250_fixed_rate_ext_clks),
ext_clk_match);
@@ -685,6 +722,8 @@ static void __init exynos5250_clk_init(struct device_node *np)
samsung_clk_register_gate(exynos5250_gate_clks,
ARRAY_SIZE(exynos5250_gate_clks));
+ exynos5250_clk_sleep_init();
+
pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
_get_rate("div_arm2"));
}
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index ab4f2f7d88ef..60b26819bed5 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -16,6 +16,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "clk.h"
@@ -108,6 +109,11 @@ enum exynos5420_plls {
nr_plls /* number of PLLs */
};
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *exynos5420_save;
+
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
@@ -174,6 +180,41 @@ static unsigned long exynos5420_clk_regs[] __initdata = {
DIV_KFC0,
};
+static int exynos5420_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, exynos5420_save,
+ ARRAY_SIZE(exynos5420_clk_regs));
+
+ return 0;
+}
+
+static void exynos5420_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, exynos5420_save,
+ ARRAY_SIZE(exynos5420_clk_regs));
+}
+
+static struct syscore_ops exynos5420_clk_syscore_ops = {
+ .suspend = exynos5420_clk_suspend,
+ .resume = exynos5420_clk_resume,
+};
+
+static void exynos5420_clk_sleep_init(void)
+{
+ exynos5420_save = samsung_clk_alloc_reg_dump(exynos5420_clk_regs,
+ ARRAY_SIZE(exynos5420_clk_regs));
+ if (!exynos5420_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&exynos5420_clk_syscore_ops);
+}
+#else
+static void exynos5420_clk_sleep_init(void) {}
+#endif
+
/* list of all parent clocks */
PNAME(mspll_cpu_p) = { "sclk_cpll", "sclk_dpll",
"sclk_mpll", "sclk_spll" };
@@ -737,8 +778,6 @@ static struct of_device_id ext_clk_match[] __initdata = {
/* register exynos5420 clocks */
static void __init exynos5420_clk_init(struct device_node *np)
{
- void __iomem *reg_base;
-
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
@@ -747,9 +786,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
panic("%s: unable to determine soc\n", __func__);
}
- samsung_clk_init(np, reg_base, CLK_NR_CLKS,
- exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
- NULL, 0);
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
ext_clk_match);
@@ -765,5 +802,7 @@ static void __init exynos5420_clk_init(struct device_node *np)
ARRAY_SIZE(exynos5420_div_clks));
samsung_clk_register_gate(exynos5420_gate_clks,
ARRAY_SIZE(exynos5420_gate_clks));
+
+ exynos5420_clk_sleep_init();
}
CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
index cbc15b56891d..2bfad5a993d0 100644
--- a/drivers/clk/samsung/clk-exynos5440.c
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -101,7 +101,7 @@ static void __init exynos5440_clk_init(struct device_node *np)
return;
}
- samsung_clk_init(np, reg_base, CLK_NR_CLKS, NULL, 0, NULL, 0);
+ samsung_clk_init(np, reg_base, CLK_NR_CLKS);
samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c
index 8e27aee6887e..8bda658137a8 100644
--- a/drivers/clk/samsung/clk-s3c64xx.c
+++ b/drivers/clk/samsung/clk-s3c64xx.c
@@ -13,6 +13,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include <dt-bindings/clock/samsung,s3c64xx-clock.h>
@@ -61,6 +62,13 @@ enum s3c64xx_plls {
apll, mpll, epll,
};
+static void __iomem *reg_base;
+static bool is_s3c6400;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *s3c64xx_save_common;
+static struct samsung_clk_reg_dump *s3c64xx_save_soc;
+
/*
* List of controller registers to be saved and restored during
* a suspend/resume cycle.
@@ -87,6 +95,60 @@ static unsigned long s3c6410_clk_regs[] __initdata = {
MEM0_GATE,
};
+static int s3c64xx_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, s3c64xx_save_common,
+ ARRAY_SIZE(s3c64xx_clk_regs));
+
+ if (!is_s3c6400)
+ samsung_clk_save(reg_base, s3c64xx_save_soc,
+ ARRAY_SIZE(s3c6410_clk_regs));
+
+ return 0;
+}
+
+static void s3c64xx_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, s3c64xx_save_common,
+ ARRAY_SIZE(s3c64xx_clk_regs));
+
+ if (!is_s3c6400)
+ samsung_clk_restore(reg_base, s3c64xx_save_soc,
+ ARRAY_SIZE(s3c6410_clk_regs));
+}
+
+static struct syscore_ops s3c64xx_clk_syscore_ops = {
+ .suspend = s3c64xx_clk_suspend,
+ .resume = s3c64xx_clk_resume,
+};
+
+static void s3c64xx_clk_sleep_init(void)
+{
+ s3c64xx_save_common = samsung_clk_alloc_reg_dump(s3c64xx_clk_regs,
+ ARRAY_SIZE(s3c64xx_clk_regs));
+ if (!s3c64xx_save_common)
+ goto err_warn;
+
+ if (!is_s3c6400) {
+ s3c64xx_save_soc = samsung_clk_alloc_reg_dump(s3c6410_clk_regs,
+ ARRAY_SIZE(s3c6410_clk_regs));
+ if (!s3c64xx_save_soc)
+ goto err_soc;
+ }
+
+ register_syscore_ops(&s3c64xx_clk_syscore_ops);
+ return;
+
+err_soc:
+ kfree(s3c64xx_save_common);
+err_warn:
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+}
+#else
+static void s3c64xx_clk_sleep_init(void) {}
+#endif
+
/* List of parent clocks common for all S3C64xx SoCs. */
PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" };
PNAME(uart_p) = { "mout_epll", "dout_mpll" };
@@ -391,11 +453,11 @@ static void __init s3c64xx_clk_register_fixed_ext(unsigned long fin_pll_f,
/* Register s3c64xx clocks. */
void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
- unsigned long xusbxti_f, bool is_s3c6400,
- void __iomem *reg_base)
+ unsigned long xusbxti_f, bool s3c6400,
+ void __iomem *base)
{
- unsigned long *soc_regs = NULL;
- unsigned long nr_soc_regs = 0;
+ reg_base = base;
+ is_s3c6400 = s3c6400;
if (np) {
reg_base = of_iomap(np, 0);
@@ -403,13 +465,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
panic("%s: failed to map registers\n", __func__);
}
- if (!is_s3c6400) {
- soc_regs = s3c6410_clk_regs;
- nr_soc_regs = ARRAY_SIZE(s3c6410_clk_regs);
- }
-
- samsung_clk_init(np, reg_base, NR_CLKS, s3c64xx_clk_regs,
- ARRAY_SIZE(s3c64xx_clk_regs), soc_regs, nr_soc_regs);
+ samsung_clk_init(np, reg_base, NR_CLKS);
/* Register external clocks. */
if (!np)
@@ -452,6 +508,7 @@ void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
samsung_clk_register_alias(s3c64xx_clock_aliases,
ARRAY_SIZE(s3c64xx_clock_aliases));
+ s3c64xx_clk_sleep_init();
pr_info("%s clocks: apll = %lu, mpll = %lu\n"
"\tepll = %lu, arm_clk = %lu\n",
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index f503f32e2f80..91bec3ebdc8f 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -21,64 +21,45 @@ static void __iomem *reg_base;
static struct clk_onecell_data clk_data;
#endif
-#ifdef CONFIG_PM_SLEEP
-static struct samsung_clk_reg_dump *reg_dump;
-static unsigned long nr_reg_dump;
-
-static int samsung_clk_suspend(void)
+void samsung_clk_save(void __iomem *base,
+ struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs)
{
- struct samsung_clk_reg_dump *rd = reg_dump;
- unsigned long i;
-
- for (i = 0; i < nr_reg_dump; i++, rd++)
- rd->value = __raw_readl(reg_base + rd->offset);
+ for (; num_regs > 0; --num_regs, ++rd)
+ rd->value = readl(base + rd->offset);
+}
- return 0;
+void samsung_clk_restore(void __iomem *base,
+ const struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs)
+{
+ for (; num_regs > 0; --num_regs, ++rd)
+ writel(rd->value, base + rd->offset);
}
-static void samsung_clk_resume(void)
+struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
+ const unsigned long *rdump,
+ unsigned long nr_rdump)
{
- struct samsung_clk_reg_dump *rd = reg_dump;
- unsigned long i;
+ struct samsung_clk_reg_dump *rd;
+ unsigned int i;
- for (i = 0; i < nr_reg_dump; i++, rd++)
- __raw_writel(rd->value, reg_base + rd->offset);
-}
+ rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ for (i = 0; i < nr_rdump; ++i)
+ rd[i].offset = rdump[i];
-static struct syscore_ops samsung_clk_syscore_ops = {
- .suspend = samsung_clk_suspend,
- .resume = samsung_clk_resume,
-};
-#endif /* CONFIG_PM_SLEEP */
+ return rd;
+}
/* setup the essentials required to support clock lookup using ccf */
void __init samsung_clk_init(struct device_node *np, void __iomem *base,
- unsigned long nr_clks, unsigned long *rdump,
- unsigned long nr_rdump, unsigned long *soc_rdump,
- unsigned long nr_soc_rdump)
+ unsigned long nr_clks)
{
reg_base = base;
-#ifdef CONFIG_PM_SLEEP
- if (rdump && nr_rdump) {
- unsigned int idx;
- reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
- * (nr_rdump + nr_soc_rdump), GFP_KERNEL);
- if (!reg_dump) {
- pr_err("%s: memory alloc for register dump failed\n",
- __func__);
- return;
- }
-
- for (idx = 0; idx < nr_rdump; idx++)
- reg_dump[idx].offset = rdump[idx];
- for (idx = 0; idx < nr_soc_rdump; idx++)
- reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
- nr_reg_dump = nr_rdump + nr_soc_rdump;
- register_syscore_ops(&samsung_clk_syscore_ops);
- }
-#endif
-
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table)
panic("could not allocate clock lookup table\n");
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 31b4174e7a5b..c7141ba826e0 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -313,9 +313,7 @@ struct samsung_pll_clock {
_lock, _con, _rtable, _alias)
extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
- unsigned long nr_clks, unsigned long *rdump,
- unsigned long nr_rdump, unsigned long *soc_rdump,
- unsigned long nr_soc_rdump);
+ unsigned long nr_clks);
extern void __init samsung_clk_of_register_fixed_ext(
struct samsung_fixed_rate_clock *fixed_rate_clk,
unsigned int nr_fixed_rate_clk,
@@ -340,4 +338,14 @@ extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
extern unsigned long _get_rate(const char *clk_name);
+extern void samsung_clk_save(void __iomem *base,
+ struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs);
+extern void samsung_clk_restore(void __iomem *base,
+ const struct samsung_clk_reg_dump *rd,
+ unsigned int num_regs);
+extern struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
+ const unsigned long *rdump,
+ unsigned long nr_rdump);
+
#endif /* __SAMSUNG_CLK_H */
diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile
index 9ecef140dba7..5404cb931ebf 100644
--- a/drivers/clk/shmobile/Makefile
+++ b/drivers/clk/shmobile/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
+obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c
index aac4756ec52e..f065f694cb65 100644
--- a/drivers/clk/shmobile/clk-div6.c
+++ b/drivers/clk/shmobile/clk-div6.c
@@ -23,7 +23,7 @@
#define CPG_DIV6_DIV_MASK 0x3f
/**
- * struct div6_clock - MSTP gating clock
+ * struct div6_clock - CPG 6 bit divider clock
* @hw: handle between common and hardware-specific interfaces
* @reg: IO-remapped register
* @div: divisor value (1-64)
diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c
index 42d5912b1d25..2e5810c88d11 100644
--- a/drivers/clk/shmobile/clk-mstp.c
+++ b/drivers/clk/shmobile/clk-mstp.c
@@ -137,7 +137,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name,
init.name = name;
init.ops = &cpg_mstp_clock_ops;
- init.flags = CLK_IS_BASIC;
+ init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
init.parent_names = &parent_name;
init.num_parents = 1;
diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c
index 99c27b1c625b..dff7f79a19b9 100644
--- a/drivers/clk/shmobile/clk-rcar-gen2.c
+++ b/drivers/clk/shmobile/clk-rcar-gen2.c
@@ -242,22 +242,22 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
parent_name = "main";
mult = config->pll3_mult;
} else if (!strcmp(name, "lb")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
div = cpg_mode & BIT(18) ? 36 : 24;
} else if (!strcmp(name, "qspi")) {
parent_name = "pll1_div2";
div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2)
? 8 : 10;
} else if (!strcmp(name, "sdh")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
table = cpg_sdh_div_table;
shift = 8;
} else if (!strcmp(name, "sd0")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
table = cpg_sd01_div_table;
shift = 4;
} else if (!strcmp(name, "sd1")) {
- parent_name = "pll1_div2";
+ parent_name = "pll1";
table = cpg_sd01_div_table;
shift = 0;
} else if (!strcmp(name, "z")) {
diff --git a/drivers/clk/shmobile/clk-rz.c b/drivers/clk/shmobile/clk-rz.c
new file mode 100644
index 000000000000..7e68e8630962
--- /dev/null
+++ b/drivers/clk/shmobile/clk-rz.c
@@ -0,0 +1,103 @@
+/*
+ * rz Core CPG Clocks
+ *
+ * Copyright (C) 2013 Ideas On Board SPRL
+ * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+struct rz_cpg {
+ struct clk_onecell_data data;
+ void __iomem *reg;
+};
+
+#define CPG_FRQCR 0x10
+#define CPG_FRQCR2 0x14
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+static struct clk * __init
+rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
+{
+ u32 val;
+ unsigned mult;
+ static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
+
+ if (strcmp(name, "pll") == 0) {
+ /* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */
+ unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */
+ const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
+
+ mult = cpg_mode ? (32 / 4) : 30;
+
+ return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
+ }
+
+ /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
+ if (!cpg->reg)
+ return ERR_PTR(-ENXIO);
+
+ /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
+ * and the constraint that always g <= i. To get the rz platform started,
+ * let them run at fixed current speed and implement the details later.
+ */
+ if (strcmp(name, "i") == 0)
+ val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
+ else if (strcmp(name, "g") == 0)
+ val = clk_readl(cpg->reg + CPG_FRQCR2) & 3;
+ else
+ return ERR_PTR(-EINVAL);
+
+ mult = frqcr_tab[val];
+ return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
+}
+
+static void __init rz_cpg_clocks_init(struct device_node *np)
+{
+ struct rz_cpg *cpg;
+ struct clk **clks;
+ unsigned i;
+ int num_clks;
+
+ num_clks = of_property_count_strings(np, "clock-output-names");
+ if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
+ return;
+
+ cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
+ clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
+ BUG_ON(!cpg || !clks);
+
+ cpg->data.clks = clks;
+ cpg->data.clk_num = num_clks;
+
+ cpg->reg = of_iomap(np, 0);
+
+ for (i = 0; i < num_clks; ++i) {
+ const char *name;
+ struct clk *clk;
+
+ of_property_read_string_index(np, "clock-output-names", i, &name);
+
+ clk = rz_cpg_register_clock(np, cpg, name);
+ if (IS_ERR(clk))
+ pr_err("%s: failed to register %s %s clock (%ld)\n",
+ __func__, np->name, name, PTR_ERR(clk));
+ else
+ cpg->data.clks[i] = clk;
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
+}
+CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);
diff --git a/drivers/clk/sirf/clk-atlas6.c b/drivers/clk/sirf/clk-atlas6.c
index f9f4a15a64ab..d63b76ca60c3 100644
--- a/drivers/clk/sirf/clk-atlas6.c
+++ b/drivers/clk/sirf/clk-atlas6.c
@@ -1,7 +1,8 @@
/*
* Clock tree for CSR SiRFatlasVI
*
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c
index 7dde6a82f514..37af51c5f213 100644
--- a/drivers/clk/sirf/clk-common.c
+++ b/drivers/clk/sirf/clk-common.c
@@ -1,7 +1,8 @@
/*
* common clks module for all SiRF SoCs
*
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
diff --git a/drivers/clk/sirf/clk-prima2.c b/drivers/clk/sirf/clk-prima2.c
index 7adc5c70c7ff..6968e2ebcd8a 100644
--- a/drivers/clk/sirf/clk-prima2.c
+++ b/drivers/clk/sirf/clk-prima2.c
@@ -1,7 +1,8 @@
/*
* Clock tree for CSR SiRFprimaII
*
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
*
* Licensed under GPLv2 or later.
*/
diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile
index 0303c0b99cd0..7e2d15a0c7b8 100644
--- a/drivers/clk/socfpga/Makefile
+++ b/drivers/clk/socfpga/Makefile
@@ -1 +1,4 @@
obj-y += clk.o
+obj-y += clk-gate.o
+obj-y += clk-pll.o
+obj-y += clk-periph.o
diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c
new file mode 100644
index 000000000000..501d513bf890
--- /dev/null
+++ b/drivers/clk/socfpga/clk-gate.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Based from clk-highbank.c
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "clk.h"
+
+#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
+#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
+#define SOCFPGA_NAND_CLK "nand_clk"
+#define SOCFPGA_NAND_X_CLK "nand_x_clk"
+#define SOCFPGA_MMC_CLK "sdmmc_clk"
+#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8
+
+#define div_mask(width) ((1 << (width)) - 1)
+#define streq(a, b) (strcmp((a), (b)) == 0)
+
+#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
+
+/* SDMMC Group for System Manager defines */
+#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
+#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
+ ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
+
+static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
+{
+ u32 l4_src;
+ u32 perpll_src;
+
+ if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
+ l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ return l4_src &= 0x1;
+ }
+ if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
+ l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ return !!(l4_src & 2);
+ }
+
+ perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
+ if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
+ return perpll_src &= 0x3;
+ if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
+ streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
+ return (perpll_src >> 2) & 3;
+
+ /* QSPI clock */
+ return (perpll_src >> 4) & 3;
+
+}
+
+static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
+{
+ u32 src_reg;
+
+ if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
+ src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ src_reg &= ~0x1;
+ src_reg |= parent;
+ writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
+ } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
+ src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
+ src_reg &= ~0x2;
+ src_reg |= (parent << 1);
+ writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
+ } else {
+ src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
+ if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
+ src_reg &= ~0x3;
+ src_reg |= parent;
+ } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
+ streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
+ src_reg &= ~0xC;
+ src_reg |= (parent << 2);
+ } else {/* QSPI clock */
+ src_reg &= ~0x30;
+ src_reg |= (parent << 4);
+ }
+ writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
+ }
+
+ return 0;
+}
+
+static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ u32 div = 1, val;
+
+ if (socfpgaclk->fixed_div)
+ div = socfpgaclk->fixed_div;
+ else if (socfpgaclk->div_reg) {
+ val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
+ val &= div_mask(socfpgaclk->width);
+ /* Check for GPIO_DB_CLK by its offset */
+ if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET)
+ div = val + 1;
+ else
+ div = (1 << val);
+ }
+
+ return parent_rate / div;
+}
+
+static int socfpga_clk_prepare(struct clk_hw *hwclk)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ struct regmap *sys_mgr_base_addr;
+ int i;
+ u32 hs_timing;
+ u32 clk_phase[2];
+
+ if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) {
+ sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
+ if (IS_ERR(sys_mgr_base_addr)) {
+ pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ switch (socfpgaclk->clk_phase[i]) {
+ case 0:
+ clk_phase[i] = 0;
+ break;
+ case 45:
+ clk_phase[i] = 1;
+ break;
+ case 90:
+ clk_phase[i] = 2;
+ break;
+ case 135:
+ clk_phase[i] = 3;
+ break;
+ case 180:
+ clk_phase[i] = 4;
+ break;
+ case 225:
+ clk_phase[i] = 5;
+ break;
+ case 270:
+ clk_phase[i] = 6;
+ break;
+ case 315:
+ clk_phase[i] = 7;
+ break;
+ default:
+ clk_phase[i] = 0;
+ break;
+ }
+ }
+ hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]);
+ regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET,
+ hs_timing);
+ }
+ return 0;
+}
+
+static struct clk_ops gateclk_ops = {
+ .prepare = socfpga_clk_prepare,
+ .recalc_rate = socfpga_clk_recalc_rate,
+ .get_parent = socfpga_clk_get_parent,
+ .set_parent = socfpga_clk_set_parent,
+};
+
+static void __init __socfpga_gate_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 clk_gate[2];
+ u32 div_reg[3];
+ u32 clk_phase[2];
+ u32 fixed_div;
+ struct clk *clk;
+ struct socfpga_gate_clk *socfpga_clk;
+ const char *clk_name = node->name;
+ const char *parent_name[SOCFPGA_MAX_PARENTS];
+ struct clk_init_data init;
+ int rc;
+ int i = 0;
+
+ socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
+ if (WARN_ON(!socfpga_clk))
+ return;
+
+ rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
+ if (rc)
+ clk_gate[0] = 0;
+
+ if (clk_gate[0]) {
+ socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
+ socfpga_clk->hw.bit_idx = clk_gate[1];
+
+ gateclk_ops.enable = clk_gate_ops.enable;
+ gateclk_ops.disable = clk_gate_ops.disable;
+ }
+
+ rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
+ if (rc)
+ socfpga_clk->fixed_div = 0;
+ else
+ socfpga_clk->fixed_div = fixed_div;
+
+ rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
+ if (!rc) {
+ socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
+ socfpga_clk->shift = div_reg[1];
+ socfpga_clk->width = div_reg[2];
+ } else {
+ socfpga_clk->div_reg = 0;
+ }
+
+ rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2);
+ if (!rc) {
+ socfpga_clk->clk_phase[0] = clk_phase[0];
+ socfpga_clk->clk_phase[1] = clk_phase[1];
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] =
+ of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ init.parent_names = parent_name;
+ init.num_parents = i;
+ socfpga_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &socfpga_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(socfpga_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (WARN_ON(rc))
+ return;
+}
+
+void __init socfpga_gate_init(struct device_node *node)
+{
+ __socfpga_gate_init(node, &gateclk_ops);
+}
diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c
new file mode 100644
index 000000000000..81623a3736f9
--- /dev/null
+++ b/drivers/clk/socfpga/clk-periph.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Based from clk-highbank.c
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "clk.h"
+
+#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
+
+static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk);
+ u32 div;
+
+ if (socfpgaclk->fixed_div)
+ div = socfpgaclk->fixed_div;
+ else
+ div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
+
+ return parent_rate / div;
+}
+
+static const struct clk_ops periclk_ops = {
+ .recalc_rate = clk_periclk_recalc_rate,
+};
+
+static __init void __socfpga_periph_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 reg;
+ struct clk *clk;
+ struct socfpga_periph_clk *periph_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ u32 fixed_div;
+
+ of_property_read_u32(node, "reg", &reg);
+
+ periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
+ if (WARN_ON(!periph_clk))
+ return;
+
+ periph_clk->hw.reg = clk_mgr_base_addr + reg;
+
+ rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
+ if (rc)
+ periph_clk->fixed_div = 0;
+ else
+ periph_clk->fixed_div = fixed_div;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ periph_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &periph_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(periph_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+void __init socfpga_periph_init(struct device_node *node)
+{
+ __socfpga_periph_init(node, &periclk_ops);
+}
diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c
new file mode 100644
index 000000000000..88dafb5e9627
--- /dev/null
+++ b/drivers/clk/socfpga/clk-pll.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Based from clk-highbank.c
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "clk.h"
+
+/* Clock bypass bits */
+#define MAINPLL_BYPASS (1<<0)
+#define SDRAMPLL_BYPASS (1<<1)
+#define SDRAMPLL_SRC_BYPASS (1<<2)
+#define PERPLL_BYPASS (1<<3)
+#define PERPLL_SRC_BYPASS (1<<4)
+
+#define SOCFPGA_PLL_BG_PWRDWN 0
+#define SOCFPGA_PLL_EXT_ENA 1
+#define SOCFPGA_PLL_PWR_DOWN 2
+#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
+#define SOCFPGA_PLL_DIVF_SHIFT 3
+#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
+#define SOCFPGA_PLL_DIVQ_SHIFT 16
+
+#define CLK_MGR_PLL_CLK_SRC_SHIFT 22
+#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
+
+#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ unsigned long divf, divq, reg;
+ unsigned long long vco_freq;
+ unsigned long bypass;
+
+ reg = readl(socfpgaclk->hw.reg);
+ bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
+ if (bypass & MAINPLL_BYPASS)
+ return parent_rate;
+
+ divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
+ divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
+ vco_freq = (unsigned long long)parent_rate * (divf + 1);
+ do_div(vco_freq, (1 + divq));
+ return (unsigned long)vco_freq;
+}
+
+static u8 clk_pll_get_parent(struct clk_hw *hwclk)
+{
+ u32 pll_src;
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+
+ pll_src = readl(socfpgaclk->hw.reg);
+ return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
+ CLK_MGR_PLL_CLK_SRC_MASK;
+}
+
+static struct clk_ops clk_pll_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+ .get_parent = clk_pll_get_parent,
+};
+
+static __init struct clk *__socfpga_pll_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 reg;
+ struct clk *clk;
+ struct socfpga_pll *pll_clk;
+ const char *clk_name = node->name;
+ const char *parent_name[SOCFPGA_MAX_PARENTS];
+ struct clk_init_data init;
+ int rc;
+ int i = 0;
+
+ of_property_read_u32(node, "reg", &reg);
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (WARN_ON(!pll_clk))
+ return NULL;
+
+ pll_clk->hw.reg = clk_mgr_base_addr + reg;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+
+ while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] =
+ of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ init.num_parents = i;
+ init.parent_names = parent_name;
+ pll_clk->hw.hw.init = &init;
+
+ pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
+ clk_pll_ops.enable = clk_gate_ops.enable;
+ clk_pll_ops.disable = clk_gate_ops.disable;
+
+ clk = clk_register(NULL, &pll_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(pll_clk);
+ return NULL;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return clk;
+}
+
+void __init socfpga_pll_init(struct device_node *node)
+{
+ __socfpga_pll_init(node, &clk_pll_ops);
+}
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c
index 5983a26a8c5f..35a960a993f9 100644
--- a/drivers/clk/socfpga/clk.c
+++ b/drivers/clk/socfpga/clk.c
@@ -22,325 +22,23 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
-/* Clock Manager offsets */
-#define CLKMGR_CTRL 0x0
-#define CLKMGR_BYPASS 0x4
-#define CLKMGR_L4SRC 0x70
-#define CLKMGR_PERPLL_SRC 0xAC
+#include "clk.h"
-/* Clock bypass bits */
-#define MAINPLL_BYPASS (1<<0)
-#define SDRAMPLL_BYPASS (1<<1)
-#define SDRAMPLL_SRC_BYPASS (1<<2)
-#define PERPLL_BYPASS (1<<3)
-#define PERPLL_SRC_BYPASS (1<<4)
+void __iomem *clk_mgr_base_addr;
-#define SOCFPGA_PLL_BG_PWRDWN 0
-#define SOCFPGA_PLL_EXT_ENA 1
-#define SOCFPGA_PLL_PWR_DOWN 2
-#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
-#define SOCFPGA_PLL_DIVF_SHIFT 3
-#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
-#define SOCFPGA_PLL_DIVQ_SHIFT 16
-#define SOCFGPA_MAX_PARENTS 3
-
-#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
-#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
-#define SOCFPGA_NAND_CLK "nand_clk"
-#define SOCFPGA_NAND_X_CLK "nand_x_clk"
-#define SOCFPGA_MMC_CLK "sdmmc_clk"
-#define SOCFPGA_DB_CLK "gpio_db_clk"
-
-#define div_mask(width) ((1 << (width)) - 1)
-#define streq(a, b) (strcmp((a), (b)) == 0)
-
-extern void __iomem *clk_mgr_base_addr;
-
-struct socfpga_clk {
- struct clk_gate hw;
- char *parent_name;
- char *clk_name;
- u32 fixed_div;
- void __iomem *div_reg;
- u32 width; /* only valid if div_reg != 0 */
- u32 shift; /* only valid if div_reg != 0 */
-};
-#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
-
-static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
- unsigned long parent_rate)
-{
- struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
- unsigned long divf, divq, vco_freq, reg;
- unsigned long bypass;
-
- reg = readl(socfpgaclk->hw.reg);
- bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
- if (bypass & MAINPLL_BYPASS)
- return parent_rate;
-
- divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
- divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
- vco_freq = parent_rate * (divf + 1);
- return vco_freq / (1 + divq);
-}
-
-
-static struct clk_ops clk_pll_ops = {
- .recalc_rate = clk_pll_recalc_rate,
-};
-
-static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
- unsigned long parent_rate)
-{
- struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
- u32 div;
-
- if (socfpgaclk->fixed_div)
- div = socfpgaclk->fixed_div;
- else
- div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
-
- return parent_rate / div;
-}
-
-static const struct clk_ops periclk_ops = {
- .recalc_rate = clk_periclk_recalc_rate,
-};
-
-static __init struct clk *socfpga_clk_init(struct device_node *node,
- const struct clk_ops *ops)
-{
- u32 reg;
- struct clk *clk;
- struct socfpga_clk *socfpga_clk;
- const char *clk_name = node->name;
- const char *parent_name;
- struct clk_init_data init;
- int rc;
- u32 fixed_div;
-
- of_property_read_u32(node, "reg", &reg);
-
- socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
- if (WARN_ON(!socfpga_clk))
- return NULL;
-
- socfpga_clk->hw.reg = clk_mgr_base_addr + reg;
-
- rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
- if (rc)
- socfpga_clk->fixed_div = 0;
- else
- socfpga_clk->fixed_div = fixed_div;
-
- of_property_read_string(node, "clock-output-names", &clk_name);
-
- init.name = clk_name;
- init.ops = ops;
- init.flags = 0;
- parent_name = of_clk_get_parent_name(node, 0);
- init.parent_names = &parent_name;
- init.num_parents = 1;
-
- socfpga_clk->hw.hw.init = &init;
-
- if (streq(clk_name, "main_pll") ||
- streq(clk_name, "periph_pll") ||
- streq(clk_name, "sdram_pll")) {
- socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
- clk_pll_ops.enable = clk_gate_ops.enable;
- clk_pll_ops.disable = clk_gate_ops.disable;
- }
-
- clk = clk_register(NULL, &socfpga_clk->hw.hw);
- if (WARN_ON(IS_ERR(clk))) {
- kfree(socfpga_clk);
- return NULL;
- }
- rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
- return clk;
-}
-
-static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
-{
- u32 l4_src;
- u32 perpll_src;
-
- if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
- l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- return l4_src &= 0x1;
- }
- if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
- l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- return !!(l4_src & 2);
- }
-
- perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
- if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
- return perpll_src &= 0x3;
- if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
- streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
- return (perpll_src >> 2) & 3;
-
- /* QSPI clock */
- return (perpll_src >> 4) & 3;
-
-}
-
-static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
-{
- u32 src_reg;
-
- if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
- src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- src_reg &= ~0x1;
- src_reg |= parent;
- writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
- } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
- src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
- src_reg &= ~0x2;
- src_reg |= (parent << 1);
- writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
- } else {
- src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
- if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
- src_reg &= ~0x3;
- src_reg |= parent;
- } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
- streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
- src_reg &= ~0xC;
- src_reg |= (parent << 2);
- } else {/* QSPI clock */
- src_reg &= ~0x30;
- src_reg |= (parent << 4);
- }
- writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
- }
-
- return 0;
-}
-
-static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
- unsigned long parent_rate)
-{
- struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
- u32 div = 1, val;
-
- if (socfpgaclk->fixed_div)
- div = socfpgaclk->fixed_div;
- else if (socfpgaclk->div_reg) {
- val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
- val &= div_mask(socfpgaclk->width);
- if (streq(hwclk->init->name, SOCFPGA_DB_CLK))
- div = val + 1;
- else
- div = (1 << val);
- }
-
- return parent_rate / div;
-}
-
-static struct clk_ops gateclk_ops = {
- .recalc_rate = socfpga_clk_recalc_rate,
- .get_parent = socfpga_clk_get_parent,
- .set_parent = socfpga_clk_set_parent,
+static const struct of_device_id socfpga_child_clocks[] __initconst = {
+ { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, },
+ { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, },
+ { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, },
+ {},
};
-static void __init socfpga_gate_clk_init(struct device_node *node,
- const struct clk_ops *ops)
-{
- u32 clk_gate[2];
- u32 div_reg[3];
- u32 fixed_div;
- struct clk *clk;
- struct socfpga_clk *socfpga_clk;
- const char *clk_name = node->name;
- const char *parent_name[SOCFGPA_MAX_PARENTS];
- struct clk_init_data init;
- int rc;
- int i = 0;
-
- socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
- if (WARN_ON(!socfpga_clk))
- return;
-
- rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
- if (rc)
- clk_gate[0] = 0;
-
- if (clk_gate[0]) {
- socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
- socfpga_clk->hw.bit_idx = clk_gate[1];
-
- gateclk_ops.enable = clk_gate_ops.enable;
- gateclk_ops.disable = clk_gate_ops.disable;
- }
-
- rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
- if (rc)
- socfpga_clk->fixed_div = 0;
- else
- socfpga_clk->fixed_div = fixed_div;
-
- rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
- if (!rc) {
- socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
- socfpga_clk->shift = div_reg[1];
- socfpga_clk->width = div_reg[2];
- } else {
- socfpga_clk->div_reg = NULL;
- }
-
- of_property_read_string(node, "clock-output-names", &clk_name);
-
- init.name = clk_name;
- init.ops = ops;
- init.flags = 0;
- while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
- of_clk_get_parent_name(node, i)) != NULL)
- i++;
-
- init.parent_names = parent_name;
- init.num_parents = i;
- socfpga_clk->hw.hw.init = &init;
-
- clk = clk_register(NULL, &socfpga_clk->hw.hw);
- if (WARN_ON(IS_ERR(clk))) {
- kfree(socfpga_clk);
- return;
- }
- rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
- if (WARN_ON(rc))
- return;
-}
-
-static void __init socfpga_pll_init(struct device_node *node)
+static void __init socfpga_clkmgr_init(struct device_node *node)
{
- socfpga_clk_init(node, &clk_pll_ops);
+ clk_mgr_base_addr = of_iomap(node, 0);
+ of_clk_init(socfpga_child_clocks);
}
-CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init);
+CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init);
-static void __init socfpga_periph_init(struct device_node *node)
-{
- socfpga_clk_init(node, &periclk_ops);
-}
-CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init);
-
-static void __init socfpga_gate_init(struct device_node *node)
-{
- socfpga_gate_clk_init(node, &gateclk_ops);
-}
-CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init);
-
-void __init socfpga_init_clocks(void)
-{
- struct clk *clk;
- int ret;
-
- clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4);
- ret = clk_register_clkdev(clk, NULL, "smp_twd");
- if (ret)
- pr_err("smp_twd alias not registered\n");
-}
diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h
new file mode 100644
index 000000000000..d2e54019c94f
--- /dev/null
+++ b/drivers/clk/socfpga/clk.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * based on drivers/clk/tegra/clk.h
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __SOCFPGA_CLK_H
+#define __SOCFPGA_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+/* Clock Manager offsets */
+#define CLKMGR_CTRL 0x0
+#define CLKMGR_BYPASS 0x4
+#define CLKMGR_L4SRC 0x70
+#define CLKMGR_PERPLL_SRC 0xAC
+
+#define SOCFPGA_MAX_PARENTS 3
+
+extern void __iomem *clk_mgr_base_addr;
+
+void __init socfpga_pll_init(struct device_node *node);
+void __init socfpga_periph_init(struct device_node *node);
+void __init socfpga_gate_init(struct device_node *node);
+
+struct socfpga_pll {
+ struct clk_gate hw;
+};
+
+struct socfpga_gate_clk {
+ struct clk_gate hw;
+ char *parent_name;
+ u32 fixed_div;
+ void __iomem *div_reg;
+ u32 width; /* only valid if div_reg != 0 */
+ u32 shift; /* only valid if div_reg != 0 */
+ u32 clk_phase[2];
+};
+
+struct socfpga_periph_clk {
+ struct clk_gate hw;
+ char *parent_name;
+ u32 fixed_div;
+};
+
+#endif /* SOCFPGA_CLK_H */
diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile
new file mode 100644
index 000000000000..c7455ffdbdf7
--- /dev/null
+++ b/drivers/clk/st/Makefile
@@ -0,0 +1 @@
+obj-y += clkgen-mux.o clkgen-pll.o clkgen-fsyn.o
diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c
new file mode 100644
index 000000000000..4f53ee0778d9
--- /dev/null
+++ b/drivers/clk/st/clkgen-fsyn.c
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics R&D Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * Authors:
+ * Stephen Gallimore <stephen.gallimore@st.com>,
+ * Pankaj Dev <pankaj.dev@st.com>.
+ */
+
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+
+#include "clkgen.h"
+
+/*
+ * Maximum input clock to the PLL before we divide it down by 2
+ * although in reality in actual systems this has never been seen to
+ * be used.
+ */
+#define QUADFS_NDIV_THRESHOLD 30000000
+
+#define PLL_BW_GOODREF (0L)
+#define PLL_BW_VBADREF (1L)
+#define PLL_BW_BADREF (2L)
+#define PLL_BW_VGOODREF (3L)
+
+#define QUADFS_MAX_CHAN 4
+
+struct stm_fs {
+ unsigned long ndiv;
+ unsigned long mdiv;
+ unsigned long pe;
+ unsigned long sdiv;
+ unsigned long nsdiv;
+};
+
+static struct stm_fs fs216c65_rtbl[] = {
+ { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 312.5 Khz */
+ { .mdiv = 0x17, .pe = 0x25ed, .sdiv = 0x1, .nsdiv = 0 }, /* 27 MHz */
+ { .mdiv = 0x1a, .pe = 0x7b36, .sdiv = 0x2, .nsdiv = 1 }, /* 36.87 MHz */
+ { .mdiv = 0x13, .pe = 0x0, .sdiv = 0x2, .nsdiv = 1 }, /* 48 MHz */
+ { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x1, .nsdiv = 1 }, /* 108 MHz */
+};
+
+static struct stm_fs fs432c65_rtbl[] = {
+ { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 625 Khz */
+ { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x2, .nsdiv = 1 }, /* 108 MHz */
+ { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x0, .nsdiv = 1 }, /* 297 MHz */
+};
+
+static struct stm_fs fs660c32_rtbl[] = {
+ { .mdiv = 0x01, .pe = 0x2aaa, .sdiv = 0x8, .nsdiv = 0 }, /* 600 KHz */
+ { .mdiv = 0x02, .pe = 0x3d33, .sdiv = 0x0, .nsdiv = 0 }, /* 148.5 Mhz */
+ { .mdiv = 0x13, .pe = 0x5bcc, .sdiv = 0x0, .nsdiv = 1 }, /* 297 Mhz */
+ { .mdiv = 0x0e, .pe = 0x1025, .sdiv = 0x0, .nsdiv = 1 }, /* 333 Mhz */
+ { .mdiv = 0x0b, .pe = 0x715f, .sdiv = 0x0, .nsdiv = 1 }, /* 350 Mhz */
+};
+
+struct clkgen_quadfs_data {
+ bool reset_present;
+ bool bwfilter_present;
+ bool lockstatus_present;
+ bool nsdiv_present;
+ struct clkgen_field ndiv;
+ struct clkgen_field ref_bw;
+ struct clkgen_field nreset;
+ struct clkgen_field npda;
+ struct clkgen_field lock_status;
+
+ struct clkgen_field nsb[QUADFS_MAX_CHAN];
+ struct clkgen_field en[QUADFS_MAX_CHAN];
+ struct clkgen_field mdiv[QUADFS_MAX_CHAN];
+ struct clkgen_field pe[QUADFS_MAX_CHAN];
+ struct clkgen_field sdiv[QUADFS_MAX_CHAN];
+ struct clkgen_field nsdiv[QUADFS_MAX_CHAN];
+
+ const struct clk_ops *pll_ops;
+ struct stm_fs *rtbl;
+ u8 rtbl_cnt;
+ int (*get_rate)(unsigned long , struct stm_fs *,
+ unsigned long *);
+};
+
+static const struct clk_ops st_quadfs_pll_c65_ops;
+static const struct clk_ops st_quadfs_pll_c32_ops;
+static const struct clk_ops st_quadfs_fs216c65_ops;
+static const struct clk_ops st_quadfs_fs432c65_ops;
+static const struct clk_ops st_quadfs_fs660c32_ops;
+
+static int clk_fs216c65_get_rate(unsigned long, struct stm_fs *,
+ unsigned long *);
+static int clk_fs432c65_get_rate(unsigned long, struct stm_fs *,
+ unsigned long *);
+static int clk_fs660c32_dig_get_rate(unsigned long, struct stm_fs *,
+ unsigned long *);
+/*
+ * Values for all of the standalone instances of this clock
+ * generator found in STiH415 and STiH416 SYSCFG register banks. Note
+ * that the individual channel standby control bits (nsb) are in the
+ * first register along with the PLL control bits.
+ */
+static struct clkgen_quadfs_data st_fs216c65_416 = {
+ /* 416 specific */
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x1, 15),
+ .bwfilter_present = true,
+ .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
+ .pe = { CLKGEN_FIELD(0x8, 0xffff, 0),
+ CLKGEN_FIELD(0x18, 0xffff, 0),
+ CLKGEN_FIELD(0x28, 0xffff, 0),
+ CLKGEN_FIELD(0x38, 0xffff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0),
+ CLKGEN_FIELD(0x1C, 0x7, 0),
+ CLKGEN_FIELD(0x2C, 0x7, 0),
+ CLKGEN_FIELD(0x3C, 0x7, 0) },
+ .pll_ops = &st_quadfs_pll_c65_ops,
+ .rtbl = fs216c65_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs216c65_rtbl),
+ .get_rate = clk_fs216c65_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs432c65_416 = {
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x1, 15),
+ .bwfilter_present = true,
+ .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
+ .pe = { CLKGEN_FIELD(0x8, 0xffff, 0),
+ CLKGEN_FIELD(0x18, 0xffff, 0),
+ CLKGEN_FIELD(0x28, 0xffff, 0),
+ CLKGEN_FIELD(0x38, 0xffff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0),
+ CLKGEN_FIELD(0x1C, 0x7, 0),
+ CLKGEN_FIELD(0x2C, 0x7, 0),
+ CLKGEN_FIELD(0x3C, 0x7, 0) },
+ .pll_ops = &st_quadfs_pll_c65_ops,
+ .rtbl = fs432c65_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs432c65_rtbl),
+ .get_rate = clk_fs432c65_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs660c32_E_416 = {
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x7, 15),
+ .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0),
+ CLKGEN_FIELD(0x18, 0x7fff, 0),
+ CLKGEN_FIELD(0x28, 0x7fff, 0),
+ CLKGEN_FIELD(0x38, 0x7fff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0),
+ CLKGEN_FIELD(0x1C, 0xf, 0),
+ CLKGEN_FIELD(0x2C, 0xf, 0),
+ CLKGEN_FIELD(0x3C, 0xf, 0) },
+ .lockstatus_present = true,
+ .lock_status = CLKGEN_FIELD(0xAC, 0x1, 0),
+ .pll_ops = &st_quadfs_pll_c32_ops,
+ .rtbl = fs660c32_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
+ .get_rate = clk_fs660c32_dig_get_rate,
+};
+
+static struct clkgen_quadfs_data st_fs660c32_F_416 = {
+ .npda = CLKGEN_FIELD(0x0, 0x1, 14),
+ .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
+ CLKGEN_FIELD(0x0, 0x1, 11),
+ CLKGEN_FIELD(0x0, 0x1, 12),
+ CLKGEN_FIELD(0x0, 0x1, 13) },
+ .nsdiv_present = true,
+ .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
+ CLKGEN_FIELD(0x0, 0x1, 19),
+ CLKGEN_FIELD(0x0, 0x1, 20),
+ CLKGEN_FIELD(0x0, 0x1, 21) },
+ .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
+ CLKGEN_FIELD(0x14, 0x1f, 0),
+ CLKGEN_FIELD(0x24, 0x1f, 0),
+ CLKGEN_FIELD(0x34, 0x1f, 0) },
+ .en = { CLKGEN_FIELD(0x10, 0x1, 0),
+ CLKGEN_FIELD(0x20, 0x1, 0),
+ CLKGEN_FIELD(0x30, 0x1, 0),
+ CLKGEN_FIELD(0x40, 0x1, 0) },
+ .ndiv = CLKGEN_FIELD(0x0, 0x7, 15),
+ .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0),
+ CLKGEN_FIELD(0x18, 0x7fff, 0),
+ CLKGEN_FIELD(0x28, 0x7fff, 0),
+ CLKGEN_FIELD(0x38, 0x7fff, 0) },
+ .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0),
+ CLKGEN_FIELD(0x1C, 0xf, 0),
+ CLKGEN_FIELD(0x2C, 0xf, 0),
+ CLKGEN_FIELD(0x3C, 0xf, 0) },
+ .lockstatus_present = true,
+ .lock_status = CLKGEN_FIELD(0xEC, 0x1, 0),
+ .pll_ops = &st_quadfs_pll_c32_ops,
+ .rtbl = fs660c32_rtbl,
+ .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
+ .get_rate = clk_fs660c32_dig_get_rate,
+};
+
+/**
+ * DOC: A Frequency Synthesizer that multiples its input clock by a fixed factor
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control the Fsyn
+ * rate - inherits rate from parent. set_rate/round_rate/recalc_rate
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+/**
+ * struct st_clk_quadfs_pll - A pll which outputs a fixed multiplier of
+ * its parent clock, found inside a type of
+ * ST quad channel frequency synthesizer block
+ *
+ * @hw: handle between common and hardware-specific interfaces.
+ * @ndiv: regmap field for the ndiv control.
+ * @regs_base: base address of the configuration registers.
+ * @lock: spinlock.
+ *
+ */
+struct st_clk_quadfs_pll {
+ struct clk_hw hw;
+ void __iomem *regs_base;
+ spinlock_t *lock;
+ struct clkgen_quadfs_data *data;
+ u32 ndiv;
+};
+
+#define to_quadfs_pll(_hw) container_of(_hw, struct st_clk_quadfs_pll, hw)
+
+static int quadfs_pll_enable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ unsigned long flags = 0, timeout = jiffies + msecs_to_jiffies(10);
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ /*
+ * Bring block out of reset if we have reset control.
+ */
+ if (pll->data->reset_present)
+ CLKGEN_WRITE(pll, nreset, 1);
+
+ /*
+ * Use a fixed input clock noise bandwidth filter for the moment
+ */
+ if (pll->data->bwfilter_present)
+ CLKGEN_WRITE(pll, ref_bw, PLL_BW_GOODREF);
+
+
+ CLKGEN_WRITE(pll, ndiv, pll->ndiv);
+
+ /*
+ * Power up the PLL
+ */
+ CLKGEN_WRITE(pll, npda, 1);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ if (pll->data->lockstatus_present)
+ while (!CLKGEN_READ(pll, lock_status)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void quadfs_pll_disable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ /*
+ * Powerdown the PLL and then put block into soft reset if we have
+ * reset control.
+ */
+ CLKGEN_WRITE(pll, npda, 0);
+
+ if (pll->data->reset_present)
+ CLKGEN_WRITE(pll, nreset, 0);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int quadfs_pll_is_enabled(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ u32 npda = CLKGEN_READ(pll, npda);
+
+ return !!npda;
+}
+
+int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs,
+ unsigned long *rate)
+{
+ unsigned long nd = fs->ndiv + 16; /* ndiv value */
+
+ *rate = input * nd;
+
+ return 0;
+}
+
+static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ unsigned long rate = 0;
+ struct stm_fs params;
+
+ params.ndiv = CLKGEN_READ(pll, ndiv);
+ if (clk_fs660c32_vco_get_rate(parent_rate, &params, &rate))
+ pr_err("%s:%s error calculating rate\n",
+ __clk_get_name(hw->clk), __func__);
+
+ pll->ndiv = params.ndiv;
+
+ return rate;
+}
+
+int clk_fs660c32_vco_get_params(unsigned long input,
+ unsigned long output, struct stm_fs *fs)
+{
+/* Formula
+ VCO frequency = (fin x ndiv) / pdiv
+ ndiv = VCOfreq * pdiv / fin
+ */
+ unsigned long pdiv = 1, n;
+
+ /* Output clock range: 384Mhz to 660Mhz */
+ if (output < 384000000 || output > 660000000)
+ return -EINVAL;
+
+ if (input > 40000000)
+ /* This means that PDIV would be 2 instead of 1.
+ Not supported today. */
+ return -EINVAL;
+
+ input /= 1000;
+ output /= 1000;
+
+ n = output * pdiv / input;
+ if (n < 16)
+ n = 16;
+ fs->ndiv = n - 16; /* Converting formula value to reg value */
+
+ return 0;
+}
+
+static long quadfs_pll_fs660c32_round_rate(struct clk_hw *hw, unsigned long rate
+ , unsigned long *prate)
+{
+ struct stm_fs params;
+
+ if (!clk_fs660c32_vco_get_params(*prate, rate, &params))
+ clk_fs660c32_vco_get_rate(*prate, &params, &rate);
+
+ pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n",
+ __func__, __clk_get_name(hw->clk),
+ rate, (unsigned int)params.sdiv,
+ (unsigned int)params.mdiv,
+ (unsigned int)params.pe, (unsigned int)params.nsdiv);
+
+ return rate;
+}
+
+static int quadfs_pll_fs660c32_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw);
+ struct stm_fs params;
+ long hwrate = 0;
+ unsigned long flags = 0;
+
+ if (!rate || !parent_rate)
+ return -EINVAL;
+
+ if (!clk_fs660c32_vco_get_params(parent_rate, rate, &params))
+ clk_fs660c32_vco_get_rate(parent_rate, &params, &hwrate);
+
+ pr_debug("%s: %s new rate %ld [ndiv=0x%x]\n",
+ __func__, __clk_get_name(hw->clk),
+ hwrate, (unsigned int)params.ndiv);
+
+ if (!hwrate)
+ return -EINVAL;
+
+ pll->ndiv = params.ndiv;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ CLKGEN_WRITE(pll, ndiv, pll->ndiv);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops st_quadfs_pll_c65_ops = {
+ .enable = quadfs_pll_enable,
+ .disable = quadfs_pll_disable,
+ .is_enabled = quadfs_pll_is_enabled,
+};
+
+static const struct clk_ops st_quadfs_pll_c32_ops = {
+ .enable = quadfs_pll_enable,
+ .disable = quadfs_pll_disable,
+ .is_enabled = quadfs_pll_is_enabled,
+ .recalc_rate = quadfs_pll_fs660c32_recalc_rate,
+ .round_rate = quadfs_pll_fs660c32_round_rate,
+ .set_rate = quadfs_pll_fs660c32_set_rate,
+};
+
+static struct clk * __init st_clk_register_quadfs_pll(
+ const char *name, const char *parent_name,
+ struct clkgen_quadfs_data *quadfs, void __iomem *reg,
+ spinlock_t *lock)
+{
+ struct st_clk_quadfs_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /*
+ * Sanity check required pointers.
+ */
+ if (WARN_ON(!name || !parent_name))
+ return ERR_PTR(-EINVAL);
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = quadfs->pll_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll->data = quadfs;
+ pll->regs_base = reg;
+ pll->lock = lock;
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+/**
+ * DOC: A digital frequency synthesizer
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional
+ * rate - set rate is functional
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+/**
+ * struct st_clk_quadfs_fsynth - One clock output from a four channel digital
+ * frequency synthesizer (fsynth) block.
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * @nsb: regmap field in the output control register for the digital
+ * standby of this fsynth channel. This control is active low so
+ * the channel is in standby when the control bit is cleared.
+ *
+ * @nsdiv: regmap field in the output control register for
+ * for the optional divide by 3 of this fsynth channel. This control
+ * is active low so the divide by 3 is active when the control bit is
+ * cleared and the divide is bypassed when the bit is set.
+ */
+struct st_clk_quadfs_fsynth {
+ struct clk_hw hw;
+ void __iomem *regs_base;
+ spinlock_t *lock;
+ struct clkgen_quadfs_data *data;
+
+ u32 chan;
+ /*
+ * Cached hardware values from set_rate so we can program the
+ * hardware in enable. There are two reasons for this:
+ *
+ * 1. The registers may not be writable until the parent has been
+ * enabled.
+ *
+ * 2. It restores the clock rate when a driver does an enable
+ * on PM restore, after a suspend to RAM has lost the hardware
+ * setup.
+ */
+ u32 md;
+ u32 pe;
+ u32 sdiv;
+ u32 nsdiv;
+};
+
+#define to_quadfs_fsynth(_hw) \
+ container_of(_hw, struct st_clk_quadfs_fsynth, hw)
+
+static void quadfs_fsynth_program_enable(struct st_clk_quadfs_fsynth *fs)
+{
+ /*
+ * Pulse the program enable register lsb to make the hardware take
+ * notice of the new md/pe values with a glitchless transition.
+ */
+ CLKGEN_WRITE(fs, en[fs->chan], 1);
+ CLKGEN_WRITE(fs, en[fs->chan], 0);
+}
+
+static void quadfs_fsynth_program_rate(struct st_clk_quadfs_fsynth *fs)
+{
+ unsigned long flags = 0;
+
+ /*
+ * Ensure the md/pe parameters are ignored while we are
+ * reprogramming them so we can get a glitchless change
+ * when fine tuning the speed of a running clock.
+ */
+ CLKGEN_WRITE(fs, en[fs->chan], 0);
+
+ CLKGEN_WRITE(fs, mdiv[fs->chan], fs->md);
+ CLKGEN_WRITE(fs, pe[fs->chan], fs->pe);
+ CLKGEN_WRITE(fs, sdiv[fs->chan], fs->sdiv);
+
+ if (fs->lock)
+ spin_lock_irqsave(fs->lock, flags);
+
+ if (fs->data->nsdiv_present)
+ CLKGEN_WRITE(fs, nsdiv[fs->chan], fs->nsdiv);
+
+ if (fs->lock)
+ spin_unlock_irqrestore(fs->lock, flags);
+}
+
+static int quadfs_fsynth_enable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ unsigned long flags = 0;
+
+ pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
+
+ quadfs_fsynth_program_rate(fs);
+
+ if (fs->lock)
+ spin_lock_irqsave(fs->lock, flags);
+
+ CLKGEN_WRITE(fs, nsb[fs->chan], 1);
+
+ if (fs->lock)
+ spin_unlock_irqrestore(fs->lock, flags);
+
+ quadfs_fsynth_program_enable(fs);
+
+ return 0;
+}
+
+static void quadfs_fsynth_disable(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ unsigned long flags = 0;
+
+ pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk));
+
+ if (fs->lock)
+ spin_lock_irqsave(fs->lock, flags);
+
+ CLKGEN_WRITE(fs, nsb[fs->chan], 0);
+
+ if (fs->lock)
+ spin_unlock_irqrestore(fs->lock, flags);
+}
+
+static int quadfs_fsynth_is_enabled(struct clk_hw *hw)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ u32 nsb = CLKGEN_READ(fs, nsb[fs->chan]);
+
+ pr_debug("%s: %s enable bit = 0x%x\n",
+ __func__, __clk_get_name(hw->clk), nsb);
+
+ return !!nsb;
+}
+
+#define P15 (uint64_t)(1 << 15)
+
+static int clk_fs216c65_get_rate(unsigned long input, struct stm_fs *fs,
+ unsigned long *rate)
+{
+ uint64_t res;
+ unsigned long ns;
+ unsigned long nd = 8; /* ndiv stuck at 0 => val = 8 */
+ unsigned long s;
+ long m;
+
+ m = fs->mdiv - 32;
+ s = 1 << (fs->sdiv + 1);
+ ns = (fs->nsdiv ? 1 : 3);
+
+ res = (uint64_t)(s * ns * P15 * (uint64_t)(m + 33));
+ res = res - (s * ns * fs->pe);
+ *rate = div64_u64(P15 * nd * input * 32, res);
+
+ return 0;
+}
+
+static int clk_fs432c65_get_rate(unsigned long input, struct stm_fs *fs,
+ unsigned long *rate)
+{
+ uint64_t res;
+ unsigned long nd = 16; /* ndiv value; stuck at 0 (30Mhz input) */
+ long m;
+ unsigned long sd;
+ unsigned long ns;
+
+ m = fs->mdiv - 32;
+ sd = 1 << (fs->sdiv + 1);
+ ns = (fs->nsdiv ? 1 : 3);
+
+ res = (uint64_t)(sd * ns * P15 * (uint64_t)(m + 33));
+ res = res - (sd * ns * fs->pe);
+ *rate = div64_u64(P15 * nd * input * 32, res);
+
+ return 0;
+}
+
+#define P20 (uint64_t)(1 << 20)
+
+static int clk_fs660c32_dig_get_rate(unsigned long input,
+ struct stm_fs *fs, unsigned long *rate)
+{
+ unsigned long s = (1 << fs->sdiv);
+ unsigned long ns;
+ uint64_t res;
+
+ /*
+ * 'nsdiv' is a register value ('BIN') which is translated
+ * to a decimal value according to following rules.
+ *
+ * nsdiv ns.dec
+ * 0 3
+ * 1 1
+ */
+ ns = (fs->nsdiv == 1) ? 1 : 3;
+
+ res = (P20 * (32 + fs->mdiv) + 32 * fs->pe) * s * ns;
+ *rate = (unsigned long)div64_u64(input * P20 * 32, res);
+
+ return 0;
+}
+
+static int quadfs_fsynt_get_hw_value_for_recalc(struct st_clk_quadfs_fsynth *fs,
+ struct stm_fs *params)
+{
+ /*
+ * Get the initial hardware values for recalc_rate
+ */
+ params->mdiv = CLKGEN_READ(fs, mdiv[fs->chan]);
+ params->pe = CLKGEN_READ(fs, pe[fs->chan]);
+ params->sdiv = CLKGEN_READ(fs, sdiv[fs->chan]);
+
+ if (fs->data->nsdiv_present)
+ params->nsdiv = CLKGEN_READ(fs, nsdiv[fs->chan]);
+ else
+ params->nsdiv = 1;
+
+ /*
+ * If All are NULL then assume no clock rate is programmed.
+ */
+ if (!params->mdiv && !params->pe && !params->sdiv)
+ return 1;
+
+ fs->md = params->mdiv;
+ fs->pe = params->pe;
+ fs->sdiv = params->sdiv;
+ fs->nsdiv = params->nsdiv;
+
+ return 0;
+}
+
+static long quadfs_find_best_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate, struct stm_fs *params)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ int (*clk_fs_get_rate)(unsigned long ,
+ struct stm_fs *, unsigned long *);
+ struct stm_fs prev_params;
+ unsigned long prev_rate, rate = 0;
+ unsigned long diff_rate, prev_diff_rate = ~0;
+ int index;
+
+ clk_fs_get_rate = fs->data->get_rate;
+
+ for (index = 0; index < fs->data->rtbl_cnt; index++) {
+ prev_rate = rate;
+
+ *params = fs->data->rtbl[index];
+ prev_params = *params;
+
+ clk_fs_get_rate(prate, &fs->data->rtbl[index], &rate);
+
+ diff_rate = abs(drate - rate);
+
+ if (diff_rate > prev_diff_rate) {
+ rate = prev_rate;
+ *params = prev_params;
+ break;
+ }
+
+ prev_diff_rate = diff_rate;
+
+ if (drate == rate)
+ return rate;
+ }
+
+
+ if (index == fs->data->rtbl_cnt)
+ *params = prev_params;
+
+ return rate;
+}
+
+static unsigned long quadfs_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ unsigned long rate = 0;
+ struct stm_fs params;
+ int (*clk_fs_get_rate)(unsigned long ,
+ struct stm_fs *, unsigned long *);
+
+ clk_fs_get_rate = fs->data->get_rate;
+
+ if (quadfs_fsynt_get_hw_value_for_recalc(fs, &params))
+ return 0;
+
+ if (clk_fs_get_rate(parent_rate, &params, &rate)) {
+ pr_err("%s:%s error calculating rate\n",
+ __clk_get_name(hw->clk), __func__);
+ }
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+static long quadfs_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct stm_fs params;
+
+ rate = quadfs_find_best_rate(hw, rate, *prate, &params);
+
+ pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n",
+ __func__, __clk_get_name(hw->clk),
+ rate, (unsigned int)params.sdiv, (unsigned int)params.mdiv,
+ (unsigned int)params.pe, (unsigned int)params.nsdiv);
+
+ return rate;
+}
+
+
+static void quadfs_program_and_enable(struct st_clk_quadfs_fsynth *fs,
+ struct stm_fs *params)
+{
+ fs->md = params->mdiv;
+ fs->pe = params->pe;
+ fs->sdiv = params->sdiv;
+ fs->nsdiv = params->nsdiv;
+
+ /*
+ * In some integrations you can only change the fsynth programming when
+ * the parent entity containing it is enabled.
+ */
+ quadfs_fsynth_program_rate(fs);
+ quadfs_fsynth_program_enable(fs);
+}
+
+static int quadfs_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
+ struct stm_fs params;
+ long hwrate;
+ int uninitialized_var(i);
+
+ if (!rate || !parent_rate)
+ return -EINVAL;
+
+ memset(&params, 0, sizeof(struct stm_fs));
+
+ hwrate = quadfs_find_best_rate(hw, rate, parent_rate, &params);
+ if (!hwrate)
+ return -EINVAL;
+
+ quadfs_program_and_enable(fs, &params);
+
+ return 0;
+}
+
+
+
+static const struct clk_ops st_quadfs_ops = {
+ .enable = quadfs_fsynth_enable,
+ .disable = quadfs_fsynth_disable,
+ .is_enabled = quadfs_fsynth_is_enabled,
+ .round_rate = quadfs_round_rate,
+ .set_rate = quadfs_set_rate,
+ .recalc_rate = quadfs_recalc_rate,
+};
+
+static struct clk * __init st_clk_register_quadfs_fsynth(
+ const char *name, const char *parent_name,
+ struct clkgen_quadfs_data *quadfs, void __iomem *reg, u32 chan,
+ spinlock_t *lock)
+{
+ struct st_clk_quadfs_fsynth *fs;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /*
+ * Sanity check required pointers, note that nsdiv3 is optional.
+ */
+ if (WARN_ON(!name || !parent_name))
+ return ERR_PTR(-EINVAL);
+
+ fs = kzalloc(sizeof(*fs), GFP_KERNEL);
+ if (!fs)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &st_quadfs_ops;
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ fs->data = quadfs;
+ fs->regs_base = reg;
+ fs->chan = chan;
+ fs->lock = lock;
+ fs->hw.init = &init;
+
+ clk = clk_register(NULL, &fs->hw);
+
+ if (IS_ERR(clk))
+ kfree(fs);
+
+ return clk;
+}
+
+static struct of_device_id quadfs_of_match[] = {
+ {
+ .compatible = "st,stih416-quadfs216",
+ .data = (void *)&st_fs216c65_416
+ },
+ {
+ .compatible = "st,stih416-quadfs432",
+ .data = (void *)&st_fs432c65_416
+ },
+ {
+ .compatible = "st,stih416-quadfs660-E",
+ .data = (void *)&st_fs660c32_E_416
+ },
+ {
+ .compatible = "st,stih416-quadfs660-F",
+ .data = (void *)&st_fs660c32_F_416
+ },
+ {}
+};
+
+static void __init st_of_create_quadfs_fsynths(
+ struct device_node *np, const char *pll_name,
+ struct clkgen_quadfs_data *quadfs, void __iomem *reg,
+ spinlock_t *lock)
+{
+ struct clk_onecell_data *clk_data;
+ int fschan;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clk_num = QUADFS_MAX_CHAN;
+ clk_data->clks = kzalloc(QUADFS_MAX_CHAN * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ for (fschan = 0; fschan < QUADFS_MAX_CHAN; fschan++) {
+ struct clk *clk;
+ const char *clk_name;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ fschan, &clk_name)) {
+ break;
+ }
+
+ /*
+ * If we read an empty clock name then the channel is unused
+ */
+ if (*clk_name == '\0')
+ continue;
+
+ clk = st_clk_register_quadfs_fsynth(clk_name, pll_name,
+ quadfs, reg, fschan, lock);
+
+ /*
+ * If there was an error registering this clock output, clean
+ * up and move on to the next one.
+ */
+ if (!IS_ERR(clk)) {
+ clk_data->clks[fschan] = clk;
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+ }
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+}
+
+static void __init st_of_quadfs_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ const char *pll_name, *clk_parent_name;
+ void __iomem *reg;
+ spinlock_t *lock;
+
+ match = of_match_node(quadfs_of_match, np);
+ if (WARN_ON(!match))
+ return;
+
+ reg = of_iomap(np, 0);
+ if (!reg)
+ return;
+
+ clk_parent_name = of_clk_get_parent_name(np, 0);
+ if (!clk_parent_name)
+ return;
+
+ pll_name = kasprintf(GFP_KERNEL, "%s.pll", np->name);
+ if (!pll_name)
+ return;
+
+ lock = kzalloc(sizeof(*lock), GFP_KERNEL);
+ if (!lock)
+ goto err_exit;
+
+ spin_lock_init(lock);
+
+ clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name,
+ (struct clkgen_quadfs_data *) match->data, reg, lock);
+ if (IS_ERR(clk))
+ goto err_exit;
+ else
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ st_of_create_quadfs_fsynths(np, pll_name,
+ (struct clkgen_quadfs_data *)match->data,
+ reg, lock);
+
+err_exit:
+ kfree(pll_name); /* No longer need local copy of the PLL name */
+}
+CLK_OF_DECLARE(quadfs, "st,quadfs", st_of_quadfs_setup);
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
new file mode 100644
index 000000000000..a329906d1e81
--- /dev/null
+++ b/drivers/clk/st/clkgen-mux.c
@@ -0,0 +1,820 @@
+/*
+ * clkgen-mux.c: ST GEN-MUX Clock driver
+ *
+ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
+ *
+ * Authors: Stephen Gallimore <stephen.gallimore@st.com>
+ * Pankaj Dev <pankaj.dev@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+
+static DEFINE_SPINLOCK(clkgena_divmux_lock);
+static DEFINE_SPINLOCK(clkgenf_lock);
+
+static const char ** __init clkgen_mux_get_parents(struct device_node *np,
+ int *num_parents)
+{
+ const char **parents;
+ int nparents, i;
+
+ nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+ if (WARN_ON(nparents <= 0))
+ return ERR_PTR(-EINVAL);
+
+ parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL);
+ if (!parents)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < nparents; i++)
+ parents[i] = of_clk_get_parent_name(np, i);
+
+ *num_parents = nparents;
+ return parents;
+}
+
+/**
+ * DOC: Clock mux with a programmable divider on each of its three inputs.
+ * The mux has an input setting which effectively gates its output.
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - set rate is supported
+ * parent - set/get parent
+ */
+
+#define NUM_INPUTS 3
+
+struct clkgena_divmux {
+ struct clk_hw hw;
+ /* Subclassed mux and divider structures */
+ struct clk_mux mux;
+ struct clk_divider div[NUM_INPUTS];
+ /* Enable/running feedback register bits for each input */
+ void __iomem *feedback_reg[NUM_INPUTS];
+ int feedback_bit_idx;
+
+ u8 muxsel;
+};
+
+#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
+
+struct clkgena_divmux_data {
+ int num_outputs;
+ int mux_offset;
+ int mux_offset2;
+ int mux_start_bit;
+ int div_offsets[NUM_INPUTS];
+ int fb_offsets[NUM_INPUTS];
+ int fb_start_bit_idx;
+};
+
+#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
+
+static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
+{
+ u32 regval = readl(mux->feedback_reg[mux->muxsel]);
+ u32 running = regval & BIT(mux->feedback_bit_idx);
+ return !!running;
+}
+
+static int clkgena_divmux_enable(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+ unsigned long timeout;
+ int ret = 0;
+
+ mux_hw->clk = hw->clk;
+
+ ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
+ if (ret)
+ return ret;
+
+ timeout = jiffies + msecs_to_jiffies(10);
+
+ while (!clkgena_divmux_is_running(genamux)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static void clkgena_divmux_disable(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
+}
+
+static int clkgena_divmux_is_enabled(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
+}
+
+u8 clkgena_divmux_get_parent(struct clk_hw *hw)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *mux_hw = &genamux->mux.hw;
+
+ mux_hw->clk = hw->clk;
+
+ genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
+ if ((s8)genamux->muxsel < 0) {
+ pr_debug("%s: %s: Invalid parent, setting to default.\n",
+ __func__, __clk_get_name(hw->clk));
+ genamux->muxsel = 0;
+ }
+
+ return genamux->muxsel;
+}
+
+static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+
+ if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
+ return -EINVAL;
+
+ genamux->muxsel = index;
+
+ /*
+ * If the mux is already enabled, call enable directly to set the
+ * new mux position and wait for it to start running again. Otherwise
+ * do nothing.
+ */
+ if (clkgena_divmux_is_enabled(hw))
+ clkgena_divmux_enable(hw);
+
+ return 0;
+}
+
+unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
+
+ div_hw->clk = hw->clk;
+
+ return clk_divider_ops.recalc_rate(div_hw, parent_rate);
+}
+
+static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
+
+ div_hw->clk = hw->clk;
+
+ return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
+}
+
+static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
+ struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
+
+ div_hw->clk = hw->clk;
+
+ return clk_divider_ops.round_rate(div_hw, rate, prate);
+}
+
+static const struct clk_ops clkgena_divmux_ops = {
+ .enable = clkgena_divmux_enable,
+ .disable = clkgena_divmux_disable,
+ .is_enabled = clkgena_divmux_is_enabled,
+ .get_parent = clkgena_divmux_get_parent,
+ .set_parent = clkgena_divmux_set_parent,
+ .round_rate = clkgena_divmux_round_rate,
+ .recalc_rate = clkgena_divmux_recalc_rate,
+ .set_rate = clkgena_divmux_set_rate,
+};
+
+/**
+ * clk_register_genamux - register a genamux clock with the clock framework
+ */
+struct clk *clk_register_genamux(const char *name,
+ const char **parent_names, u8 num_parents,
+ void __iomem *reg,
+ const struct clkgena_divmux_data *muxdata,
+ u32 idx)
+{
+ /*
+ * Fixed constants across all ClockgenA variants
+ */
+ const int mux_width = 2;
+ const int divider_width = 5;
+ struct clkgena_divmux *genamux;
+ struct clk *clk;
+ struct clk_init_data init;
+ int i;
+
+ genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
+ if (!genamux)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clkgena_divmux_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ genamux->mux.lock = &clkgena_divmux_lock;
+ genamux->mux.mask = BIT(mux_width) - 1;
+ genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
+ if (genamux->mux.shift > 31) {
+ /*
+ * We have spilled into the second mux register so
+ * adjust the register address and the bit shift accordingly
+ */
+ genamux->mux.reg = reg + muxdata->mux_offset2;
+ genamux->mux.shift -= 32;
+ } else {
+ genamux->mux.reg = reg + muxdata->mux_offset;
+ }
+
+ for (i = 0; i < NUM_INPUTS; i++) {
+ /*
+ * Divider config for each input
+ */
+ void __iomem *divbase = reg + muxdata->div_offsets[i];
+ genamux->div[i].width = divider_width;
+ genamux->div[i].reg = divbase + (idx * sizeof(u32));
+
+ /*
+ * Mux enabled/running feedback register for each input.
+ */
+ genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
+ }
+
+ genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
+ genamux->hw.init = &init;
+
+ clk = clk_register(NULL, &genamux->hw);
+ if (IS_ERR(clk)) {
+ kfree(genamux);
+ goto err;
+ }
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+err:
+ return clk;
+}
+
+static struct clkgena_divmux_data st_divmux_c65hs = {
+ .num_outputs = 4,
+ .mux_offset = 0x14,
+ .mux_start_bit = 0,
+ .div_offsets = { 0x800, 0x900, 0xb00 },
+ .fb_offsets = { 0x18, 0x1c, 0x20 },
+ .fb_start_bit_idx = 0,
+};
+
+static struct clkgena_divmux_data st_divmux_c65ls = {
+ .num_outputs = 14,
+ .mux_offset = 0x14,
+ .mux_offset2 = 0x24,
+ .mux_start_bit = 8,
+ .div_offsets = { 0x810, 0xa10, 0xb10 },
+ .fb_offsets = { 0x18, 0x1c, 0x20 },
+ .fb_start_bit_idx = 4,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf0 = {
+ .num_outputs = 8,
+ .mux_offset = 0x1c,
+ .mux_start_bit = 0,
+ .div_offsets = { 0x800, 0x900, 0xa60 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 0,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf1 = {
+ .num_outputs = 8,
+ .mux_offset = 0x1c,
+ .mux_start_bit = 16,
+ .div_offsets = { 0x820, 0x980, 0xa80 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 8,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf2 = {
+ .num_outputs = 8,
+ .mux_offset = 0x20,
+ .mux_start_bit = 0,
+ .div_offsets = { 0x840, 0xa20, 0xb10 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 16,
+};
+
+static struct clkgena_divmux_data st_divmux_c32odf3 = {
+ .num_outputs = 8,
+ .mux_offset = 0x20,
+ .mux_start_bit = 16,
+ .div_offsets = { 0x860, 0xa40, 0xb30 },
+ .fb_offsets = { 0x2c, 0x24, 0x28 },
+ .fb_start_bit_idx = 24,
+};
+
+static struct of_device_id clkgena_divmux_of_match[] = {
+ {
+ .compatible = "st,clkgena-divmux-c65-hs",
+ .data = &st_divmux_c65hs,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c65-ls",
+ .data = &st_divmux_c65ls,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf0",
+ .data = &st_divmux_c32odf0,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf1",
+ .data = &st_divmux_c32odf1,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf2",
+ .data = &st_divmux_c32odf2,
+ },
+ {
+ .compatible = "st,clkgena-divmux-c32-odf3",
+ .data = &st_divmux_c32odf3,
+ },
+ {}
+};
+
+static void __iomem * __init clkgen_get_register_base(
+ struct device_node *np)
+{
+ struct device_node *pnode;
+ void __iomem *reg = NULL;
+
+ pnode = of_get_parent(np);
+ if (!pnode)
+ return NULL;
+
+ reg = of_iomap(pnode, 0);
+
+ of_node_put(pnode);
+ return reg;
+}
+
+void __init st_of_clkgena_divmux_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ const struct clkgena_divmux_data *data;
+ struct clk_onecell_data *clk_data;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents = 0, i;
+
+ match = of_match_node(clkgena_divmux_of_match, np);
+ if (WARN_ON(!match))
+ return;
+
+ data = (struct clkgena_divmux_data *)match->data;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents))
+ return;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto err;
+
+ clk_data->clk_num = data->num_outputs;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ for (i = 0; i < clk_data->clk_num; i++) {
+ struct clk *clk;
+ const char *clk_name;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ i, &clk_name))
+ break;
+
+ /*
+ * If we read an empty clock name then the output is unused
+ */
+ if (*clk_name == '\0')
+ continue;
+
+ clk = clk_register_genamux(clk_name, parents, num_parents,
+ reg, data, i);
+
+ if (IS_ERR(clk))
+ goto err;
+
+ clk_data->clks[i] = clk;
+ }
+
+ kfree(parents);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+err:
+ if (clk_data)
+ kfree(clk_data->clks);
+
+ kfree(clk_data);
+ kfree(parents);
+}
+CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
+
+struct clkgena_prediv_data {
+ u32 offset;
+ u8 shift;
+ struct clk_div_table *table;
+};
+
+static struct clk_div_table prediv_table16[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 16 },
+ { .div = 0 },
+};
+
+static struct clkgena_prediv_data prediv_c65_data = {
+ .offset = 0x4c,
+ .shift = 31,
+ .table = prediv_table16,
+};
+
+static struct clkgena_prediv_data prediv_c32_data = {
+ .offset = 0x50,
+ .shift = 1,
+ .table = prediv_table16,
+};
+
+static struct of_device_id clkgena_prediv_of_match[] = {
+ { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
+ { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
+ {}
+};
+
+void __init st_of_clkgena_prediv_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ void __iomem *reg;
+ const char *parent_name, *clk_name;
+ struct clk *clk;
+ struct clkgena_prediv_data *data;
+
+ match = of_match_node(clkgena_prediv_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgena_prediv_data *)match->data;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name))
+ return;
+
+ clk = clk_register_divider_table(NULL, clk_name, parent_name, 0,
+ reg + data->offset, data->shift, 1,
+ 0, data->table, NULL);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ return;
+}
+CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
+
+struct clkgen_mux_data {
+ u32 offset;
+ u8 shift;
+ u8 width;
+ spinlock_t *lock;
+ unsigned long clk_flags;
+ u8 mux_flags;
+};
+
+static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
+ .offset = 0,
+ .shift = 0,
+ .width = 1,
+};
+
+static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
+ .offset = 0,
+ .shift = 0,
+ .width = 1,
+};
+
+static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
+ .offset = 0,
+ .shift = 0,
+ .width = 1,
+};
+
+static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
+ .offset = 0,
+ .shift = 16,
+ .width = 1,
+ .lock = &clkgenf_lock,
+};
+
+static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
+ .offset = 0,
+ .shift = 17,
+ .width = 1,
+ .lock = &clkgenf_lock,
+};
+
+static struct clkgen_mux_data stih415_a9_mux_data = {
+ .offset = 0,
+ .shift = 1,
+ .width = 2,
+};
+static struct clkgen_mux_data stih416_a9_mux_data = {
+ .offset = 0,
+ .shift = 0,
+ .width = 2,
+};
+
+static struct of_device_id mux_of_match[] = {
+ {
+ .compatible = "st,stih416-clkgenc-vcc-hd",
+ .data = &clkgen_mux_c_vcc_hd_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-fvdp",
+ .data = &clkgen_mux_f_vcc_fvdp_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-hva",
+ .data = &clkgen_mux_f_vcc_hva_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-hd",
+ .data = &clkgen_mux_f_vcc_hd_416,
+ },
+ {
+ .compatible = "st,stih416-clkgenf-vcc-sd",
+ .data = &clkgen_mux_c_vcc_sd_416,
+ },
+ {
+ .compatible = "st,stih415-clkgen-a9-mux",
+ .data = &stih415_a9_mux_data,
+ },
+ {
+ .compatible = "st,stih416-clkgen-a9-mux",
+ .data = &stih416_a9_mux_data,
+ },
+ {}
+};
+
+void __init st_of_clkgen_mux_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents;
+ struct clkgen_mux_data *data;
+
+ match = of_match_node(mux_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgen_mux_data *)match->data;
+
+ reg = of_iomap(np, 0);
+ if (!reg) {
+ pr_err("%s: Failed to get base address\n", __func__);
+ return;
+ }
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents)) {
+ pr_err("%s: Failed to get parents (%ld)\n",
+ __func__, PTR_ERR(parents));
+ return;
+ }
+
+ clk = clk_register_mux(NULL, np->name, parents, num_parents,
+ data->clk_flags | CLK_SET_RATE_PARENT,
+ reg + data->offset,
+ data->shift, data->width, data->mux_flags,
+ data->lock);
+ if (IS_ERR(clk))
+ goto err;
+
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+err:
+ kfree(parents);
+
+ return;
+}
+CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
+
+#define VCC_MAX_CHANNELS 16
+
+#define VCC_GATE_OFFSET 0x0
+#define VCC_MUX_OFFSET 0x4
+#define VCC_DIV_OFFSET 0x8
+
+struct clkgen_vcc_data {
+ spinlock_t *lock;
+ unsigned long clk_flags;
+};
+
+static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
+ .clk_flags = CLK_SET_RATE_PARENT,
+};
+
+static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
+ .lock = &clkgenf_lock,
+};
+
+static struct of_device_id vcc_of_match[] = {
+ { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
+ { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
+ {}
+};
+
+void __init st_of_clkgen_vcc_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ void __iomem *reg;
+ const char **parents;
+ int num_parents, i;
+ struct clk_onecell_data *clk_data;
+ struct clkgen_vcc_data *data;
+
+ match = of_match_node(vcc_of_match, np);
+ if (WARN_ON(!match))
+ return;
+ data = (struct clkgen_vcc_data *)match->data;
+
+ reg = of_iomap(np, 0);
+ if (!reg)
+ return;
+
+ parents = clkgen_mux_get_parents(np, &num_parents);
+ if (IS_ERR(parents))
+ return;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto err;
+
+ clk_data->clk_num = VCC_MAX_CHANNELS;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ for (i = 0; i < clk_data->clk_num; i++) {
+ struct clk *clk;
+ const char *clk_name;
+ struct clk_gate *gate;
+ struct clk_divider *div;
+ struct clk_mux *mux;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ i, &clk_name))
+ break;
+
+ /*
+ * If we read an empty clock name then the output is unused
+ */
+ if (*clk_name == '\0')
+ continue;
+
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate)
+ break;
+
+ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+ if (!div) {
+ kfree(gate);
+ break;
+ }
+
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux) {
+ kfree(gate);
+ kfree(div);
+ break;
+ }
+
+ gate->reg = reg + VCC_GATE_OFFSET;
+ gate->bit_idx = i;
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->lock = data->lock;
+
+ div->reg = reg + VCC_DIV_OFFSET;
+ div->shift = 2 * i;
+ div->width = 2;
+ div->flags = CLK_DIVIDER_POWER_OF_TWO;
+
+ mux->reg = reg + VCC_MUX_OFFSET;
+ mux->shift = 2 * i;
+ mux->mask = 0x3;
+
+ clk = clk_register_composite(NULL, clk_name, parents,
+ num_parents,
+ &mux->hw, &clk_mux_ops,
+ &div->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops,
+ data->clk_flags);
+ if (IS_ERR(clk)) {
+ kfree(gate);
+ kfree(div);
+ kfree(mux);
+ goto err;
+ }
+
+ pr_debug("%s: parent %s rate %u\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ (unsigned int)clk_get_rate(clk));
+
+ clk_data->clks[i] = clk;
+ }
+
+ kfree(parents);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+
+err:
+ for (i = 0; i < clk_data->clk_num; i++) {
+ struct clk_composite *composite;
+
+ if (!clk_data->clks[i])
+ continue;
+
+ composite = container_of(__clk_get_hw(clk_data->clks[i]),
+ struct clk_composite, hw);
+ kfree(container_of(composite->gate_hw, struct clk_gate, hw));
+ kfree(container_of(composite->rate_hw, struct clk_divider, hw));
+ kfree(container_of(composite->mux_hw, struct clk_mux, hw));
+ }
+
+ if (clk_data)
+ kfree(clk_data->clks);
+
+ kfree(clk_data);
+ kfree(parents);
+}
+CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c
new file mode 100644
index 000000000000..bca0a0badbfa
--- /dev/null
+++ b/drivers/clk/st/clkgen-pll.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Authors:
+ * Stephen Gallimore <stephen.gallimore@st.com>,
+ * Pankaj Dev <pankaj.dev@st.com>.
+ */
+
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+
+#include "clkgen.h"
+
+static DEFINE_SPINLOCK(clkgena_c32_odf_lock);
+
+/*
+ * Common PLL configuration register bits for PLL800 and PLL1600 C65
+ */
+#define C65_MDIV_PLL800_MASK (0xff)
+#define C65_MDIV_PLL1600_MASK (0x7)
+#define C65_NDIV_MASK (0xff)
+#define C65_PDIV_MASK (0x7)
+
+/*
+ * PLL configuration register bits for PLL3200 C32
+ */
+#define C32_NDIV_MASK (0xff)
+#define C32_IDF_MASK (0x7)
+#define C32_ODF_MASK (0x3f)
+#define C32_LDF_MASK (0x7f)
+
+#define C32_MAX_ODFS (4)
+
+struct clkgen_pll_data {
+ struct clkgen_field pdn_status;
+ struct clkgen_field locked_status;
+ struct clkgen_field mdiv;
+ struct clkgen_field ndiv;
+ struct clkgen_field pdiv;
+ struct clkgen_field idf;
+ struct clkgen_field ldf;
+ unsigned int num_odfs;
+ struct clkgen_field odf[C32_MAX_ODFS];
+ struct clkgen_field odf_gate[C32_MAX_ODFS];
+ const struct clk_ops *ops;
+};
+
+static const struct clk_ops st_pll1600c65_ops;
+static const struct clk_ops st_pll800c65_ops;
+static const struct clk_ops stm_pll3200c32_ops;
+static const struct clk_ops st_pll1200c32_ops;
+
+static struct clkgen_pll_data st_pll1600c65_ax = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
+ .locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
+ .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL1600_MASK, 0),
+ .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
+ .ops = &st_pll1600c65_ops
+};
+
+static struct clkgen_pll_data st_pll800c65_ax = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
+ .locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
+ .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL800_MASK, 0),
+ .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
+ .pdiv = CLKGEN_FIELD(0x0, C65_PDIV_MASK, 16),
+ .ops = &st_pll800c65_ops
+};
+
+static struct clkgen_pll_data st_pll3200c32_a1x_0 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 31),
+ .locked_status = CLKGEN_FIELD(0x4, 0x1, 31),
+ .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 0x0),
+ .idf = CLKGEN_FIELD(0x4, C32_IDF_MASK, 0x0),
+ .num_odfs = 4,
+ .odf = { CLKGEN_FIELD(0x54, C32_ODF_MASK, 4),
+ CLKGEN_FIELD(0x54, C32_ODF_MASK, 10),
+ CLKGEN_FIELD(0x54, C32_ODF_MASK, 16),
+ CLKGEN_FIELD(0x54, C32_ODF_MASK, 22) },
+ .odf_gate = { CLKGEN_FIELD(0x54, 0x1, 0),
+ CLKGEN_FIELD(0x54, 0x1, 1),
+ CLKGEN_FIELD(0x54, 0x1, 2),
+ CLKGEN_FIELD(0x54, 0x1, 3) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll3200c32_a1x_1 = {
+ .pdn_status = CLKGEN_FIELD(0xC, 0x1, 31),
+ .locked_status = CLKGEN_FIELD(0x10, 0x1, 31),
+ .ndiv = CLKGEN_FIELD(0xC, C32_NDIV_MASK, 0x0),
+ .idf = CLKGEN_FIELD(0x10, C32_IDF_MASK, 0x0),
+ .num_odfs = 4,
+ .odf = { CLKGEN_FIELD(0x58, C32_ODF_MASK, 4),
+ CLKGEN_FIELD(0x58, C32_ODF_MASK, 10),
+ CLKGEN_FIELD(0x58, C32_ODF_MASK, 16),
+ CLKGEN_FIELD(0x58, C32_ODF_MASK, 22) },
+ .odf_gate = { CLKGEN_FIELD(0x58, 0x1, 0),
+ CLKGEN_FIELD(0x58, 0x1, 1),
+ CLKGEN_FIELD(0x58, 0x1, 2),
+ CLKGEN_FIELD(0x58, 0x1, 3) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+/* 415 specific */
+static struct clkgen_pll_data st_pll3200c32_a9_415 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 9),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 22),
+ .num_odfs = 1,
+ .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 3) },
+ .odf_gate = { CLKGEN_FIELD(0x0, 0x1, 28) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll3200c32_ddr_415 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x100, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
+ .num_odfs = 2,
+ .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
+ CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
+ .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
+ CLKGEN_FIELD(0x4, 0x1, 29) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll1200c32_gpu_415 = {
+ .pdn_status = CLKGEN_FIELD(0x144, 0x1, 3),
+ .locked_status = CLKGEN_FIELD(0x168, 0x1, 0),
+ .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
+ .num_odfs = 0,
+ .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
+ .ops = &st_pll1200c32_ops,
+};
+
+/* 416 specific */
+static struct clkgen_pll_data st_pll3200c32_a9_416 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
+ .num_odfs = 1,
+ .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8) },
+ .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll3200c32_ddr_416 = {
+ .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
+ .locked_status = CLKGEN_FIELD(0x10C, 0x1, 0),
+ .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
+ .num_odfs = 2,
+ .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
+ CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
+ .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
+ CLKGEN_FIELD(0x4, 0x1, 29) },
+ .ops = &stm_pll3200c32_ops,
+};
+
+static struct clkgen_pll_data st_pll1200c32_gpu_416 = {
+ .pdn_status = CLKGEN_FIELD(0x8E4, 0x1, 3),
+ .locked_status = CLKGEN_FIELD(0x90C, 0x1, 0),
+ .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
+ .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
+ .num_odfs = 0,
+ .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
+ .ops = &st_pll1200c32_ops,
+};
+
+/**
+ * DOC: Clock Generated by PLL, rate set and enabled by bootloader
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable/disable only ensures parent is enabled
+ * rate - rate is fixed. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+/**
+ * PLL clock that is integrated in the ClockGenA instances on the STiH415
+ * and STiH416.
+ *
+ * @hw: handle between common and hardware-specific interfaces.
+ * @type: PLL instance type.
+ * @regs_base: base of the PLL configuration register(s).
+ *
+ */
+struct clkgen_pll {
+ struct clk_hw hw;
+ struct clkgen_pll_data *data;
+ void __iomem *regs_base;
+};
+
+#define to_clkgen_pll(_hw) container_of(_hw, struct clkgen_pll, hw)
+
+static int clkgen_pll_is_locked(struct clk_hw *hw)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ u32 locked = CLKGEN_READ(pll, locked_status);
+
+ return !!locked;
+}
+
+static int clkgen_pll_is_enabled(struct clk_hw *hw)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ u32 poweroff = CLKGEN_READ(pll, pdn_status);
+ return !poweroff;
+}
+
+unsigned long recalc_stm_pll800c65(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long mdiv, ndiv, pdiv;
+ unsigned long rate;
+ uint64_t res;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ pdiv = CLKGEN_READ(pll, pdiv);
+ mdiv = CLKGEN_READ(pll, mdiv);
+ ndiv = CLKGEN_READ(pll, ndiv);
+
+ if (!mdiv)
+ mdiv++; /* mdiv=0 or 1 => MDIV=1 */
+
+ res = (uint64_t)2 * (uint64_t)parent_rate * (uint64_t)ndiv;
+ rate = (unsigned long)div64_u64(res, mdiv * (1 << pdiv));
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+
+}
+
+unsigned long recalc_stm_pll1600c65(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long mdiv, ndiv;
+ unsigned long rate;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ mdiv = CLKGEN_READ(pll, mdiv);
+ ndiv = CLKGEN_READ(pll, ndiv);
+
+ if (!mdiv)
+ mdiv = 1;
+
+ /* Note: input is divided by 1000 to avoid overflow */
+ rate = ((2 * (parent_rate / 1000) * ndiv) / mdiv) * 1000;
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+unsigned long recalc_stm_pll3200c32(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long ndiv, idf;
+ unsigned long rate = 0;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ ndiv = CLKGEN_READ(pll, ndiv);
+ idf = CLKGEN_READ(pll, idf);
+
+ if (idf)
+ /* Note: input is divided to avoid overflow */
+ rate = ((2 * (parent_rate/1000) * ndiv) / idf) * 1000;
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+unsigned long recalc_stm_pll1200c32(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clkgen_pll *pll = to_clkgen_pll(hw);
+ unsigned long odf, ldf, idf;
+ unsigned long rate;
+
+ if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
+ return 0;
+
+ odf = CLKGEN_READ(pll, odf[0]);
+ ldf = CLKGEN_READ(pll, ldf);
+ idf = CLKGEN_READ(pll, idf);
+
+ if (!idf) /* idf==0 means 1 */
+ idf = 1;
+ if (!odf) /* odf==0 means 1 */
+ odf = 1;
+
+ /* Note: input is divided by 1000 to avoid overflow */
+ rate = (((parent_rate / 1000) * ldf) / (odf * idf)) * 1000;
+
+ pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
+
+ return rate;
+}
+
+static const struct clk_ops st_pll1600c65_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll1600c65,
+};
+
+static const struct clk_ops st_pll800c65_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll800c65,
+};
+
+static const struct clk_ops stm_pll3200c32_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll3200c32,
+};
+
+static const struct clk_ops st_pll1200c32_ops = {
+ .is_enabled = clkgen_pll_is_enabled,
+ .recalc_rate = recalc_stm_pll1200c32,
+};
+
+static struct clk * __init clkgen_pll_register(const char *parent_name,
+ struct clkgen_pll_data *pll_data,
+ void __iomem *reg,
+ const char *clk_name)
+{
+ struct clkgen_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = clk_name;
+ init.ops = pll_data->ops;
+
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll->data = pll_data;
+ pll->regs_base = reg;
+ pll->hw.init = &init;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ kfree(pll);
+ return clk;
+ }
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+
+ return clk;
+}
+
+static struct clk * __init clkgen_c65_lsdiv_register(const char *parent_name,
+ const char *clk_name)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, 1, 2);
+ if (IS_ERR(clk))
+ return clk;
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+ return clk;
+}
+
+static void __iomem * __init clkgen_get_register_base(
+ struct device_node *np)
+{
+ struct device_node *pnode;
+ void __iomem *reg = NULL;
+
+ pnode = of_get_parent(np);
+ if (!pnode)
+ return NULL;
+
+ reg = of_iomap(pnode, 0);
+
+ of_node_put(pnode);
+ return reg;
+}
+
+#define CLKGENAx_PLL0_OFFSET 0x0
+#define CLKGENAx_PLL1_OFFSET 0x4
+
+static void __init clkgena_c65_pll_setup(struct device_node *np)
+{
+ const int num_pll_outputs = 3;
+ struct clk_onecell_data *clk_data;
+ const char *parent_name;
+ void __iomem *reg;
+ const char *clk_name;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clk_num = num_pll_outputs;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name))
+ goto err;
+
+ /*
+ * PLL0 HS (high speed) output
+ */
+ clk_data->clks[0] = clkgen_pll_register(parent_name,
+ &st_pll1600c65_ax,
+ reg + CLKGENAx_PLL0_OFFSET,
+ clk_name);
+
+ if (IS_ERR(clk_data->clks[0]))
+ goto err;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 1, &clk_name))
+ goto err;
+
+ /*
+ * PLL0 LS (low speed) output, which is a fixed divide by 2 of the
+ * high speed output.
+ */
+ clk_data->clks[1] = clkgen_c65_lsdiv_register(__clk_get_name
+ (clk_data->clks[0]),
+ clk_name);
+
+ if (IS_ERR(clk_data->clks[1]))
+ goto err;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 2, &clk_name))
+ goto err;
+
+ /*
+ * PLL1 output
+ */
+ clk_data->clks[2] = clkgen_pll_register(parent_name,
+ &st_pll800c65_ax,
+ reg + CLKGENAx_PLL1_OFFSET,
+ clk_name);
+
+ if (IS_ERR(clk_data->clks[2]))
+ goto err;
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+
+err:
+ kfree(clk_data->clks);
+ kfree(clk_data);
+}
+CLK_OF_DECLARE(clkgena_c65_plls,
+ "st,clkgena-plls-c65", clkgena_c65_pll_setup);
+
+static struct clk * __init clkgen_odf_register(const char *parent_name,
+ void * __iomem reg,
+ struct clkgen_pll_data *pll_data,
+ int odf,
+ spinlock_t *odf_lock,
+ const char *odf_name)
+{
+ struct clk *clk;
+ unsigned long flags;
+ struct clk_gate *gate;
+ struct clk_divider *div;
+
+ flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->flags = CLK_GATE_SET_TO_DISABLE;
+ gate->reg = reg + pll_data->odf_gate[odf].offset;
+ gate->bit_idx = pll_data->odf_gate[odf].shift;
+ gate->lock = odf_lock;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO;
+ div->reg = reg + pll_data->odf[odf].offset;
+ div->shift = pll_data->odf[odf].shift;
+ div->width = fls(pll_data->odf[odf].mask);
+ div->lock = odf_lock;
+
+ clk = clk_register_composite(NULL, odf_name, &parent_name, 1,
+ NULL, NULL,
+ &div->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops,
+ flags);
+ if (IS_ERR(clk))
+ return clk;
+
+ pr_debug("%s: parent %s rate %lu\n",
+ __clk_get_name(clk),
+ __clk_get_name(clk_get_parent(clk)),
+ clk_get_rate(clk));
+ return clk;
+}
+
+static struct of_device_id c32_pll_of_match[] = {
+ {
+ .compatible = "st,plls-c32-a1x-0",
+ .data = &st_pll3200c32_a1x_0,
+ },
+ {
+ .compatible = "st,plls-c32-a1x-1",
+ .data = &st_pll3200c32_a1x_1,
+ },
+ {
+ .compatible = "st,stih415-plls-c32-a9",
+ .data = &st_pll3200c32_a9_415,
+ },
+ {
+ .compatible = "st,stih415-plls-c32-ddr",
+ .data = &st_pll3200c32_ddr_415,
+ },
+ {
+ .compatible = "st,stih416-plls-c32-a9",
+ .data = &st_pll3200c32_a9_416,
+ },
+ {
+ .compatible = "st,stih416-plls-c32-ddr",
+ .data = &st_pll3200c32_ddr_416,
+ },
+ {}
+};
+
+static void __init clkgen_c32_pll_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ const char *parent_name, *pll_name;
+ void __iomem *pll_base;
+ int num_odfs, odf;
+ struct clk_onecell_data *clk_data;
+ struct clkgen_pll_data *data;
+
+ match = of_match_node(c32_pll_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgen_pll_data *) match->data;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ pll_base = clkgen_get_register_base(np);
+ if (!pll_base)
+ return;
+
+ clk = clkgen_pll_register(parent_name, data, pll_base, np->name);
+ if (IS_ERR(clk))
+ return;
+
+ pll_name = __clk_get_name(clk);
+
+ num_odfs = data->num_odfs;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clk_num = num_odfs;
+ clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+
+ if (!clk_data->clks)
+ goto err;
+
+ for (odf = 0; odf < num_odfs; odf++) {
+ struct clk *clk;
+ const char *clk_name;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ odf, &clk_name))
+ return;
+
+ clk = clkgen_odf_register(pll_name, pll_base, data,
+ odf, &clkgena_c32_odf_lock, clk_name);
+ if (IS_ERR(clk))
+ goto err;
+
+ clk_data->clks[odf] = clk;
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ return;
+
+err:
+ kfree(pll_name);
+ kfree(clk_data->clks);
+ kfree(clk_data);
+}
+CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup);
+
+static struct of_device_id c32_gpu_pll_of_match[] = {
+ {
+ .compatible = "st,stih415-gpu-pll-c32",
+ .data = &st_pll1200c32_gpu_415,
+ },
+ {
+ .compatible = "st,stih416-gpu-pll-c32",
+ .data = &st_pll1200c32_gpu_416,
+ },
+};
+
+static void __init clkgengpu_c32_pll_setup(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct clk *clk;
+ const char *parent_name;
+ void __iomem *reg;
+ const char *clk_name;
+ struct clkgen_pll_data *data;
+
+ match = of_match_node(c32_gpu_pll_of_match, np);
+ if (!match) {
+ pr_err("%s: No matching data\n", __func__);
+ return;
+ }
+
+ data = (struct clkgen_pll_data *)match->data;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ reg = clkgen_get_register_base(np);
+ if (!reg)
+ return;
+
+ if (of_property_read_string_index(np, "clock-output-names",
+ 0, &clk_name))
+ return;
+
+ /*
+ * PLL 1200MHz output
+ */
+ clk = clkgen_pll_register(parent_name, data, reg, clk_name);
+
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+ return;
+}
+CLK_OF_DECLARE(clkgengpu_c32_pll,
+ "st,clkgengpu-pll-c32", clkgengpu_c32_pll_setup);
diff --git a/drivers/clk/st/clkgen.h b/drivers/clk/st/clkgen.h
new file mode 100644
index 000000000000..35c863295268
--- /dev/null
+++ b/drivers/clk/st/clkgen.h
@@ -0,0 +1,48 @@
+/************************************************************************
+File : Clock H/w specific Information
+
+Author: Pankaj Dev <pankaj.dev@st.com>
+
+Copyright (C) 2014 STMicroelectronics
+************************************************************************/
+
+#ifndef __CLKGEN_INFO_H
+#define __CLKGEN_INFO_H
+
+struct clkgen_field {
+ unsigned int offset;
+ unsigned int mask;
+ unsigned int shift;
+};
+
+static inline unsigned long clkgen_read(void __iomem *base,
+ struct clkgen_field *field)
+{
+ return (readl(base + field->offset) >> field->shift) & field->mask;
+}
+
+
+static inline void clkgen_write(void __iomem *base, struct clkgen_field *field,
+ unsigned long val)
+{
+ writel((readl(base + field->offset) &
+ ~(field->mask << field->shift)) | (val << field->shift),
+ base + field->offset);
+
+ return;
+}
+
+#define CLKGEN_FIELD(_offset, _mask, _shift) { \
+ .offset = _offset, \
+ .mask = _mask, \
+ .shift = _shift, \
+ }
+
+#define CLKGEN_READ(pll, field) clkgen_read(pll->regs_base, \
+ &pll->data->field)
+
+#define CLKGEN_WRITE(pll, field, val) clkgen_write(pll->regs_base, \
+ &pll->data->field, val)
+
+#endif /*__CLKGEN_INFO_H*/
+
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index abb6c5ac8a10..bd7dc733c1ca 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -18,6 +18,7 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/reset-controller.h>
#include "clk-factors.h"
@@ -51,6 +52,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
if (!gate)
goto err_free_fixed;
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
/* set up gate and fixed rate properties */
gate->reg = of_iomap(node, 0);
gate->bit_idx = SUNXI_OSC24M_GATE;
@@ -77,7 +80,7 @@ err_free_gate:
err_free_fixed:
kfree(fixed);
}
-CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup);
+CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup);
@@ -249,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
*n = DIV_ROUND_UP(div, (*k+1));
}
+/**
+ * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6
+ * PLL6 rate is calculated as follows
+ * rate = parent_rate * n * (k + 1) / 2
+ * parent_rate is always 24Mhz
+ */
+
+static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
+ u8 *n, u8 *k, u8 *m, u8 *p)
+{
+ u8 div;
+
+ /*
+ * We always have 24MHz / 2, so we can just say that our
+ * parent clock is 12MHz.
+ */
+ parent_rate = parent_rate / 2;
+ /* Normalize value to a parent_rate multiple (24M / 2) */
+ div = *freq / parent_rate;
+ *freq = parent_rate * div;
+
+ /* we were called to round the frequency, we can now return */
+ if (n == NULL)
+ return;
+
+ *k = div / 32;
+ if (*k > 3)
+ *k = 3;
+
+ *n = DIV_ROUND_UP(div, (*k+1));
+}
/**
* sun4i_get_apb1_factors() - calculates m, p factors for APB1
@@ -265,7 +299,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
if (parent_rate < *freq)
*freq = parent_rate;
- parent_rate = (parent_rate + (*freq - 1)) / *freq;
+ parent_rate = DIV_ROUND_UP(parent_rate, *freq);
/* Invalid rate! */
if (parent_rate > 32)
@@ -296,7 +330,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
/**
* sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
- * MMC rate is calculated as follows
+ * MOD0 rate is calculated as follows
* rate = (parent_rate >> p) / (m + 1);
*/
@@ -310,7 +344,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
if (*freq > parent_rate)
*freq = parent_rate;
- div = parent_rate / *freq;
+ div = DIV_ROUND_UP(parent_rate, *freq);
if (div < 16)
calcp = 0;
@@ -351,7 +385,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
if (*freq > parent_rate)
*freq = parent_rate;
- div = parent_rate / *freq;
+ div = DIV_ROUND_UP(parent_rate, *freq);
if (div < 32)
calcp = 0;
@@ -377,6 +411,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
/**
+ * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module
+ *
+ * This clock looks something like this
+ * ________________________
+ * MII TX clock from PHY >-----|___________ _________|----> to GMAC core
+ * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY
+ * Ext. 125MHz RGMII TX clk >--|__divider__/ |
+ * |________________________|
+ *
+ * The external 125 MHz reference is optional, i.e. GMAC can use its
+ * internal TX clock just fine. The A31 GMAC clock module does not have
+ * the divider controls for the external reference.
+ *
+ * To keep it simple, let the GMAC use either the MII TX clock for MII mode,
+ * and its internal TX clock for GMII and RGMII modes. The GMAC driver should
+ * select the appropriate source and gate/ungate the output to the PHY.
+ *
+ * Only the GMAC should use this clock. Altering the clock so that it doesn't
+ * match the GMAC's operation parameters will result in the GMAC not being
+ * able to send traffic out. The GMAC driver should set the clock rate and
+ * enable/disable this clock to configure the required state. The clock
+ * driver then responds by auto-reparenting the clock.
+ */
+
+#define SUN7I_A20_GMAC_GPIT 2
+#define SUN7I_A20_GMAC_MASK 0x3
+#define SUN7I_A20_GMAC_PARENTS 2
+
+static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ struct clk_mux *mux;
+ struct clk_gate *gate;
+ const char *clk_name = node->name;
+ const char *parents[SUN7I_A20_GMAC_PARENTS];
+ void *reg;
+
+ if (of_property_read_string(node, "clock-output-names", &clk_name))
+ return;
+
+ /* allocate mux and gate clock structs */
+ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+ if (!mux)
+ return;
+
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate)
+ goto free_mux;
+
+ /* gmac clock requires exactly 2 parents */
+ parents[0] = of_clk_get_parent_name(node, 0);
+ parents[1] = of_clk_get_parent_name(node, 1);
+ if (!parents[0] || !parents[1])
+ goto free_gate;
+
+ reg = of_iomap(node, 0);
+ if (!reg)
+ goto free_gate;
+
+ /* set up gate and fixed rate properties */
+ gate->reg = reg;
+ gate->bit_idx = SUN7I_A20_GMAC_GPIT;
+ gate->lock = &clk_lock;
+ mux->reg = reg;
+ mux->mask = SUN7I_A20_GMAC_MASK;
+ mux->flags = CLK_MUX_INDEX_BIT;
+ mux->lock = &clk_lock;
+
+ clk = clk_register_composite(NULL, clk_name,
+ parents, SUN7I_A20_GMAC_PARENTS,
+ &mux->hw, &clk_mux_ops,
+ NULL, NULL,
+ &gate->hw, &clk_gate_ops,
+ 0);
+
+ if (IS_ERR(clk))
+ goto iounmap_reg;
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+
+ return;
+
+iounmap_reg:
+ iounmap(reg);
+free_gate:
+ kfree(gate);
+free_mux:
+ kfree(mux);
+}
+CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
+ sun7i_a20_gmac_clk_setup);
+
+
+
+/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
@@ -387,6 +517,7 @@ struct factors_data {
int mux;
struct clk_factors_config *table;
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+ const char *name;
};
static struct clk_factors_config sun4i_pll1_config = {
@@ -416,6 +547,13 @@ static struct clk_factors_config sun4i_pll5_config = {
.kwidth = 2,
};
+static struct clk_factors_config sun6i_a31_pll6_config = {
+ .nshift = 8,
+ .nwidth = 5,
+ .kshift = 4,
+ .kwidth = 2,
+};
+
static struct clk_factors_config sun4i_apb1_config = {
.mshift = 0,
.mwidth = 5,
@@ -451,10 +589,30 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = {
.getter = sun6i_a31_get_pll1_factors,
};
+static const struct factors_data sun7i_a20_pll4_data __initconst = {
+ .enable = 31,
+ .table = &sun4i_pll5_config,
+ .getter = sun4i_get_pll5_factors,
+};
+
static const struct factors_data sun4i_pll5_data __initconst = {
.enable = 31,
.table = &sun4i_pll5_config,
.getter = sun4i_get_pll5_factors,
+ .name = "pll5",
+};
+
+static const struct factors_data sun4i_pll6_data __initconst = {
+ .enable = 31,
+ .table = &sun4i_pll5_config,
+ .getter = sun4i_get_pll5_factors,
+ .name = "pll6",
+};
+
+static const struct factors_data sun6i_a31_pll6_data __initconst = {
+ .enable = 31,
+ .table = &sun6i_a31_pll6_config,
+ .getter = sun6i_a31_get_pll6_factors,
};
static const struct factors_data sun4i_apb1_data __initconst = {
@@ -497,14 +655,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
- /* Nodes should be providing the name via clock-output-names
- * but originally our dts didn't, and so we used node->name.
- * The new, better nodes look like clk@deadbeef, so we pull the
- * name just in this case */
- if (!strcmp("clk", clk_name)) {
- of_property_read_string_index(node, "clock-output-names",
- 0, &clk_name);
- }
+ /*
+ * some factor clocks, such as pll5 and pll6, may have multiple
+ * outputs, and have their name designated in factors_data
+ */
+ if (data->name)
+ clk_name = data->name;
+ else
+ of_property_read_string(node, "clock-output-names", &clk_name);
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
if (!factors)
@@ -601,6 +759,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
clk = clk_register_mux(NULL, clk_name, parents, i,
CLK_SET_RATE_NO_REPARENT, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
@@ -660,6 +820,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
clk_parent = of_clk_get_parent_name(node, 0);
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
reg, data->shift, data->width,
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
@@ -673,6 +835,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
/**
+ * sunxi_gates_reset... - reset bits in leaf gate clk registers handling
+ */
+
+struct gates_reset_data {
+ void __iomem *reg;
+ spinlock_t *lock;
+ struct reset_controller_dev rcdev;
+};
+
+static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gates_reset_data *data = container_of(rcdev,
+ struct gates_reset_data,
+ rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(data->lock, flags);
+
+ reg = readl(data->reg);
+ writel(reg & ~BIT(id), data->reg);
+
+ spin_unlock_irqrestore(data->lock, flags);
+
+ return 0;
+}
+
+static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gates_reset_data *data = container_of(rcdev,
+ struct gates_reset_data,
+ rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(data->lock, flags);
+
+ reg = readl(data->reg);
+ writel(reg | BIT(id), data->reg);
+
+ spin_unlock_irqrestore(data->lock, flags);
+
+ return 0;
+}
+
+static struct reset_control_ops sunxi_gates_reset_ops = {
+ .assert = sunxi_gates_reset_assert,
+ .deassert = sunxi_gates_reset_deassert,
+};
+
+/**
* sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
*/
@@ -680,6 +895,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
struct gates_data {
DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+ u32 reset_mask;
};
static const struct gates_data sun4i_axi_gates_data __initconst = {
@@ -746,10 +962,21 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = {
.mask = { 0xff80ff },
};
+static const struct gates_data sun4i_a10_usb_gates_data __initconst = {
+ .mask = {0x1C0},
+ .reset_mask = 0x07,
+};
+
+static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
+ .mask = {0x140},
+ .reset_mask = 0x03,
+};
+
static void __init sunxi_gates_clk_setup(struct device_node *node,
struct gates_data *data)
{
struct clk_onecell_data *clk_data;
+ struct gates_reset_data *reset_data;
const char *clk_parent;
const char *clk_name;
void *reg;
@@ -793,6 +1020,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
clk_data->clk_num = i;
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ /* Register a reset controler for gates with reset bits */
+ if (data->reset_mask == 0)
+ return;
+
+ reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+ if (!reset_data)
+ return;
+
+ reset_data->reg = reg;
+ reset_data->lock = &clk_lock;
+ reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1;
+ reset_data->rcdev.ops = &sunxi_gates_reset_ops;
+ reset_data->rcdev.of_node = node;
+ reset_controller_register(&reset_data->rcdev);
}
@@ -832,7 +1074,7 @@ static const struct divs_data pll5_divs_data __initconst = {
};
static const struct divs_data pll6_divs_data __initconst = {
- .factors = &sun4i_pll5_data,
+ .factors = &sun4i_pll6_data,
.div = {
{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
{ .fixed = 2 }, /* P, other */
@@ -854,7 +1096,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
struct divs_data *data)
{
struct clk_onecell_data *clk_data;
- const char *parent = node->name;
+ const char *parent;
const char *clk_name;
struct clk **clks, *pclk;
struct clk_hw *gate_hw, *rate_hw;
@@ -868,6 +1110,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
/* Set up factor clock that we will be dividing */
pclk = sunxi_factors_clk_setup(node, data->factors);
+ parent = __clk_get_name(pclk);
reg = of_iomap(node, 0);
@@ -970,56 +1213,60 @@ free_clkdata:
/* Matches for factors clocks */
static const struct of_device_id clk_factors_match[] __initconst = {
- {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,},
+ {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,},
{.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,},
- {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,},
- {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,},
+ {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
+ {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
+ {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
+ {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
{}
};
/* Matches for divider clocks */
static const struct of_device_id clk_div_match[] __initconst = {
- {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,},
- {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,},
- {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,},
+ {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,},
+ {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,},
+ {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,},
{.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,},
{}
};
/* Matches for divided outputs */
static const struct of_device_id clk_divs_match[] __initconst = {
- {.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,},
- {.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,},
+ {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,},
+ {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,},
{}
};
/* Matches for mux clocks */
static const struct of_device_id clk_mux_match[] __initconst = {
- {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,},
- {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
+ {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,},
+ {.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
{.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,},
{}
};
/* Matches for gate clocks */
static const struct of_device_id clk_gates_match[] __initconst = {
- {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,},
- {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
{.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,},
{.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,},
{.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,},
{.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,},
- {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
{.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,},
{.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,},
{.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,},
- {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
{.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,},
{.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,},
{.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,},
{.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,},
{.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,},
+ {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,},
+ {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,},
{}
};
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index 356e9b804421..9e899c18af86 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -130,7 +130,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
.disable = clk_periph_disable,
};
-const struct clk_ops tegra_clk_periph_no_gate_ops = {
+static const struct clk_ops tegra_clk_periph_no_gate_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.recalc_rate = clk_periph_recalc_rate,
diff --git a/drivers/clk/ti/clk-33xx.c b/drivers/clk/ti/clk-33xx.c
index 776ee4594bd4..028b33783d38 100644
--- a/drivers/clk/ti/clk-33xx.c
+++ b/drivers/clk/ti/clk-33xx.c
@@ -34,7 +34,6 @@ static struct ti_dt_clk am33xx_clks[] = {
DT_CLK(NULL, "dpll_core_m5_ck", "dpll_core_m5_ck"),
DT_CLK(NULL, "dpll_core_m6_ck", "dpll_core_m6_ck"),
DT_CLK(NULL, "dpll_mpu_ck", "dpll_mpu_ck"),
- DT_CLK("cpu0", NULL, "dpll_mpu_ck"),
DT_CLK(NULL, "dpll_mpu_m2_ck", "dpll_mpu_m2_ck"),
DT_CLK(NULL, "dpll_ddr_ck", "dpll_ddr_ck"),
DT_CLK(NULL, "dpll_ddr_m2_ck", "dpll_ddr_m2_ck"),
diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c
index d3230234f07b..0d1750a8aea4 100644
--- a/drivers/clk/ti/clk-3xxx.c
+++ b/drivers/clk/ti/clk-3xxx.c
@@ -130,10 +130,6 @@ static struct ti_dt_clk omap3xxx_clks[] = {
DT_CLK(NULL, "dss_tv_fck", "dss_tv_fck"),
DT_CLK(NULL, "dss_96m_fck", "dss_96m_fck"),
DT_CLK(NULL, "dss2_alwon_fck", "dss2_alwon_fck"),
- DT_CLK(NULL, "utmi_p1_gfclk", "dummy_ck"),
- DT_CLK(NULL, "utmi_p2_gfclk", "dummy_ck"),
- DT_CLK(NULL, "xclk60mhsp1_ck", "dummy_ck"),
- DT_CLK(NULL, "xclk60mhsp2_ck", "dummy_ck"),
DT_CLK(NULL, "init_60m_fclk", "dummy_ck"),
DT_CLK(NULL, "gpt1_fck", "gpt1_fck"),
DT_CLK(NULL, "aes2_ick", "aes2_ick"),
diff --git a/drivers/clk/ti/clk-44xx.c b/drivers/clk/ti/clk-44xx.c
index ae00218b5da3..02517a8206bd 100644
--- a/drivers/clk/ti/clk-44xx.c
+++ b/drivers/clk/ti/clk-44xx.c
@@ -222,7 +222,6 @@ static struct ti_dt_clk omap44xx_clks[] = {
DT_CLK(NULL, "auxclk5_src_ck", "auxclk5_src_ck"),
DT_CLK(NULL, "auxclk5_ck", "auxclk5_ck"),
DT_CLK(NULL, "auxclkreq5_ck", "auxclkreq5_ck"),
- DT_CLK("50000000.gpmc", "fck", "dummy_ck"),
DT_CLK("omap_i2c.1", "ick", "dummy_ck"),
DT_CLK("omap_i2c.2", "ick", "dummy_ck"),
DT_CLK("omap_i2c.3", "ick", "dummy_ck"),
diff --git a/drivers/clk/ti/clk-54xx.c b/drivers/clk/ti/clk-54xx.c
index 0ef9f581286b..08f3d1b915b3 100644
--- a/drivers/clk/ti/clk-54xx.c
+++ b/drivers/clk/ti/clk-54xx.c
@@ -182,7 +182,6 @@ static struct ti_dt_clk omap54xx_clks[] = {
DT_CLK(NULL, "auxclk3_src_ck", "auxclk3_src_ck"),
DT_CLK(NULL, "auxclk3_ck", "auxclk3_ck"),
DT_CLK(NULL, "auxclkreq3_ck", "auxclkreq3_ck"),
- DT_CLK(NULL, "gpmc_ck", "dummy_ck"),
DT_CLK("omap_i2c.1", "ick", "dummy_ck"),
DT_CLK("omap_i2c.2", "ick", "dummy_ck"),
DT_CLK("omap_i2c.3", "ick", "dummy_ck"),
diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c
index 9977653f2d63..f7e40734c819 100644
--- a/drivers/clk/ti/clk-7xx.c
+++ b/drivers/clk/ti/clk-7xx.c
@@ -262,7 +262,6 @@ static struct ti_dt_clk dra7xx_clks[] = {
DT_CLK(NULL, "vip1_gclk_mux", "vip1_gclk_mux"),
DT_CLK(NULL, "vip2_gclk_mux", "vip2_gclk_mux"),
DT_CLK(NULL, "vip3_gclk_mux", "vip3_gclk_mux"),
- DT_CLK(NULL, "gpmc_ck", "dummy_ck"),
DT_CLK("omap_i2c.1", "ick", "dummy_ck"),
DT_CLK("omap_i2c.2", "ick", "dummy_ck"),
DT_CLK("omap_i2c.3", "ick", "dummy_ck"),
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index a15e445570b2..e6aa10db7bba 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -112,7 +112,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
- return parent_rate / div;
+ return DIV_ROUND_UP(parent_rate, div);
}
/*
@@ -182,7 +182,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
- now = parent_rate / i;
+ now = DIV_ROUND_UP(parent_rate, i);
if (now <= rate && now > best) {
bestdiv = i;
best = now;
@@ -205,7 +205,7 @@ static long ti_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
int div;
div = ti_clk_divider_bestdiv(hw, rate, prate);
- return *prate / div;
+ return DIV_ROUND_UP(*prate, div);
}
static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -216,7 +216,7 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long flags = 0;
u32 val;
- div = parent_rate / rate;
+ div = DIV_ROUND_UP(parent_rate, rate);
value = _get_val(divider, div);
if (value > div_mask(divider))
diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c
index cdeff299de26..7b55ef89baa5 100644
--- a/drivers/clk/ux500/u8500_of_clk.c
+++ b/drivers/clk/ux500/u8500_of_clk.c
@@ -29,7 +29,8 @@ static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_C
#define PRCC_KCLK_STORE(clk, base, bit) \
prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk
-struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data)
+static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec,
+ void *data)
{
struct clk **clk_data = data;
unsigned int base, bit;
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
index 8cbfcf88fae3..a820b0cfcf57 100644
--- a/drivers/clk/versatile/clk-icst.c
+++ b/drivers/clk/versatile/clk-icst.c
@@ -33,7 +33,7 @@ struct clk_icst {
struct clk_hw hw;
void __iomem *vcoreg;
void __iomem *lockreg;
- const struct icst_params *params;
+ struct icst_params *params;
unsigned long rate;
};
@@ -84,6 +84,8 @@ static unsigned long icst_recalc_rate(struct clk_hw *hw,
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ if (parent_rate)
+ icst->params->ref = parent_rate;
vco = vco_get(icst->vcoreg);
icst->rate = icst_hz(icst->params, vco);
return icst->rate;
@@ -105,6 +107,8 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ if (parent_rate)
+ icst->params->ref = parent_rate;
vco = icst_hz_to_vco(icst->params, rate);
icst->rate = icst_hz(icst->params, vco);
vco_set(icst->lockreg, icst->vcoreg, vco);
@@ -120,24 +124,33 @@ static const struct clk_ops icst_ops = {
struct clk *icst_clk_register(struct device *dev,
const struct clk_icst_desc *desc,
const char *name,
+ const char *parent_name,
void __iomem *base)
{
struct clk *clk;
struct clk_icst *icst;
struct clk_init_data init;
+ struct icst_params *pclone;
icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL);
if (!icst) {
pr_err("could not allocate ICST clock!\n");
return ERR_PTR(-ENOMEM);
}
+
+ pclone = kmemdup(desc->params, sizeof(*pclone), GFP_KERNEL);
+ if (!pclone) {
+ pr_err("could not clone ICST params\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
init.name = name;
init.ops = &icst_ops;
init.flags = CLK_IS_ROOT;
- init.parent_names = NULL;
- init.num_parents = 0;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
icst->hw.init = &init;
- icst->params = desc->params;
+ icst->params = pclone;
icst->vcoreg = base + desc->vco_offset;
icst->lockreg = base + desc->lock_offset;
diff --git a/drivers/clk/versatile/clk-icst.h b/drivers/clk/versatile/clk-icst.h
index be99dd0da785..04e6f0aef588 100644
--- a/drivers/clk/versatile/clk-icst.h
+++ b/drivers/clk/versatile/clk-icst.h
@@ -16,4 +16,5 @@ struct clk_icst_desc {
struct clk *icst_clk_register(struct device *dev,
const struct clk_icst_desc *desc,
const char *name,
+ const char *parent_name,
void __iomem *base);
diff --git a/drivers/clk/versatile/clk-impd1.c b/drivers/clk/versatile/clk-impd1.c
index 844f8d711a12..31b44f025f9e 100644
--- a/drivers/clk/versatile/clk-impd1.c
+++ b/drivers/clk/versatile/clk-impd1.c
@@ -13,10 +13,12 @@
#include <linux/io.h>
#include <linux/platform_data/clk-integrator.h>
-#include <mach/impd1.h>
-
#include "clk-icst.h"
+#define IMPD1_OSC1 0x00
+#define IMPD1_OSC2 0x04
+#define IMPD1_LOCK 0x08
+
struct impd1_clk {
char *vco1name;
struct clk *vco1clk;
@@ -93,13 +95,15 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
imc = &impd1_clks[id];
imc->vco1name = kasprintf(GFP_KERNEL, "lm%x-vco1", id);
- clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, base);
+ clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, NULL,
+ base);
imc->vco1clk = clk;
imc->clks[0] = clkdev_alloc(clk, NULL, "lm%x:01000", id);
/* VCO2 is also called "CLK2" */
imc->vco2name = kasprintf(GFP_KERNEL, "lm%x-vco2", id);
- clk = icst_clk_register(NULL, &impd1_icst2_desc, imc->vco2name, base);
+ clk = icst_clk_register(NULL, &impd1_icst2_desc, imc->vco2name, NULL,
+ base);
imc->vco2clk = clk;
/* MMCI uses CLK2 right off */
diff --git a/drivers/clk/versatile/clk-integrator.c b/drivers/clk/versatile/clk-integrator.c
index bda8967e09c2..734c4b8fe6ab 100644
--- a/drivers/clk/versatile/clk-integrator.c
+++ b/drivers/clk/versatile/clk-integrator.c
@@ -10,21 +10,17 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
-#include <linux/platform_data/clk-integrator.h>
-
-#include <mach/hardware.h>
-#include <mach/platform.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include "clk-icst.h"
-/*
- * Implementation of the ARM Integrator/AP and Integrator/CP clock tree.
- * Inspired by portions of:
- * plat-versatile/clock.c and plat-versatile/include/plat/clock.h
- */
+#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
-static const struct icst_params cp_auxvco_params = {
- .ref = 24000000,
+/* Base offset for the core module */
+static void __iomem *cm_base;
+
+static const struct icst_params cp_auxosc_params = {
.vco_max = ICST525_VCO_MAX_5V,
.vco_min = ICST525_VCO_MIN,
.vd_min = 8,
@@ -35,50 +31,39 @@ static const struct icst_params cp_auxvco_params = {
.idx2s = icst525_idx2s,
};
-static const struct clk_icst_desc __initdata cp_icst_desc = {
- .params = &cp_auxvco_params,
+static const struct clk_icst_desc __initdata cm_auxosc_desc = {
+ .params = &cp_auxosc_params,
.vco_offset = 0x1c,
.lock_offset = INTEGRATOR_HDR_LOCK_OFFSET,
};
-/*
- * integrator_clk_init() - set up the integrator clock tree
- * @is_cp: pass true if it's the Integrator/CP else AP is assumed
- */
-void __init integrator_clk_init(bool is_cp)
+static void __init of_integrator_cm_osc_setup(struct device_node *np)
{
- struct clk *clk;
-
- /* APB clock dummy */
- clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
- clk_register_clkdev(clk, "apb_pclk", NULL);
-
- /* UART reference clock */
- clk = clk_register_fixed_rate(NULL, "uartclk", NULL, CLK_IS_ROOT,
- 14745600);
- clk_register_clkdev(clk, NULL, "uart0");
- clk_register_clkdev(clk, NULL, "uart1");
- if (is_cp)
- clk_register_clkdev(clk, NULL, "mmci");
-
- /* 24 MHz clock */
- clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT,
- 24000000);
- clk_register_clkdev(clk, NULL, "kmi0");
- clk_register_clkdev(clk, NULL, "kmi1");
- if (!is_cp)
- clk_register_clkdev(clk, NULL, "ap_timer");
+ struct clk *clk = ERR_PTR(-EINVAL);
+ const char *clk_name = np->name;
+ const struct clk_icst_desc *desc = &cm_auxosc_desc;
+ const char *parent_name;
- if (!is_cp)
- return;
+ if (!cm_base) {
+ /* Remap the core module base if not done yet */
+ struct device_node *parent;
- /* 1 MHz clock */
- clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT,
- 1000000);
- clk_register_clkdev(clk, NULL, "sp804");
+ parent = of_get_parent(np);
+ if (!np) {
+ pr_err("no parent on core module clock\n");
+ return;
+ }
+ cm_base = of_iomap(parent, 0);
+ if (!cm_base) {
+ pr_err("could not remap core module base\n");
+ return;
+ }
+ }
- /* ICST VCO clock used on the Integrator/CP CLCD */
- clk = icst_clk_register(NULL, &cp_icst_desc, "icst",
- __io_address(INTEGRATOR_HDR_BASE));
- clk_register_clkdev(clk, NULL, "clcd");
+ parent_name = of_clk_get_parent_name(np, 0);
+ clk = icst_clk_register(NULL, desc, clk_name, parent_name, cm_base);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
+CLK_OF_DECLARE(integrator_cm_auxosc_clk,
+ "arm,integrator-cm-auxosc", of_integrator_cm_osc_setup);
diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c
index 747e7b31117c..c8b523117fb7 100644
--- a/drivers/clk/versatile/clk-realview.c
+++ b/drivers/clk/versatile/clk-realview.c
@@ -85,10 +85,10 @@ void __init realview_clk_init(void __iomem *sysbase, bool is_pb1176)
/* ICST VCO clock */
if (is_pb1176)
clk = icst_clk_register(NULL, &realview_osc0_desc,
- "osc0", sysbase);
+ "osc0", NULL, sysbase);
else
clk = icst_clk_register(NULL, &realview_osc4_desc,
- "osc4", sysbase);
+ "osc4", NULL, sysbase);
clk_register_clkdev(clk, NULL, "dev:clcd");
clk_register_clkdev(clk, NULL, "issp:clcd");
diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c
index 09dd0173ea0a..52c09afdcfb7 100644
--- a/drivers/clk/zynq/clkc.c
+++ b/drivers/clk/zynq/clkc.c
@@ -21,34 +21,35 @@
#include <linux/clk/zynq.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/io.h>
-static void __iomem *zynq_slcr_base_priv;
-
-#define SLCR_ARMPLL_CTRL (zynq_slcr_base_priv + 0x100)
-#define SLCR_DDRPLL_CTRL (zynq_slcr_base_priv + 0x104)
-#define SLCR_IOPLL_CTRL (zynq_slcr_base_priv + 0x108)
-#define SLCR_PLL_STATUS (zynq_slcr_base_priv + 0x10c)
-#define SLCR_ARM_CLK_CTRL (zynq_slcr_base_priv + 0x120)
-#define SLCR_DDR_CLK_CTRL (zynq_slcr_base_priv + 0x124)
-#define SLCR_DCI_CLK_CTRL (zynq_slcr_base_priv + 0x128)
-#define SLCR_APER_CLK_CTRL (zynq_slcr_base_priv + 0x12c)
-#define SLCR_GEM0_CLK_CTRL (zynq_slcr_base_priv + 0x140)
-#define SLCR_GEM1_CLK_CTRL (zynq_slcr_base_priv + 0x144)
-#define SLCR_SMC_CLK_CTRL (zynq_slcr_base_priv + 0x148)
-#define SLCR_LQSPI_CLK_CTRL (zynq_slcr_base_priv + 0x14c)
-#define SLCR_SDIO_CLK_CTRL (zynq_slcr_base_priv + 0x150)
-#define SLCR_UART_CLK_CTRL (zynq_slcr_base_priv + 0x154)
-#define SLCR_SPI_CLK_CTRL (zynq_slcr_base_priv + 0x158)
-#define SLCR_CAN_CLK_CTRL (zynq_slcr_base_priv + 0x15c)
-#define SLCR_CAN_MIOCLK_CTRL (zynq_slcr_base_priv + 0x160)
-#define SLCR_DBG_CLK_CTRL (zynq_slcr_base_priv + 0x164)
-#define SLCR_PCAP_CLK_CTRL (zynq_slcr_base_priv + 0x168)
-#define SLCR_FPGA0_CLK_CTRL (zynq_slcr_base_priv + 0x170)
-#define SLCR_621_TRUE (zynq_slcr_base_priv + 0x1c4)
-#define SLCR_SWDT_CLK_SEL (zynq_slcr_base_priv + 0x304)
+static void __iomem *zynq_clkc_base;
+
+#define SLCR_ARMPLL_CTRL (zynq_clkc_base + 0x00)
+#define SLCR_DDRPLL_CTRL (zynq_clkc_base + 0x04)
+#define SLCR_IOPLL_CTRL (zynq_clkc_base + 0x08)
+#define SLCR_PLL_STATUS (zynq_clkc_base + 0x0c)
+#define SLCR_ARM_CLK_CTRL (zynq_clkc_base + 0x20)
+#define SLCR_DDR_CLK_CTRL (zynq_clkc_base + 0x24)
+#define SLCR_DCI_CLK_CTRL (zynq_clkc_base + 0x28)
+#define SLCR_APER_CLK_CTRL (zynq_clkc_base + 0x2c)
+#define SLCR_GEM0_CLK_CTRL (zynq_clkc_base + 0x40)
+#define SLCR_GEM1_CLK_CTRL (zynq_clkc_base + 0x44)
+#define SLCR_SMC_CLK_CTRL (zynq_clkc_base + 0x48)
+#define SLCR_LQSPI_CLK_CTRL (zynq_clkc_base + 0x4c)
+#define SLCR_SDIO_CLK_CTRL (zynq_clkc_base + 0x50)
+#define SLCR_UART_CLK_CTRL (zynq_clkc_base + 0x54)
+#define SLCR_SPI_CLK_CTRL (zynq_clkc_base + 0x58)
+#define SLCR_CAN_CLK_CTRL (zynq_clkc_base + 0x5c)
+#define SLCR_CAN_MIOCLK_CTRL (zynq_clkc_base + 0x60)
+#define SLCR_DBG_CLK_CTRL (zynq_clkc_base + 0x64)
+#define SLCR_PCAP_CLK_CTRL (zynq_clkc_base + 0x68)
+#define SLCR_FPGA0_CLK_CTRL (zynq_clkc_base + 0x70)
+#define SLCR_621_TRUE (zynq_clkc_base + 0xc4)
+#define SLCR_SWDT_CLK_SEL (zynq_clkc_base + 0x204)
#define NUM_MIO_PINS 54
@@ -148,7 +149,7 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk,
clks[fclk] = clk_register_gate(NULL, clk_name,
div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg,
0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock);
- enable_reg = readl(fclk_gate_reg) & 1;
+ enable_reg = clk_readl(fclk_gate_reg) & 1;
if (enable && !enable_reg) {
if (clk_prepare_enable(clks[fclk]))
pr_warn("%s: FCLK%u enable failed\n", __func__,
@@ -277,7 +278,7 @@ static void __init zynq_clk_setup(struct device_node *np)
SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock);
/* CPU clocks */
- tmp = readl(SLCR_621_TRUE) & 1;
+ tmp = clk_readl(SLCR_621_TRUE) & 1;
clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4,
CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0,
&armclk_lock);
@@ -569,8 +570,42 @@ static void __init zynq_clk_setup(struct device_node *np)
CLK_OF_DECLARE(zynq_clkc, "xlnx,ps7-clkc", zynq_clk_setup);
-void __init zynq_clock_init(void __iomem *slcr_base)
+void __init zynq_clock_init(void)
{
- zynq_slcr_base_priv = slcr_base;
- of_clk_init(NULL);
+ struct device_node *np;
+ struct device_node *slcr;
+ struct resource res;
+
+ np = of_find_compatible_node(NULL, NULL, "xlnx,ps7-clkc");
+ if (!np) {
+ pr_err("%s: clkc node not found\n", __func__);
+ goto np_err;
+ }
+
+ if (of_address_to_resource(np, 0, &res)) {
+ pr_err("%s: failed to get resource\n", np->name);
+ goto np_err;
+ }
+
+ slcr = of_get_parent(np);
+
+ if (slcr->data) {
+ zynq_clkc_base = (__force void __iomem *)slcr->data + res.start;
+ } else {
+ pr_err("%s: Unable to get I/O memory\n", np->name);
+ of_node_put(slcr);
+ goto np_err;
+ }
+
+ pr_info("%s: clkc starts at %p\n", __func__, zynq_clkc_base);
+
+ of_node_put(slcr);
+ of_node_put(np);
+
+ return;
+
+np_err:
+ of_node_put(np);
+ BUG();
+ return;
}
diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c
index 3226f54fa595..cec97596fe65 100644
--- a/drivers/clk/zynq/pll.c
+++ b/drivers/clk/zynq/pll.c
@@ -90,7 +90,7 @@ static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
* makes probably sense to redundantly save fbdiv in the struct
* zynq_pll to save the IO access.
*/
- fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
+ fbdiv = (clk_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
PLLCTRL_FBDIV_SHIFT;
return parent_rate * fbdiv;
@@ -112,7 +112,7 @@ static int zynq_pll_is_enabled(struct clk_hw *hw)
spin_lock_irqsave(clk->lock, flags);
- reg = readl(clk->pll_ctrl);
+ reg = clk_readl(clk->pll_ctrl);
spin_unlock_irqrestore(clk->lock, flags);
@@ -138,10 +138,10 @@ static int zynq_pll_enable(struct clk_hw *hw)
/* Power up PLL and wait for lock */
spin_lock_irqsave(clk->lock, flags);
- reg = readl(clk->pll_ctrl);
+ reg = clk_readl(clk->pll_ctrl);
reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);
- writel(reg, clk->pll_ctrl);
- while (!(readl(clk->pll_status) & (1 << clk->lockbit)))
+ clk_writel(reg, clk->pll_ctrl);
+ while (!(clk_readl(clk->pll_status) & (1 << clk->lockbit)))
;
spin_unlock_irqrestore(clk->lock, flags);
@@ -168,9 +168,9 @@ static void zynq_pll_disable(struct clk_hw *hw)
/* shut down PLL */
spin_lock_irqsave(clk->lock, flags);
- reg = readl(clk->pll_ctrl);
+ reg = clk_readl(clk->pll_ctrl);
reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK;
- writel(reg, clk->pll_ctrl);
+ clk_writel(reg, clk->pll_ctrl);
spin_unlock_irqrestore(clk->lock, flags);
}
@@ -225,9 +225,9 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent,
spin_lock_irqsave(pll->lock, flags);
- reg = readl(pll->pll_ctrl);
+ reg = clk_readl(pll->pll_ctrl);
reg &= ~PLLCTRL_BPQUAL_MASK;
- writel(reg, pll->pll_ctrl);
+ clk_writel(reg, pll->pll_ctrl);
spin_unlock_irqrestore(pll->lock, flags);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 52e9329e3c51..96918e1f26a3 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -188,3 +188,6 @@ config EM_TIMER_STI
This enables build of a clocksource and clockevent driver for
the 48-bit System Timer (STI) hardware available on a SoCs
such as EMEV2 from former NEC Electronics.
+
+config CLKSRC_QCOM
+ bool
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index f3fe4cb4974b..98cb6c51aa87 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
+obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
diff --git a/drivers/clocksource/dummy_timer.c b/drivers/clocksource/dummy_timer.c
index b3eb582d6a6f..ad3572541728 100644
--- a/drivers/clocksource/dummy_timer.c
+++ b/drivers/clocksource/dummy_timer.c
@@ -56,14 +56,19 @@ static struct notifier_block dummy_timer_cpu_nb = {
static int __init dummy_timer_register(void)
{
- int err = register_cpu_notifier(&dummy_timer_cpu_nb);
+ int err = 0;
+
+ cpu_notifier_register_begin();
+ err = __register_cpu_notifier(&dummy_timer_cpu_nb);
if (err)
- return err;
+ goto out;
/* We won't get a call on the boot CPU, so register immediately */
if (num_possible_cpus() > 1)
dummy_timer_setup();
- return 0;
+out:
+ cpu_notifier_register_done();
+ return err;
}
early_initcall(dummy_timer_register);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index c2e390efbdca..a6ee6d7cd63f 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -25,8 +25,6 @@
#include <linux/of_address.h>
#include <linux/clocksource.h>
-#include <asm/mach/time.h>
-
#define EXYNOS4_MCTREG(x) (x)
#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
new file mode 100644
index 000000000000..e807acf4c665
--- /dev/null
+++ b/drivers/clocksource/qcom-timer.c
@@ -0,0 +1,330 @@
+/*
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/cpu.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+
+#define TIMER_MATCH_VAL 0x0000
+#define TIMER_COUNT_VAL 0x0004
+#define TIMER_ENABLE 0x0008
+#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
+#define TIMER_ENABLE_EN BIT(0)
+#define TIMER_CLEAR 0x000C
+#define DGT_CLK_CTL 0x10
+#define DGT_CLK_CTL_DIV_4 0x3
+#define TIMER_STS_GPT0_CLR_PEND BIT(10)
+
+#define GPT_HZ 32768
+
+#define MSM_DGT_SHIFT 5
+
+static void __iomem *event_base;
+static void __iomem *sts_base;
+
+static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ /* Stop the timer tick */
+ if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
+ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
+ ctrl &= ~TIMER_ENABLE_EN;
+ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
+ }
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+}
+
+static int msm_timer_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
+
+ ctrl &= ~TIMER_ENABLE_EN;
+ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
+
+ writel_relaxed(ctrl, event_base + TIMER_CLEAR);
+ writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
+
+ if (sts_base)
+ while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
+ cpu_relax();
+
+ writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
+ return 0;
+}
+
+static void msm_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ u32 ctrl;
+
+ ctrl = readl_relaxed(event_base + TIMER_ENABLE);
+ ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_RESUME:
+ case CLOCK_EVT_MODE_PERIODIC:
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ /* Timer is enabled in set_next_event */
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ break;
+ }
+ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
+}
+
+static struct clock_event_device __percpu *msm_evt;
+
+static void __iomem *source_base;
+
+static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
+{
+ return readl_relaxed(source_base + TIMER_COUNT_VAL);
+}
+
+static struct clocksource msm_clocksource = {
+ .name = "dg_timer",
+ .rating = 300,
+ .read = msm_read_timer_count,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int msm_timer_irq;
+static int msm_timer_has_ppi;
+
+static int msm_local_timer_setup(struct clock_event_device *evt)
+{
+ int cpu = smp_processor_id();
+ int err;
+
+ evt->irq = msm_timer_irq;
+ evt->name = "msm_timer";
+ evt->features = CLOCK_EVT_FEAT_ONESHOT;
+ evt->rating = 200;
+ evt->set_mode = msm_timer_set_mode;
+ evt->set_next_event = msm_timer_set_next_event;
+ evt->cpumask = cpumask_of(cpu);
+
+ clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
+
+ if (msm_timer_has_ppi) {
+ enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
+ } else {
+ err = request_irq(evt->irq, msm_timer_interrupt,
+ IRQF_TIMER | IRQF_NOBALANCING |
+ IRQF_TRIGGER_RISING, "gp_timer", evt);
+ if (err)
+ pr_err("request_irq failed\n");
+ }
+
+ return 0;
+}
+
+static void msm_local_timer_stop(struct clock_event_device *evt)
+{
+ evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
+ disable_percpu_irq(evt->irq);
+}
+
+static int msm_timer_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ /*
+ * Grab cpu pointer in each case to avoid spurious
+ * preemptible warnings
+ */
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_STARTING:
+ msm_local_timer_setup(this_cpu_ptr(msm_evt));
+ break;
+ case CPU_DYING:
+ msm_local_timer_stop(this_cpu_ptr(msm_evt));
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block msm_timer_cpu_nb = {
+ .notifier_call = msm_timer_cpu_notify,
+};
+
+static u64 notrace msm_sched_clock_read(void)
+{
+ return msm_clocksource.read(&msm_clocksource);
+}
+
+static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
+ bool percpu)
+{
+ struct clocksource *cs = &msm_clocksource;
+ int res = 0;
+
+ msm_timer_irq = irq;
+ msm_timer_has_ppi = percpu;
+
+ msm_evt = alloc_percpu(struct clock_event_device);
+ if (!msm_evt) {
+ pr_err("memory allocation failed for clockevents\n");
+ goto err;
+ }
+
+ if (percpu)
+ res = request_percpu_irq(irq, msm_timer_interrupt,
+ "gp_timer", msm_evt);
+
+ if (res) {
+ pr_err("request_percpu_irq failed\n");
+ } else {
+ res = register_cpu_notifier(&msm_timer_cpu_nb);
+ if (res) {
+ free_percpu_irq(irq, msm_evt);
+ goto err;
+ }
+
+ /* Immediately configure the timer on the boot CPU */
+ msm_local_timer_setup(__this_cpu_ptr(msm_evt));
+ }
+
+err:
+ writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
+ res = clocksource_register_hz(cs, dgt_hz);
+ if (res)
+ pr_err("clocksource_register failed\n");
+ sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
+}
+
+#ifdef CONFIG_ARCH_QCOM
+static void __init msm_dt_timer_init(struct device_node *np)
+{
+ u32 freq;
+ int irq;
+ struct resource res;
+ u32 percpu_offset;
+ void __iomem *base;
+ void __iomem *cpu0_base;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("Failed to map event base\n");
+ return;
+ }
+
+ /* We use GPT0 for the clockevent */
+ irq = irq_of_parse_and_map(np, 1);
+ if (irq <= 0) {
+ pr_err("Can't get irq\n");
+ return;
+ }
+
+ /* We use CPU0's DGT for the clocksource */
+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
+ percpu_offset = 0;
+
+ if (of_address_to_resource(np, 0, &res)) {
+ pr_err("Failed to parse DGT resource\n");
+ return;
+ }
+
+ cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
+ if (!cpu0_base) {
+ pr_err("Failed to map source base\n");
+ return;
+ }
+
+ if (of_property_read_u32(np, "clock-frequency", &freq)) {
+ pr_err("Unknown frequency\n");
+ return;
+ }
+
+ event_base = base + 0x4;
+ sts_base = base + 0x88;
+ source_base = cpu0_base + 0x24;
+ freq /= 4;
+ writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
+
+ msm_timer_init(freq, 32, irq, !!percpu_offset);
+}
+CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+#else
+
+static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
+ u32 sts)
+{
+ void __iomem *base;
+
+ base = ioremap(addr, SZ_256);
+ if (!base) {
+ pr_err("Failed to map timer base\n");
+ return -ENOMEM;
+ }
+ event_base = base + event;
+ source_base = base + source;
+ if (sts)
+ sts_base = base + sts;
+
+ return 0;
+}
+
+static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
+{
+ /*
+ * Shift timer count down by a constant due to unreliable lower bits
+ * on some targets.
+ */
+ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
+}
+
+void __init msm7x01_timer_init(void)
+{
+ struct clocksource *cs = &msm_clocksource;
+
+ if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
+ return;
+ cs->read = msm_read_timer_count_shift;
+ cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
+ /* 600 KHz */
+ msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
+ false);
+}
+
+void __init msm7x30_timer_init(void)
+{
+ if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
+ return;
+ msm_timer_init(24576000 / 4, 32, 1, false);
+}
+
+void __init qsd8x50_timer_init(void)
+{
+ if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
+ return;
+ msm_timer_init(19200000 / 4, 32, 7, false);
+}
+#endif
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c
index 09a17d9a6594..b52e1c078b99 100644
--- a/drivers/clocksource/timer-marco.c
+++ b/drivers/clocksource/timer-marco.c
@@ -19,7 +19,8 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
-#include <asm/mach/time.h>
+
+#define MARCO_CLOCK_FREQ 1000000
#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
@@ -191,7 +192,7 @@ static int sirfsoc_local_timer_setup(struct clock_event_device *ce)
ce->rating = 200;
ce->set_mode = sirfsoc_timer_set_mode;
ce->set_next_event = sirfsoc_timer_set_next_event;
- clockevents_calc_mult_shift(ce, CLOCK_TICK_RATE, 60);
+ clockevents_calc_mult_shift(ce, MARCO_CLOCK_FREQ, 60);
ce->max_delta_ns = clockevent_delta2ns(-2, ce);
ce->min_delta_ns = clockevent_delta2ns(2, ce);
ce->cpumask = cpumask_of(cpu);
@@ -263,11 +264,11 @@ static void __init sirfsoc_marco_timer_init(void)
BUG_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
- BUG_ON(rate < CLOCK_TICK_RATE);
- BUG_ON(rate % CLOCK_TICK_RATE);
+ BUG_ON(rate < MARCO_CLOCK_FREQ);
+ BUG_ON(rate % MARCO_CLOCK_FREQ);
/* Initialize the timer dividers */
- timer_div = rate / CLOCK_TICK_RATE - 1;
+ timer_div = rate / MARCO_CLOCK_FREQ - 1;
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
@@ -283,7 +284,7 @@ static void __init sirfsoc_marco_timer_init(void)
/* Clear all interrupts */
writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
- BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+ BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, MARCO_CLOCK_FREQ));
sirfsoc_clockevent_init();
}
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
index 8a492d34ff9f..1a6b2d6356d6 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -21,6 +21,8 @@
#include <linux/sched_clock.h>
#include <asm/mach/time.h>
+#define PRIMA2_CLOCK_FREQ 1000000
+
#define SIRFSOC_TIMER_COUNTER_LO 0x0000
#define SIRFSOC_TIMER_COUNTER_HI 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0008
@@ -173,7 +175,7 @@ static u64 notrace sirfsoc_read_sched_clock(void)
static void __init sirfsoc_clockevent_init(void)
{
sirfsoc_clockevent.cpumask = cpumask_of(0);
- clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE,
+ clockevents_config_and_register(&sirfsoc_clockevent, PRIMA2_CLOCK_FREQ,
2, -2);
}
@@ -190,8 +192,8 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
rate = clk_get_rate(clk);
- BUG_ON(rate < CLOCK_TICK_RATE);
- BUG_ON(rate % CLOCK_TICK_RATE);
+ BUG_ON(rate < PRIMA2_CLOCK_FREQ);
+ BUG_ON(rate % PRIMA2_CLOCK_FREQ);
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base)
@@ -199,14 +201,16 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
- writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
+ writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
+ sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
- BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
+ BUG_ON(clocksource_register_hz(&sirfsoc_clocksource,
+ PRIMA2_CLOCK_FREQ));
- sched_clock_register(sirfsoc_read_sched_clock, 64, CLOCK_TICK_RATE);
+ sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ);
BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c
index e63d469661fd..5dcf756970e7 100644
--- a/drivers/clocksource/timer-u300.c
+++ b/drivers/clocksource/timer-u300.c
@@ -333,7 +333,7 @@ static irqreturn_t u300_timer_interrupt(int irq, void *dev_id)
static struct irqaction u300_timer_irq = {
.name = "U300 Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = u300_timer_interrupt,
};
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 9fb627046e17..1e2b9db563ec 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -122,7 +122,7 @@ config ARM_INTEGRATOR
If in doubt, say Y.
config ARM_KIRKWOOD_CPUFREQ
- def_bool ARCH_KIRKWOOD && OF
+ def_bool MACH_KIRKWOOD
help
This adds the CPUFreq driver for Marvell Kirkwood
SoCs.
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 822ca03a87f7..d5eaedbe464f 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -906,15 +906,16 @@ static void __init acpi_cpufreq_boost_init(void)
acpi_cpufreq_driver.boost_supported = true;
acpi_cpufreq_driver.boost_enabled = boost_state(0);
- get_online_cpus();
+
+ cpu_notifier_register_begin();
/* Force all MSRs to the same value */
boost_set_msrs(acpi_cpufreq_driver.boost_enabled,
cpu_online_mask);
- register_cpu_notifier(&boost_nb);
+ __register_cpu_notifier(&boost_nb);
- put_online_cpus();
+ cpu_notifier_register_done();
}
}
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index d988948a89a0..97ccc31dbdd8 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -22,7 +22,7 @@ config ARM_HIGHBANK_CPUIDLE
config ARM_KIRKWOOD_CPUIDLE
bool "CPU Idle Driver for Marvell Kirkwood SoCs"
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
help
This adds the CPU Idle driver for Marvell Kirkwood SoCs.
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 362e7c49f2e1..64ceca2920b8 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -5,6 +5,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -26,11 +27,21 @@ struct omap_dmadev {
spinlock_t lock;
struct tasklet_struct task;
struct list_head pending;
+ void __iomem *base;
+ const struct omap_dma_reg *reg_map;
+ struct omap_system_dma_plat_info *plat;
+ bool legacy;
+ spinlock_t irq_lock;
+ uint32_t irq_enable_mask;
+ struct omap_chan *lch_map[32];
};
struct omap_chan {
struct virt_dma_chan vc;
struct list_head node;
+ void __iomem *channel_base;
+ const struct omap_dma_reg *reg_map;
+ uint32_t ccr;
struct dma_slave_config cfg;
unsigned dma_sig;
@@ -54,19 +65,93 @@ struct omap_desc {
dma_addr_t dev_addr;
int16_t fi; /* for OMAP_DMA_SYNC_PACKET */
- uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */
- uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
- uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
- uint8_t periph_port; /* Peripheral port */
+ uint8_t es; /* CSDP_DATA_TYPE_xxx */
+ uint32_t ccr; /* CCR value */
+ uint16_t clnk_ctrl; /* CLNK_CTRL value */
+ uint16_t cicr; /* CICR value */
+ uint32_t csdp; /* CSDP value */
unsigned sglen;
struct omap_sg sg[0];
};
+enum {
+ CCR_FS = BIT(5),
+ CCR_READ_PRIORITY = BIT(6),
+ CCR_ENABLE = BIT(7),
+ CCR_AUTO_INIT = BIT(8), /* OMAP1 only */
+ CCR_REPEAT = BIT(9), /* OMAP1 only */
+ CCR_OMAP31_DISABLE = BIT(10), /* OMAP1 only */
+ CCR_SUSPEND_SENSITIVE = BIT(8), /* OMAP2+ only */
+ CCR_RD_ACTIVE = BIT(9), /* OMAP2+ only */
+ CCR_WR_ACTIVE = BIT(10), /* OMAP2+ only */
+ CCR_SRC_AMODE_CONSTANT = 0 << 12,
+ CCR_SRC_AMODE_POSTINC = 1 << 12,
+ CCR_SRC_AMODE_SGLIDX = 2 << 12,
+ CCR_SRC_AMODE_DBLIDX = 3 << 12,
+ CCR_DST_AMODE_CONSTANT = 0 << 14,
+ CCR_DST_AMODE_POSTINC = 1 << 14,
+ CCR_DST_AMODE_SGLIDX = 2 << 14,
+ CCR_DST_AMODE_DBLIDX = 3 << 14,
+ CCR_CONSTANT_FILL = BIT(16),
+ CCR_TRANSPARENT_COPY = BIT(17),
+ CCR_BS = BIT(18),
+ CCR_SUPERVISOR = BIT(22),
+ CCR_PREFETCH = BIT(23),
+ CCR_TRIGGER_SRC = BIT(24),
+ CCR_BUFFERING_DISABLE = BIT(25),
+ CCR_WRITE_PRIORITY = BIT(26),
+ CCR_SYNC_ELEMENT = 0,
+ CCR_SYNC_FRAME = CCR_FS,
+ CCR_SYNC_BLOCK = CCR_BS,
+ CCR_SYNC_PACKET = CCR_BS | CCR_FS,
+
+ CSDP_DATA_TYPE_8 = 0,
+ CSDP_DATA_TYPE_16 = 1,
+ CSDP_DATA_TYPE_32 = 2,
+ CSDP_SRC_PORT_EMIFF = 0 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_EMIFS = 1 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_OCP_T1 = 2 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_TIPB = 3 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_OCP_T2 = 4 << 2, /* OMAP1 only */
+ CSDP_SRC_PORT_MPUI = 5 << 2, /* OMAP1 only */
+ CSDP_SRC_PACKED = BIT(6),
+ CSDP_SRC_BURST_1 = 0 << 7,
+ CSDP_SRC_BURST_16 = 1 << 7,
+ CSDP_SRC_BURST_32 = 2 << 7,
+ CSDP_SRC_BURST_64 = 3 << 7,
+ CSDP_DST_PORT_EMIFF = 0 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_EMIFS = 1 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_OCP_T1 = 2 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_TIPB = 3 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_OCP_T2 = 4 << 9, /* OMAP1 only */
+ CSDP_DST_PORT_MPUI = 5 << 9, /* OMAP1 only */
+ CSDP_DST_PACKED = BIT(13),
+ CSDP_DST_BURST_1 = 0 << 14,
+ CSDP_DST_BURST_16 = 1 << 14,
+ CSDP_DST_BURST_32 = 2 << 14,
+ CSDP_DST_BURST_64 = 3 << 14,
+
+ CICR_TOUT_IE = BIT(0), /* OMAP1 only */
+ CICR_DROP_IE = BIT(1),
+ CICR_HALF_IE = BIT(2),
+ CICR_FRAME_IE = BIT(3),
+ CICR_LAST_IE = BIT(4),
+ CICR_BLOCK_IE = BIT(5),
+ CICR_PKT_IE = BIT(7), /* OMAP2+ only */
+ CICR_TRANS_ERR_IE = BIT(8), /* OMAP2+ only */
+ CICR_SUPERVISOR_ERR_IE = BIT(10), /* OMAP2+ only */
+ CICR_MISALIGNED_ERR_IE = BIT(11), /* OMAP2+ only */
+ CICR_DRAIN_IE = BIT(12), /* OMAP2+ only */
+ CICR_SUPER_BLOCK_IE = BIT(14), /* OMAP2+ only */
+
+ CLNK_CTRL_ENABLE_LNK = BIT(15),
+};
+
static const unsigned es_bytes[] = {
- [OMAP_DMA_DATA_TYPE_S8] = 1,
- [OMAP_DMA_DATA_TYPE_S16] = 2,
- [OMAP_DMA_DATA_TYPE_S32] = 4,
+ [CSDP_DATA_TYPE_8] = 1,
+ [CSDP_DATA_TYPE_16] = 2,
+ [CSDP_DATA_TYPE_32] = 4,
};
static struct of_dma_filter_info omap_dma_info = {
@@ -93,28 +178,214 @@ static void omap_dma_desc_free(struct virt_dma_desc *vd)
kfree(container_of(vd, struct omap_desc, vd));
}
+static void omap_dma_write(uint32_t val, unsigned type, void __iomem *addr)
+{
+ switch (type) {
+ case OMAP_DMA_REG_16BIT:
+ writew_relaxed(val, addr);
+ break;
+ case OMAP_DMA_REG_2X16BIT:
+ writew_relaxed(val, addr);
+ writew_relaxed(val >> 16, addr + 2);
+ break;
+ case OMAP_DMA_REG_32BIT:
+ writel_relaxed(val, addr);
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+static unsigned omap_dma_read(unsigned type, void __iomem *addr)
+{
+ unsigned val;
+
+ switch (type) {
+ case OMAP_DMA_REG_16BIT:
+ val = readw_relaxed(addr);
+ break;
+ case OMAP_DMA_REG_2X16BIT:
+ val = readw_relaxed(addr);
+ val |= readw_relaxed(addr + 2) << 16;
+ break;
+ case OMAP_DMA_REG_32BIT:
+ val = readl_relaxed(addr);
+ break;
+ default:
+ WARN_ON(1);
+ val = 0;
+ }
+
+ return val;
+}
+
+static void omap_dma_glbl_write(struct omap_dmadev *od, unsigned reg, unsigned val)
+{
+ const struct omap_dma_reg *r = od->reg_map + reg;
+
+ WARN_ON(r->stride);
+
+ omap_dma_write(val, r->type, od->base + r->offset);
+}
+
+static unsigned omap_dma_glbl_read(struct omap_dmadev *od, unsigned reg)
+{
+ const struct omap_dma_reg *r = od->reg_map + reg;
+
+ WARN_ON(r->stride);
+
+ return omap_dma_read(r->type, od->base + r->offset);
+}
+
+static void omap_dma_chan_write(struct omap_chan *c, unsigned reg, unsigned val)
+{
+ const struct omap_dma_reg *r = c->reg_map + reg;
+
+ omap_dma_write(val, r->type, c->channel_base + r->offset);
+}
+
+static unsigned omap_dma_chan_read(struct omap_chan *c, unsigned reg)
+{
+ const struct omap_dma_reg *r = c->reg_map + reg;
+
+ return omap_dma_read(r->type, c->channel_base + r->offset);
+}
+
+static void omap_dma_clear_csr(struct omap_chan *c)
+{
+ if (dma_omap1())
+ omap_dma_chan_read(c, CSR);
+ else
+ omap_dma_chan_write(c, CSR, ~0);
+}
+
+static unsigned omap_dma_get_csr(struct omap_chan *c)
+{
+ unsigned val = omap_dma_chan_read(c, CSR);
+
+ if (!dma_omap1())
+ omap_dma_chan_write(c, CSR, val);
+
+ return val;
+}
+
+static void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c,
+ unsigned lch)
+{
+ c->channel_base = od->base + od->plat->channel_stride * lch;
+
+ od->lch_map[lch] = c;
+}
+
+static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+
+ if (__dma_omap15xx(od->plat->dma_attr))
+ omap_dma_chan_write(c, CPC, 0);
+ else
+ omap_dma_chan_write(c, CDAC, 0);
+
+ omap_dma_clear_csr(c);
+
+ /* Enable interrupts */
+ omap_dma_chan_write(c, CICR, d->cicr);
+
+ /* Enable channel */
+ omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE);
+}
+
+static void omap_dma_stop(struct omap_chan *c)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ uint32_t val;
+
+ /* disable irq */
+ omap_dma_chan_write(c, CICR, 0);
+
+ omap_dma_clear_csr(c);
+
+ val = omap_dma_chan_read(c, CCR);
+ if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) {
+ uint32_t sysconfig;
+ unsigned i;
+
+ sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
+ val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK;
+ val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
+ omap_dma_glbl_write(od, OCP_SYSCONFIG, val);
+
+ val = omap_dma_chan_read(c, CCR);
+ val &= ~CCR_ENABLE;
+ omap_dma_chan_write(c, CCR, val);
+
+ /* Wait for sDMA FIFO to drain */
+ for (i = 0; ; i++) {
+ val = omap_dma_chan_read(c, CCR);
+ if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)))
+ break;
+
+ if (i > 100)
+ break;
+
+ udelay(5);
+ }
+
+ if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))
+ dev_err(c->vc.chan.device->dev,
+ "DMA drain did not complete on lch %d\n",
+ c->dma_ch);
+
+ omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig);
+ } else {
+ val &= ~CCR_ENABLE;
+ omap_dma_chan_write(c, CCR, val);
+ }
+
+ mb();
+
+ if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
+ val = omap_dma_chan_read(c, CLNK_CTRL);
+
+ if (dma_omap1())
+ val |= 1 << 14; /* set the STOP_LNK bit */
+ else
+ val &= ~CLNK_CTRL_ENABLE_LNK;
+
+ omap_dma_chan_write(c, CLNK_CTRL, val);
+ }
+}
+
static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
unsigned idx)
{
struct omap_sg *sg = d->sg + idx;
+ unsigned cxsa, cxei, cxfi;
- if (d->dir == DMA_DEV_TO_MEM)
- omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
- OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
- else
- omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
- OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+ if (d->dir == DMA_DEV_TO_MEM) {
+ cxsa = CDSA;
+ cxei = CDEI;
+ cxfi = CDFI;
+ } else {
+ cxsa = CSSA;
+ cxei = CSEI;
+ cxfi = CSFI;
+ }
- omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
- d->sync_mode, c->dma_sig, d->sync_type);
+ omap_dma_chan_write(c, cxsa, sg->addr);
+ omap_dma_chan_write(c, cxei, 0);
+ omap_dma_chan_write(c, cxfi, 0);
+ omap_dma_chan_write(c, CEN, sg->en);
+ omap_dma_chan_write(c, CFN, sg->fn);
- omap_start_dma(c->dma_ch);
+ omap_dma_start(c, d);
}
static void omap_dma_start_desc(struct omap_chan *c)
{
struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
struct omap_desc *d;
+ unsigned cxsa, cxei, cxfi;
if (!vd) {
c->desc = NULL;
@@ -126,12 +397,32 @@ static void omap_dma_start_desc(struct omap_chan *c)
c->desc = d = to_omap_dma_desc(&vd->tx);
c->sgidx = 0;
- if (d->dir == DMA_DEV_TO_MEM)
- omap_set_dma_src_params(c->dma_ch, d->periph_port,
- OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
- else
- omap_set_dma_dest_params(c->dma_ch, d->periph_port,
- OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+ /*
+ * This provides the necessary barrier to ensure data held in
+ * DMA coherent memory is visible to the DMA engine prior to
+ * the transfer starting.
+ */
+ mb();
+
+ omap_dma_chan_write(c, CCR, d->ccr);
+ if (dma_omap1())
+ omap_dma_chan_write(c, CCR2, d->ccr >> 16);
+
+ if (d->dir == DMA_DEV_TO_MEM) {
+ cxsa = CSSA;
+ cxei = CSEI;
+ cxfi = CSFI;
+ } else {
+ cxsa = CDSA;
+ cxei = CDEI;
+ cxfi = CDFI;
+ }
+
+ omap_dma_chan_write(c, cxsa, d->dev_addr);
+ omap_dma_chan_write(c, cxei, 0);
+ omap_dma_chan_write(c, cxfi, d->fi);
+ omap_dma_chan_write(c, CSDP, d->csdp);
+ omap_dma_chan_write(c, CLNK_CTRL, d->clnk_ctrl);
omap_dma_start_sg(c, d, 0);
}
@@ -186,24 +477,118 @@ static void omap_dma_sched(unsigned long data)
}
}
+static irqreturn_t omap_dma_irq(int irq, void *devid)
+{
+ struct omap_dmadev *od = devid;
+ unsigned status, channel;
+
+ spin_lock(&od->irq_lock);
+
+ status = omap_dma_glbl_read(od, IRQSTATUS_L1);
+ status &= od->irq_enable_mask;
+ if (status == 0) {
+ spin_unlock(&od->irq_lock);
+ return IRQ_NONE;
+ }
+
+ while ((channel = ffs(status)) != 0) {
+ unsigned mask, csr;
+ struct omap_chan *c;
+
+ channel -= 1;
+ mask = BIT(channel);
+ status &= ~mask;
+
+ c = od->lch_map[channel];
+ if (c == NULL) {
+ /* This should never happen */
+ dev_err(od->ddev.dev, "invalid channel %u\n", channel);
+ continue;
+ }
+
+ csr = omap_dma_get_csr(c);
+ omap_dma_glbl_write(od, IRQSTATUS_L1, mask);
+
+ omap_dma_callback(channel, csr, c);
+ }
+
+ spin_unlock(&od->irq_lock);
+
+ return IRQ_HANDLED;
+}
+
static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
+ int ret;
+
+ if (od->legacy) {
+ ret = omap_request_dma(c->dma_sig, "DMA engine",
+ omap_dma_callback, c, &c->dma_ch);
+ } else {
+ ret = omap_request_dma(c->dma_sig, "DMA engine", NULL, NULL,
+ &c->dma_ch);
+ }
+
+ dev_dbg(od->ddev.dev, "allocating channel %u for %u\n",
+ c->dma_ch, c->dma_sig);
- dev_dbg(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+ if (ret >= 0) {
+ omap_dma_assign(od, c, c->dma_ch);
- return omap_request_dma(c->dma_sig, "DMA engine",
- omap_dma_callback, c, &c->dma_ch);
+ if (!od->legacy) {
+ unsigned val;
+
+ spin_lock_irq(&od->irq_lock);
+ val = BIT(c->dma_ch);
+ omap_dma_glbl_write(od, IRQSTATUS_L1, val);
+ od->irq_enable_mask |= val;
+ omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
+
+ val = omap_dma_glbl_read(od, IRQENABLE_L0);
+ val &= ~BIT(c->dma_ch);
+ omap_dma_glbl_write(od, IRQENABLE_L0, val);
+ spin_unlock_irq(&od->irq_lock);
+ }
+ }
+
+ if (dma_omap1()) {
+ if (__dma_omap16xx(od->plat->dma_attr)) {
+ c->ccr = CCR_OMAP31_DISABLE;
+ /* Duplicate what plat-omap/dma.c does */
+ c->ccr |= c->dma_ch + 1;
+ } else {
+ c->ccr = c->dma_sig & 0x1f;
+ }
+ } else {
+ c->ccr = c->dma_sig & 0x1f;
+ c->ccr |= (c->dma_sig & ~0x1f) << 14;
+ }
+ if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
+ c->ccr |= CCR_BUFFERING_DISABLE;
+
+ return ret;
}
static void omap_dma_free_chan_resources(struct dma_chan *chan)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
+ if (!od->legacy) {
+ spin_lock_irq(&od->irq_lock);
+ od->irq_enable_mask &= ~BIT(c->dma_ch);
+ omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
+ spin_unlock_irq(&od->irq_lock);
+ }
+
+ c->channel_base = NULL;
+ od->lch_map[c->dma_ch] = NULL;
vchan_free_chan_resources(&c->vc);
omap_free_dma(c->dma_ch);
- dev_dbg(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+ dev_dbg(od->ddev.dev, "freeing channel for %u\n", c->dma_sig);
}
static size_t omap_dma_sg_size(struct omap_sg *sg)
@@ -239,6 +624,74 @@ static size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
return size;
}
+/*
+ * OMAP 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
+ * read before the DMA controller finished disabling the channel.
+ */
+static uint32_t omap_dma_chan_read_3_3(struct omap_chan *c, unsigned reg)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ uint32_t val;
+
+ val = omap_dma_chan_read(c, reg);
+ if (val == 0 && od->plat->errata & DMA_ERRATA_3_3)
+ val = omap_dma_chan_read(c, reg);
+
+ return val;
+}
+
+static dma_addr_t omap_dma_get_src_pos(struct omap_chan *c)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ dma_addr_t addr, cdac;
+
+ if (__dma_omap15xx(od->plat->dma_attr)) {
+ addr = omap_dma_chan_read(c, CPC);
+ } else {
+ addr = omap_dma_chan_read_3_3(c, CSAC);
+ cdac = omap_dma_chan_read_3_3(c, CDAC);
+
+ /*
+ * CDAC == 0 indicates that the DMA transfer on the channel has
+ * not been started (no data has been transferred so far).
+ * Return the programmed source start address in this case.
+ */
+ if (cdac == 0)
+ addr = omap_dma_chan_read(c, CSSA);
+ }
+
+ if (dma_omap1())
+ addr |= omap_dma_chan_read(c, CSSA) & 0xffff0000;
+
+ return addr;
+}
+
+static dma_addr_t omap_dma_get_dst_pos(struct omap_chan *c)
+{
+ struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
+ dma_addr_t addr;
+
+ if (__dma_omap15xx(od->plat->dma_attr)) {
+ addr = omap_dma_chan_read(c, CPC);
+ } else {
+ addr = omap_dma_chan_read_3_3(c, CDAC);
+
+ /*
+ * CDAC == 0 indicates that the DMA transfer on the channel
+ * has not been started (no data has been transferred so
+ * far). Return the programmed destination start address in
+ * this case.
+ */
+ if (addr == 0)
+ addr = omap_dma_chan_read(c, CDSA);
+ }
+
+ if (dma_omap1())
+ addr |= omap_dma_chan_read(c, CDSA) & 0xffff0000;
+
+ return addr;
+}
+
static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
@@ -260,9 +713,9 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
dma_addr_t pos;
if (d->dir == DMA_MEM_TO_DEV)
- pos = omap_get_dma_src_pos(c->dma_ch);
+ pos = omap_dma_get_src_pos(c);
else if (d->dir == DMA_DEV_TO_MEM)
- pos = omap_get_dma_dst_pos(c->dma_ch);
+ pos = omap_dma_get_dst_pos(c);
else
pos = 0;
@@ -304,24 +757,23 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
enum dma_slave_buswidth dev_width;
struct scatterlist *sgent;
struct omap_desc *d;
dma_addr_t dev_addr;
- unsigned i, j = 0, es, en, frame_bytes, sync_type;
+ unsigned i, j = 0, es, en, frame_bytes;
u32 burst;
if (dir == DMA_DEV_TO_MEM) {
dev_addr = c->cfg.src_addr;
dev_width = c->cfg.src_addr_width;
burst = c->cfg.src_maxburst;
- sync_type = OMAP_DMA_SRC_SYNC;
} else if (dir == DMA_MEM_TO_DEV) {
dev_addr = c->cfg.dst_addr;
dev_width = c->cfg.dst_addr_width;
burst = c->cfg.dst_maxburst;
- sync_type = OMAP_DMA_DST_SYNC;
} else {
dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
return NULL;
@@ -330,13 +782,13 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
/* Bus width translates to the element size (ES) */
switch (dev_width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
- es = OMAP_DMA_DATA_TYPE_S8;
+ es = CSDP_DATA_TYPE_8;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
- es = OMAP_DMA_DATA_TYPE_S16;
+ es = CSDP_DATA_TYPE_16;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
- es = OMAP_DMA_DATA_TYPE_S32;
+ es = CSDP_DATA_TYPE_32;
break;
default: /* not reached */
return NULL;
@@ -350,9 +802,31 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
d->dir = dir;
d->dev_addr = dev_addr;
d->es = es;
- d->sync_mode = OMAP_DMA_SYNC_FRAME;
- d->sync_type = sync_type;
- d->periph_port = OMAP_DMA_PORT_TIPB;
+
+ d->ccr = c->ccr | CCR_SYNC_FRAME;
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
+ else
+ d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
+
+ d->cicr = CICR_DROP_IE | CICR_BLOCK_IE;
+ d->csdp = es;
+
+ if (dma_omap1()) {
+ d->cicr |= CICR_TOUT_IE;
+
+ if (dir == DMA_DEV_TO_MEM)
+ d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_TIPB;
+ else
+ d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF;
+ } else {
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_TRIGGER_SRC;
+
+ d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
+ }
+ if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
+ d->clnk_ctrl = c->dma_ch;
/*
* Build our scatterlist entries: each contains the address,
@@ -382,23 +856,22 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
size_t period_len, enum dma_transfer_direction dir, unsigned long flags,
void *context)
{
+ struct omap_dmadev *od = to_omap_dma_dev(chan->device);
struct omap_chan *c = to_omap_dma_chan(chan);
enum dma_slave_buswidth dev_width;
struct omap_desc *d;
dma_addr_t dev_addr;
- unsigned es, sync_type;
+ unsigned es;
u32 burst;
if (dir == DMA_DEV_TO_MEM) {
dev_addr = c->cfg.src_addr;
dev_width = c->cfg.src_addr_width;
burst = c->cfg.src_maxburst;
- sync_type = OMAP_DMA_SRC_SYNC;
} else if (dir == DMA_MEM_TO_DEV) {
dev_addr = c->cfg.dst_addr;
dev_width = c->cfg.dst_addr_width;
burst = c->cfg.dst_maxburst;
- sync_type = OMAP_DMA_DST_SYNC;
} else {
dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
return NULL;
@@ -407,13 +880,13 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
/* Bus width translates to the element size (ES) */
switch (dev_width) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
- es = OMAP_DMA_DATA_TYPE_S8;
+ es = CSDP_DATA_TYPE_8;
break;
case DMA_SLAVE_BUSWIDTH_2_BYTES:
- es = OMAP_DMA_DATA_TYPE_S16;
+ es = CSDP_DATA_TYPE_16;
break;
case DMA_SLAVE_BUSWIDTH_4_BYTES:
- es = OMAP_DMA_DATA_TYPE_S32;
+ es = CSDP_DATA_TYPE_32;
break;
default: /* not reached */
return NULL;
@@ -428,32 +901,51 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
d->dev_addr = dev_addr;
d->fi = burst;
d->es = es;
- if (burst)
- d->sync_mode = OMAP_DMA_SYNC_PACKET;
- else
- d->sync_mode = OMAP_DMA_SYNC_ELEMENT;
- d->sync_type = sync_type;
- d->periph_port = OMAP_DMA_PORT_MPUI;
d->sg[0].addr = buf_addr;
d->sg[0].en = period_len / es_bytes[es];
d->sg[0].fn = buf_len / period_len;
d->sglen = 1;
- if (!c->cyclic) {
- c->cyclic = true;
- omap_dma_link_lch(c->dma_ch, c->dma_ch);
+ d->ccr = c->ccr;
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
+ else
+ d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
- if (flags & DMA_PREP_INTERRUPT)
- omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+ d->cicr = CICR_DROP_IE;
+ if (flags & DMA_PREP_INTERRUPT)
+ d->cicr |= CICR_FRAME_IE;
- omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
- }
+ d->csdp = es;
+
+ if (dma_omap1()) {
+ d->cicr |= CICR_TOUT_IE;
+
+ if (dir == DMA_DEV_TO_MEM)
+ d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_MPUI;
+ else
+ d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF;
+ } else {
+ if (burst)
+ d->ccr |= CCR_SYNC_PACKET;
+ else
+ d->ccr |= CCR_SYNC_ELEMENT;
+
+ if (dir == DMA_DEV_TO_MEM)
+ d->ccr |= CCR_TRIGGER_SRC;
+
+ d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
- if (dma_omap2plus()) {
- omap_set_dma_src_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+ d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
}
+ if (__dma_omap15xx(od->plat->dma_attr))
+ d->ccr |= CCR_AUTO_INIT | CCR_REPEAT;
+ else
+ d->clnk_ctrl = c->dma_ch | CLNK_CTRL_ENABLE_LNK;
+
+ c->cyclic = true;
+
return vchan_tx_prep(&c->vc, &d->vd, flags);
}
@@ -483,20 +975,19 @@ static int omap_dma_terminate_all(struct omap_chan *c)
/*
* Stop DMA activity: we assume the callback will not be called
- * after omap_stop_dma() returns (even if it does, it will see
+ * after omap_dma_stop() returns (even if it does, it will see
* c->desc is NULL and exit.)
*/
if (c->desc) {
c->desc = NULL;
/* Avoid stopping the dma twice */
if (!c->paused)
- omap_stop_dma(c->dma_ch);
+ omap_dma_stop(c);
}
if (c->cyclic) {
c->cyclic = false;
c->paused = false;
- omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
}
vchan_get_all_descriptors(&c->vc, &head);
@@ -513,7 +1004,7 @@ static int omap_dma_pause(struct omap_chan *c)
return -EINVAL;
if (!c->paused) {
- omap_stop_dma(c->dma_ch);
+ omap_dma_stop(c);
c->paused = true;
}
@@ -527,7 +1018,7 @@ static int omap_dma_resume(struct omap_chan *c)
return -EINVAL;
if (c->paused) {
- omap_start_dma(c->dma_ch);
+ omap_dma_start(c, c->desc);
c->paused = false;
}
@@ -573,6 +1064,7 @@ static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
if (!c)
return -ENOMEM;
+ c->reg_map = od->reg_map;
c->dma_sig = dma_sig;
c->vc.desc_free = omap_dma_desc_free;
vchan_init(&c->vc, &od->ddev);
@@ -594,18 +1086,29 @@ static void omap_dma_free(struct omap_dmadev *od)
tasklet_kill(&c->vc.task);
kfree(c);
}
- kfree(od);
}
static int omap_dma_probe(struct platform_device *pdev)
{
struct omap_dmadev *od;
- int rc, i;
+ struct resource *res;
+ int rc, i, irq;
- od = kzalloc(sizeof(*od), GFP_KERNEL);
+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
if (!od)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ od->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(od->base))
+ return PTR_ERR(od->base);
+
+ od->plat = omap_get_plat_info();
+ if (!od->plat)
+ return -EPROBE_DEFER;
+
+ od->reg_map = od->plat->reg_map;
+
dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
@@ -619,6 +1122,7 @@ static int omap_dma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&od->ddev.channels);
INIT_LIST_HEAD(&od->pending);
spin_lock_init(&od->lock);
+ spin_lock_init(&od->irq_lock);
tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
@@ -630,6 +1134,21 @@ static int omap_dma_probe(struct platform_device *pdev)
}
}
+ irq = platform_get_irq(pdev, 1);
+ if (irq <= 0) {
+ dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq);
+ od->legacy = true;
+ } else {
+ /* Disable all interrupts */
+ od->irq_enable_mask = 0;
+ omap_dma_glbl_write(od, IRQENABLE_L1, 0);
+
+ rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq,
+ IRQF_SHARED, "omap-dma-engine", od);
+ if (rc)
+ return rc;
+ }
+
rc = dma_async_device_register(&od->ddev);
if (rc) {
pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
@@ -666,6 +1185,12 @@ static int omap_dma_remove(struct platform_device *pdev)
of_dma_controller_free(pdev->dev.of_node);
dma_async_device_unregister(&od->ddev);
+
+ if (!od->legacy) {
+ /* Disable all interrupts */
+ omap_dma_glbl_write(od, IRQENABLE_L0, 0);
+ }
+
omap_dma_free(od);
return 0;
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 92d8e9a064b4..a86c49a605c6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -210,7 +210,7 @@ config GPIO_MSM_V1
config GPIO_MSM_V2
tristate "Qualcomm MSM GPIO v2"
- depends on GPIOLIB && OF && ARCH_MSM
+ depends on GPIOLIB && OF && ARCH_QCOM
help
Say yes here to support the GPIO interface on ARM v7 based
Qualcomm MSM chips. Most of the pins on the MSM can be
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index bfef20f8ab48..e73c6755a5eb 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -1,5 +1,5 @@
/*
- * Intel ICH6-10, Series 5 and 6 GPIO driver
+ * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
*
* Copyright (C) 2010 Extreme Engineering Solutions.
*
@@ -55,6 +55,16 @@ static const u8 ichx_reglen[3] = {
0x30, 0x10, 0x10,
};
+static const u8 avoton_regs[4][3] = {
+ {0x00, 0x80, 0x00},
+ {0x04, 0x84, 0x00},
+ {0x08, 0x88, 0x00},
+};
+
+static const u8 avoton_reglen[3] = {
+ 0x10, 0x10, 0x00,
+};
+
#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)
@@ -353,6 +363,17 @@ static struct ichx_desc intel5_desc = {
.reglen = ichx_reglen,
};
+/* Avoton */
+static struct ichx_desc avoton_desc = {
+ /* Avoton has only 59 GPIOs, but we assume the first set of register
+ * (Core) has 32 instead of 31 to keep gpio-ich compliance
+ */
+ .ngpio = 60,
+ .regs = avoton_regs,
+ .reglen = avoton_reglen,
+ .use_outlvl_cache = true,
+};
+
static int ichx_gpio_request_regions(struct resource *res_base,
const char *name, u8 use_gpio)
{
@@ -427,6 +448,9 @@ static int ichx_gpio_probe(struct platform_device *pdev)
case ICH_V10CONS_GPIO:
ichx_priv.desc = &ich10_cons_desc;
break;
+ case AVOTON_GPIO:
+ ichx_priv.desc = &avoton_desc;
+ break;
default:
return -ENODEV;
}
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 8e7fa4dbaed8..d1cc2f613a78 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -199,3 +199,5 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig"
+
+source "drivers/gpu/drm/bridge/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 292a79d64146..9d25dbbe6771 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -13,7 +13,8 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_crtc.o drm_modes.o drm_edid.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
drm_trace_points.o drm_global.o drm_prime.o \
- drm_rect.o drm_vma_manager.o drm_flip_work.o
+ drm_rect.o drm_vma_manager.o drm_flip_work.o \
+ drm_plane_helper.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
@@ -63,3 +64,4 @@ obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-y += i2c/
obj-y += panel/
+obj-y += bridge/
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index d8e398275ca8..81c34f949dfc 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -478,11 +478,12 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
unsigned i;
bool interlaced;
- drm_framebuffer_reference(crtc->fb);
+ drm_framebuffer_reference(crtc->primary->fb);
interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
- i = armada_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced);
+ i = armada_drm_crtc_calc_fb(dcrtc->crtc.primary->fb,
+ x, y, regs, interlaced);
rm = adj->crtc_hsync_start - adj->crtc_hdisplay;
lm = adj->crtc_htotal - adj->crtc_hsync_end;
@@ -567,10 +568,10 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
}
val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
- val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
- val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
+ val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt);
+ val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->mod);
- if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
+ if (drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt > CFG_420)
val |= CFG_PALETTE_ENA;
if (interlaced)
@@ -608,7 +609,7 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct armada_regs regs[4];
unsigned i;
- i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
+ i = armada_drm_crtc_calc_fb(crtc->primary->fb, crtc->x, crtc->y, regs,
dcrtc->interlaced);
armada_reg_queue_end(regs, i);
@@ -616,7 +617,7 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
/* Take a reference to the new fb as we're using it */
- drm_framebuffer_reference(crtc->fb);
+ drm_framebuffer_reference(crtc->primary->fb);
/* Update the base in the CRTC */
armada_drm_crtc_update_regs(dcrtc, regs);
@@ -637,7 +638,7 @@ static void armada_drm_crtc_disable(struct drm_crtc *crtc)
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
- armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
+ armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true);
/* Power down most RAMs and FIFOs */
writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
@@ -678,6 +679,7 @@ static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
base + LCD_SPU_SRAM_WRDAT);
writel_relaxed(addr | SRAM_WRITE,
base + LCD_SPU_SRAM_CTRL);
+ readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN);
addr += 1;
if ((addr & 0x00ff) == 0)
addr += 0xf00;
@@ -904,7 +906,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
int ret;
/* We don't support changing the pixel format */
- if (fb->pixel_format != crtc->fb->pixel_format)
+ if (fb->pixel_format != crtc->primary->fb->pixel_format)
return -EINVAL;
work = kmalloc(sizeof(*work), GFP_KERNEL);
@@ -912,7 +914,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
return -ENOMEM;
work->event = event;
- work->old_fb = dcrtc->crtc.fb;
+ work->old_fb = dcrtc->crtc.primary->fb;
i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
dcrtc->interlaced);
@@ -941,7 +943,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
* will _not_ drop that reference on successful return from this
* function. Simply mark this new framebuffer as the current one.
*/
- dcrtc->crtc.fb = fb;
+ dcrtc->crtc.primary->fb = fb;
/*
* Finally, if the display is blanked, we won't receive an
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index cca063b11083..a4afdc8bb578 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -81,7 +81,7 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate;
u32 hborder, vborder;
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
vbios_mode->std_table = &vbios_stdtable[VGAModeIndex];
color_index = VGAModeIndex - 1;
@@ -176,7 +176,7 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->fb->bits_per_pixel);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
@@ -340,7 +340,7 @@ static void ast_set_offset_reg(struct drm_crtc *crtc)
u16 offset;
- offset = crtc->fb->pitches[0] >> 3;
+ offset = crtc->primary->fb->pitches[0] >> 3;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x13, (offset & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xb0, (offset >> 8) & 0x3f);
}
@@ -365,7 +365,7 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode
struct ast_private *ast = crtc->dev->dev_private;
u8 jregA0 = 0, jregA3 = 0, jregA8 = 0;
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
jregA0 = 0x70;
jregA3 = 0x01;
@@ -418,7 +418,7 @@ static void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mo
static bool ast_set_dac_reg(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
break;
default:
@@ -490,7 +490,7 @@ static int ast_crtc_do_set_base(struct drm_crtc *crtc,
ast_bo_unreserve(bo);
}
- ast_fb = to_ast_framebuffer(crtc->fb);
+ ast_fb = to_ast_framebuffer(crtc->primary->fb);
obj = ast_fb->obj;
bo = gem_to_ast_bo(obj);
diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c
index 4ea9b17ac17a..b8246227bab0 100644
--- a/drivers/gpu/drm/ast/ast_ttm.c
+++ b/drivers/gpu/drm/ast/ast_ttm.c
@@ -259,7 +259,9 @@ int ast_mm_init(struct ast_private *ast)
ret = ttm_bo_device_init(&ast->ttm.bdev,
ast->ttm.bo_global_ref.ref.object,
- &ast_bo_driver, DRM_FILE_PAGE_OFFSET,
+ &ast_bo_driver,
+ dev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
true);
if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret);
@@ -324,7 +326,6 @@ int ast_bo_create(struct drm_device *dev, int size, int align,
}
astbo->bo.bdev = &ast->ttm.bdev;
- astbo->bo.bdev->dev_mapping = dev->dev_mapping;
ast_ttm_placement(astbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
index 62ec7d4b3816..dcf2e55f4ae9 100644
--- a/drivers/gpu/drm/bochs/bochs_kms.c
+++ b/drivers/gpu/drm/bochs/bochs_kms.c
@@ -62,10 +62,10 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
}
}
- if (WARN_ON(crtc->fb == NULL))
+ if (WARN_ON(crtc->primary->fb == NULL))
return -EINVAL;
- bochs_fb = to_bochs_framebuffer(crtc->fb);
+ bochs_fb = to_bochs_framebuffer(crtc->primary->fb);
bo = gem_to_bochs_bo(bochs_fb->obj);
ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
if (ret)
diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c
index ce6858765b37..f488be55d650 100644
--- a/drivers/gpu/drm/bochs/bochs_mm.c
+++ b/drivers/gpu/drm/bochs/bochs_mm.c
@@ -225,7 +225,9 @@ int bochs_mm_init(struct bochs_device *bochs)
ret = ttm_bo_device_init(&bochs->ttm.bdev,
bochs->ttm.bo_global_ref.ref.object,
- &bochs_bo_driver, DRM_FILE_PAGE_OFFSET,
+ &bochs_bo_driver,
+ bochs->dev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
true);
if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret);
@@ -359,7 +361,7 @@ static int bochs_bo_create(struct drm_device *dev, int size, int align,
}
bochsbo->bo.bdev = &bochs->ttm.bdev;
- bochsbo->bo.bdev->dev_mapping = dev->dev_mapping;
+ bochsbo->bo.bdev->dev_mapping = dev->anon_inode->i_mapping;
bochs_ttm_placement(bochsbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
new file mode 100644
index 000000000000..884923f982d9
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -0,0 +1,5 @@
+config DRM_PTN3460
+ tristate "PTN3460 DP/LVDS bridge"
+ depends on DRM
+ select DRM_KMS_HELPER
+ ---help---
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
new file mode 100644
index 000000000000..b4733e1fbd2e
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -Iinclude/drm
+
+obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
new file mode 100644
index 000000000000..b171901a3553
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -0,0 +1,350 @@
+/*
+ * NXP PTN3460 DP/LVDS bridge driver
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "drmP.h"
+#include "drm_edid.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#include "bridge/ptn3460.h"
+
+#define PTN3460_EDID_ADDR 0x0
+#define PTN3460_EDID_EMULATION_ADDR 0x84
+#define PTN3460_EDID_ENABLE_EMULATION 0
+#define PTN3460_EDID_EMULATION_SELECTION 1
+#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
+
+struct ptn3460_bridge {
+ struct drm_connector connector;
+ struct i2c_client *client;
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+ struct edid *edid;
+ int gpio_pd_n;
+ int gpio_rst_n;
+ u32 edid_emulation;
+ bool enabled;
+};
+
+static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
+ u8 *buf, int len)
+{
+ int ret;
+
+ ret = i2c_master_send(ptn_bridge->client, &addr, 1);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_master_recv(ptn_bridge->client, buf, len);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
+ char val)
+{
+ int ret;
+ char buf[2];
+
+ buf[0] = addr;
+ buf[1] = val;
+
+ ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
+{
+ int ret;
+ char val;
+
+ /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
+ ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Enable EDID emulation and select the desired EDID */
+ val = 1 << PTN3460_EDID_ENABLE_EMULATION |
+ ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
+
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
+ if (ret) {
+ DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ptn3460_pre_enable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+ int ret;
+
+ if (ptn_bridge->enabled)
+ return;
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ gpio_set_value(ptn_bridge->gpio_rst_n, 0);
+ udelay(10);
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+ }
+
+ /*
+ * There's a bug in the PTN chip where it falsely asserts hotplug before
+ * it is fully functional. We're forced to wait for the maximum start up
+ * time specified in the chip's datasheet to make sure we're really up.
+ */
+ msleep(90);
+
+ ret = ptn3460_select_edid(ptn_bridge);
+ if (ret)
+ DRM_ERROR("Select edid failed ret=%d\n", ret);
+
+ ptn_bridge->enabled = true;
+}
+
+static void ptn3460_enable(struct drm_bridge *bridge)
+{
+}
+
+static void ptn3460_disable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ if (!ptn_bridge->enabled)
+ return;
+
+ ptn_bridge->enabled = false;
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 0);
+}
+
+static void ptn3460_post_disable(struct drm_bridge *bridge)
+{
+}
+
+void ptn3460_bridge_destroy(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ drm_bridge_cleanup(bridge);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ /* Nothing else to free, we've got devm allocated memory */
+}
+
+struct drm_bridge_funcs ptn3460_bridge_funcs = {
+ .pre_enable = ptn3460_pre_enable,
+ .enable = ptn3460_enable,
+ .disable = ptn3460_disable,
+ .post_disable = ptn3460_post_disable,
+ .destroy = ptn3460_bridge_destroy,
+};
+
+int ptn3460_get_modes(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+ u8 *edid;
+ int ret, num_modes;
+ bool power_off;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ if (ptn_bridge->edid)
+ return drm_add_edid_modes(connector, ptn_bridge->edid);
+
+ power_off = !ptn_bridge->enabled;
+ ptn3460_pre_enable(ptn_bridge->bridge);
+
+ edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!edid) {
+ DRM_ERROR("Failed to allocate edid\n");
+ return 0;
+ }
+
+ ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
+ EDID_LENGTH);
+ if (ret) {
+ kfree(edid);
+ num_modes = 0;
+ goto out;
+ }
+
+ ptn_bridge->edid = (struct edid *)edid;
+ drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
+
+ num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
+
+out:
+ if (power_off)
+ ptn3460_disable(ptn_bridge->bridge);
+
+ return num_modes;
+}
+
+static int ptn3460_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ return ptn_bridge->encoder;
+}
+
+struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
+ .get_modes = ptn3460_get_modes,
+ .mode_valid = ptn3460_mode_valid,
+ .best_encoder = ptn3460_best_encoder,
+};
+
+enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
+ bool force)
+{
+ return connector_status_connected;
+}
+
+void ptn3460_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs ptn3460_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = ptn3460_detect,
+ .destroy = ptn3460_connector_destroy,
+};
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node)
+{
+ int ret;
+ struct drm_bridge *bridge;
+ struct ptn3460_bridge *ptn_bridge;
+
+ bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ DRM_ERROR("Failed to allocate drm bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
+ if (!ptn_bridge) {
+ DRM_ERROR("Failed to allocate ptn bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge->client = client;
+ ptn_bridge->encoder = encoder;
+ ptn_bridge->bridge = bridge;
+ ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
+ ret = gpio_request_one(ptn_bridge->gpio_pd_n,
+ GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
+ if (ret) {
+ DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ /*
+ * Request the reset pin low to avoid the bridge being
+ * initialized prematurely
+ */
+ ret = gpio_request_one(ptn_bridge->gpio_rst_n,
+ GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
+ if (ret) {
+ DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
+ gpio_free(ptn_bridge->gpio_pd_n);
+ return ret;
+ }
+ }
+
+ ret = of_property_read_u32(node, "edid-emulation",
+ &ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Can't read edid emulation value\n");
+ goto err;
+ }
+
+ ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
+ if (ret) {
+ DRM_ERROR("Failed to initialize bridge with drm\n");
+ goto err;
+ }
+
+ bridge->driver_private = ptn_bridge;
+ encoder->bridge = bridge;
+
+ ret = drm_connector_init(dev, &ptn_bridge->connector,
+ &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ goto err;
+ }
+ drm_connector_helper_add(&ptn_bridge->connector,
+ &ptn3460_connector_helper_funcs);
+ drm_sysfs_connector_add(&ptn_bridge->connector);
+ drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
+
+ return 0;
+
+err:
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ return ret;
+}
+EXPORT_SYMBOL(ptn3460_init);
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index 530f78f84dee..2d64aea83df2 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -149,7 +149,7 @@ static int cirrus_crtc_do_set_base(struct drm_crtc *crtc,
cirrus_bo_unreserve(bo);
}
- cirrus_fb = to_cirrus_framebuffer(crtc->fb);
+ cirrus_fb = to_cirrus_framebuffer(crtc->primary->fb);
obj = cirrus_fb->obj;
bo = gem_to_cirrus_bo(obj);
@@ -268,7 +268,7 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
sr07 = RREG8(SEQ_DATA);
sr07 &= 0xe0;
hdr = 0;
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
sr07 |= 0x11;
break;
@@ -291,13 +291,13 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
WREG_SEQ(0x7, sr07);
/* Program the pitch */
- tmp = crtc->fb->pitches[0] / 8;
+ tmp = crtc->primary->fb->pitches[0] / 8;
WREG_CRT(VGA_CRTC_OFFSET, tmp);
/* Enable extended blanking and pitch bits, and enable full memory */
tmp = 0x22;
- tmp |= (crtc->fb->pitches[0] >> 7) & 0x10;
- tmp |= (crtc->fb->pitches[0] >> 6) & 0x40;
+ tmp |= (crtc->primary->fb->pitches[0] >> 7) & 0x10;
+ tmp |= (crtc->primary->fb->pitches[0] >> 6) & 0x40;
WREG_CRT(0x1b, tmp);
/* Enable high-colour modes */
diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c
index 8b37c25ff9bd..92e6b7786097 100644
--- a/drivers/gpu/drm/cirrus/cirrus_ttm.c
+++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c
@@ -259,7 +259,9 @@ int cirrus_mm_init(struct cirrus_device *cirrus)
ret = ttm_bo_device_init(&cirrus->ttm.bdev,
cirrus->ttm.bo_global_ref.ref.object,
- &cirrus_bo_driver, DRM_FILE_PAGE_OFFSET,
+ &cirrus_bo_driver,
+ dev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
true);
if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret);
@@ -329,7 +331,6 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align,
}
cirrusbo->bo.bdev = &cirrus->ttm.bdev;
- cirrusbo->bo.bdev->dev_mapping = dev->dev_mapping;
cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 3b7d32da1604..d8b7099abece 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -38,12 +38,15 @@
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
+#include "drm_crtc_internal.h"
+
/**
* drm_modeset_lock_all - take all modeset locks
* @dev: drm device
*
* This function takes all modeset locks, suitable where a more fine-grained
- * scheme isn't (yet) implemented.
+ * scheme isn't (yet) implemented. Locks must be dropped with
+ * drm_modeset_unlock_all.
*/
void drm_modeset_lock_all(struct drm_device *dev)
{
@@ -59,6 +62,8 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
/**
* drm_modeset_unlock_all - drop all modeset locks
* @dev: device
+ *
+ * This function drop all modeset locks taken by drm_modeset_lock_all.
*/
void drm_modeset_unlock_all(struct drm_device *dev)
{
@@ -74,6 +79,8 @@ EXPORT_SYMBOL(drm_modeset_unlock_all);
/**
* drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
* @dev: device
+ *
+ * Useful as a debug assert.
*/
void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
{
@@ -114,6 +121,13 @@ static const struct drm_prop_enum_list drm_dpms_enum_list[] =
DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
+static const struct drm_prop_enum_list drm_plane_type_enum_list[] =
+{
+ { DRM_PLANE_TYPE_OVERLAY, "Overlay" },
+ { DRM_PLANE_TYPE_PRIMARY, "Primary" },
+ { DRM_PLANE_TYPE_CURSOR, "Cursor" },
+};
+
/*
* Optional properties
*/
@@ -215,6 +229,16 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] =
{ DRM_MODE_ENCODER_DSI, "DSI" },
};
+static const struct drm_prop_enum_list drm_subpixel_enum_list[] =
+{
+ { SubPixelUnknown, "Unknown" },
+ { SubPixelHorizontalRGB, "Horizontal RGB" },
+ { SubPixelHorizontalBGR, "Horizontal BGR" },
+ { SubPixelVerticalRGB, "Vertical RGB" },
+ { SubPixelVerticalBGR, "Vertical BGR" },
+ { SubPixelNone, "None" },
+};
+
void drm_connector_ida_init(void)
{
int i;
@@ -231,6 +255,15 @@ void drm_connector_ida_destroy(void)
ida_destroy(&drm_connector_enum_list[i].ida);
}
+/**
+ * drm_get_encoder_name - return a string for encoder
+ * @encoder: encoder to compute name of
+ *
+ * Note that the buffer used by this function is globally shared and owned by
+ * the function itself.
+ *
+ * FIXME: This isn't really multithreading safe.
+ */
const char *drm_get_encoder_name(const struct drm_encoder *encoder)
{
static char buf[32];
@@ -242,6 +275,15 @@ const char *drm_get_encoder_name(const struct drm_encoder *encoder)
}
EXPORT_SYMBOL(drm_get_encoder_name);
+/**
+ * drm_get_connector_name - return a string for connector
+ * @connector: connector to compute name of
+ *
+ * Note that the buffer used by this function is globally shared and owned by
+ * the function itself.
+ *
+ * FIXME: This isn't really multithreading safe.
+ */
const char *drm_get_connector_name(const struct drm_connector *connector)
{
static char buf[32];
@@ -253,6 +295,13 @@ const char *drm_get_connector_name(const struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_get_connector_name);
+/**
+ * drm_get_connector_status_name - return a string for connector status
+ * @status: connector status to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
const char *drm_get_connector_status_name(enum drm_connector_status status)
{
if (status == connector_status_connected)
@@ -264,11 +313,33 @@ const char *drm_get_connector_status_name(enum drm_connector_status status)
}
EXPORT_SYMBOL(drm_get_connector_status_name);
+/**
+ * drm_get_subpixel_order_name - return a string for a given subpixel enum
+ * @order: enum of subpixel_order
+ *
+ * Note you could abuse this and return something out of bounds, but that
+ * would be a caller error. No unscrubbed user data should make it here.
+ */
+const char *drm_get_subpixel_order_name(enum subpixel_order order)
+{
+ return drm_subpixel_enum_list[order].name;
+}
+EXPORT_SYMBOL(drm_get_subpixel_order_name);
+
static char printable_char(int c)
{
return isascii(c) && isprint(c) ? c : '?';
}
+/**
+ * drm_get_format_name - return a string for drm fourcc format
+ * @format: format to compute name of
+ *
+ * Note that the buffer used by this function is globally shared and owned by
+ * the function itself.
+ *
+ * FIXME: This isn't really multithreading safe.
+ */
const char *drm_get_format_name(uint32_t format)
{
static char buf[32];
@@ -293,14 +364,16 @@ EXPORT_SYMBOL(drm_get_format_name);
* @obj_type: object type
*
* Create a unique identifier based on @ptr in @dev's identifier space. Used
- * for tracking modes, CRTCs and connectors.
+ * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
+ * modeset identifiers are _not_ reference counted. Hence don't use this for
+ * reference counted modeset objects like framebuffers.
*
- * RETURNS:
+ * Returns:
* New unique (relative to other objects in @dev) integer identifier for the
* object.
*/
-static int drm_mode_object_get(struct drm_device *dev,
- struct drm_mode_object *obj, uint32_t obj_type)
+int drm_mode_object_get(struct drm_device *dev,
+ struct drm_mode_object *obj, uint32_t obj_type)
{
int ret;
@@ -324,10 +397,12 @@ static int drm_mode_object_get(struct drm_device *dev,
* @dev: DRM device
* @object: object to free
*
- * Free @id from @dev's unique identifier pool.
+ * Free @id from @dev's unique identifier pool. Note that despite the _get
+ * postfix modeset identifiers are _not_ reference counted. Hence don't use this
+ * for reference counted modeset objects like framebuffers.
*/
-static void drm_mode_object_put(struct drm_device *dev,
- struct drm_mode_object *object)
+void drm_mode_object_put(struct drm_device *dev,
+ struct drm_mode_object *object)
{
mutex_lock(&dev->mode_config.idr_mutex);
idr_remove(&dev->mode_config.crtc_idr, object->id);
@@ -377,7 +452,7 @@ EXPORT_SYMBOL(drm_mode_object_find);
* since all the fb attributes are invariant over its lifetime, no further
* locking but only correct reference counting is required.
*
- * RETURNS:
+ * Returns:
* Zero on success, error code on failure.
*/
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
@@ -438,7 +513,7 @@ static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev,
*
* If successful, this grabs an additional reference to the framebuffer -
* callers need to make sure to eventually unreference the returned framebuffer
- * again.
+ * again, using @drm_framebuffer_unreference.
*/
struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
uint32_t id)
@@ -471,6 +546,8 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
/**
* drm_framebuffer_reference - incr the fb refcnt
* @fb: framebuffer
+ *
+ * This functions increments the fb's refcount.
*/
void drm_framebuffer_reference(struct drm_framebuffer *fb)
{
@@ -527,8 +604,9 @@ EXPORT_SYMBOL(drm_framebuffer_unregister_private);
* drm_framebuffer_cleanup - remove a framebuffer object
* @fb: framebuffer to remove
*
- * Cleanup references to a user-created framebuffer. This function is intended
- * to be used from the drivers ->destroy callback.
+ * Cleanup framebuffer. This function is intended to be used from the drivers
+ * ->destroy callback. It can also be used to clean up driver private
+ * framebuffers embedded into a larger structure.
*
* Note that this function does not remove the fb from active usuage - if it is
* still used anywhere, hilarity can ensue since userspace could call getfb on
@@ -591,7 +669,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
drm_modeset_lock_all(dev);
/* remove from any CRTC */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (crtc->fb == fb) {
+ if (crtc->primary->fb == fb) {
/* should turn off the crtc */
memset(&set, 0, sizeof(struct drm_mode_set));
set.crtc = crtc;
@@ -614,18 +692,23 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
EXPORT_SYMBOL(drm_framebuffer_remove);
/**
- * drm_crtc_init - Initialise a new CRTC object
+ * drm_crtc_init_with_planes - Initialise a new CRTC object with
+ * specified primary and cursor planes.
* @dev: DRM device
* @crtc: CRTC object to init
+ * @primary: Primary plane for CRTC
+ * @cursor: Cursor plane for CRTC
* @funcs: callbacks for the new CRTC
*
* Inits a new object created as base part of a driver crtc object.
*
- * RETURNS:
+ * Returns:
* Zero on success, error code on failure.
*/
-int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
- const struct drm_crtc_funcs *funcs)
+int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
+ struct drm_plane *primary,
+ void *cursor,
+ const struct drm_crtc_funcs *funcs)
{
int ret;
@@ -646,12 +729,16 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
dev->mode_config.num_crtc++;
+ crtc->primary = primary;
+ if (primary)
+ primary->possible_crtcs = 1 << drm_crtc_index(crtc);
+
out:
drm_modeset_unlock_all(dev);
return ret;
}
-EXPORT_SYMBOL(drm_crtc_init);
+EXPORT_SYMBOL(drm_crtc_init_with_planes);
/**
* drm_crtc_cleanup - Clean up the core crtc usage
@@ -697,20 +784,6 @@ unsigned int drm_crtc_index(struct drm_crtc *crtc)
}
EXPORT_SYMBOL(drm_crtc_index);
-/**
- * drm_mode_probed_add - add a mode to a connector's probed mode list
- * @connector: connector the new mode
- * @mode: mode data
- *
- * Add @mode to @connector's mode list for later use.
- */
-void drm_mode_probed_add(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- list_add_tail(&mode->head, &connector->probed_modes);
-}
-EXPORT_SYMBOL(drm_mode_probed_add);
-
/*
* drm_mode_remove - remove and free a mode
* @connector: connector list to modify
@@ -735,7 +808,7 @@ static void drm_mode_remove(struct drm_connector *connector,
* Initialises a preallocated connector. Connectors should be
* subclassed as part of driver connector objects.
*
- * RETURNS:
+ * Returns:
* Zero on success, error code on failure.
*/
int drm_connector_init(struct drm_device *dev,
@@ -813,6 +886,14 @@ void drm_connector_cleanup(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_connector_cleanup);
+/**
+ * drm_connector_unplug_all - unregister connector userspace interfaces
+ * @dev: drm device
+ *
+ * This function unregisters all connector userspace interfaces in sysfs. Should
+ * be call when the device is disconnected, e.g. from an usb driver's
+ * ->disconnect callback.
+ */
void drm_connector_unplug_all(struct drm_device *dev)
{
struct drm_connector *connector;
@@ -824,6 +905,18 @@ void drm_connector_unplug_all(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_connector_unplug_all);
+/**
+ * drm_bridge_init - initialize a drm transcoder/bridge
+ * @dev: drm device
+ * @bridge: transcoder/bridge to set up
+ * @funcs: bridge function table
+ *
+ * Initialises a preallocated bridge. Bridges should be
+ * subclassed as part of driver connector objects.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
const struct drm_bridge_funcs *funcs)
{
@@ -847,6 +940,12 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
}
EXPORT_SYMBOL(drm_bridge_init);
+/**
+ * drm_bridge_cleanup - cleans up an initialised bridge
+ * @bridge: bridge to cleanup
+ *
+ * Cleans up the bridge but doesn't free the object.
+ */
void drm_bridge_cleanup(struct drm_bridge *bridge)
{
struct drm_device *dev = bridge->dev;
@@ -859,6 +958,19 @@ void drm_bridge_cleanup(struct drm_bridge *bridge)
}
EXPORT_SYMBOL(drm_bridge_cleanup);
+/**
+ * drm_encoder_init - Init a preallocated encoder
+ * @dev: drm device
+ * @encoder: the encoder to init
+ * @funcs: callbacks for this encoder
+ * @encoder_type: user visible type of the encoder
+ *
+ * Initialises a preallocated encoder. Encoder should be
+ * subclassed as part of driver encoder objects.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
@@ -886,6 +998,12 @@ int drm_encoder_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_encoder_init);
+/**
+ * drm_encoder_cleanup - cleans up an initialised encoder
+ * @encoder: encoder to cleanup
+ *
+ * Cleans up the encoder but doesn't free the object.
+ */
void drm_encoder_cleanup(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
@@ -898,25 +1016,25 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
EXPORT_SYMBOL(drm_encoder_cleanup);
/**
- * drm_plane_init - Initialise a new plane object
+ * drm_universal_plane_init - Initialize a new universal plane object
* @dev: DRM device
* @plane: plane object to init
* @possible_crtcs: bitmask of possible CRTCs
* @funcs: callbacks for the new plane
* @formats: array of supported formats (%DRM_FORMAT_*)
* @format_count: number of elements in @formats
- * @priv: plane is private (hidden from userspace)?
+ * @type: type of plane (overlay, primary, cursor)
*
- * Inits a new object created as base part of a driver plane object.
+ * Initializes a plane object of type @type.
*
- * RETURNS:
+ * Returns:
* Zero on success, error code on failure.
*/
-int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
- unsigned long possible_crtcs,
- const struct drm_plane_funcs *funcs,
- const uint32_t *formats, uint32_t format_count,
- bool priv)
+int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
+ unsigned long possible_crtcs,
+ const struct drm_plane_funcs *funcs,
+ const uint32_t *formats, uint32_t format_count,
+ enum drm_plane_type type)
{
int ret;
@@ -941,23 +1059,53 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
plane->format_count = format_count;
plane->possible_crtcs = possible_crtcs;
+ plane->type = type;
- /* private planes are not exposed to userspace, but depending on
- * display hardware, might be convenient to allow sharing programming
- * for the scanout engine with the crtc implementation.
- */
- if (!priv) {
- list_add_tail(&plane->head, &dev->mode_config.plane_list);
- dev->mode_config.num_plane++;
- } else {
- INIT_LIST_HEAD(&plane->head);
- }
+ list_add_tail(&plane->head, &dev->mode_config.plane_list);
+ dev->mode_config.num_total_plane++;
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ dev->mode_config.num_overlay_plane++;
+
+ drm_object_attach_property(&plane->base,
+ dev->mode_config.plane_type_property,
+ plane->type);
out:
drm_modeset_unlock_all(dev);
return ret;
}
+EXPORT_SYMBOL(drm_universal_plane_init);
+
+/**
+ * drm_plane_init - Initialize a legacy plane
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @format_count: number of elements in @formats
+ * @is_primary: plane type (primary vs overlay)
+ *
+ * Legacy API to initialize a DRM plane.
+ *
+ * New drivers should call drm_universal_plane_init() instead.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
+ unsigned long possible_crtcs,
+ const struct drm_plane_funcs *funcs,
+ const uint32_t *formats, uint32_t format_count,
+ bool is_primary)
+{
+ enum drm_plane_type type;
+
+ type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+ return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+ formats, format_count, type);
+}
EXPORT_SYMBOL(drm_plane_init);
/**
@@ -975,11 +1123,13 @@ void drm_plane_cleanup(struct drm_plane *plane)
drm_modeset_lock_all(dev);
kfree(plane->format_types);
drm_mode_object_put(dev, &plane->base);
- /* if not added to a list, it must be a private plane */
- if (!list_empty(&plane->head)) {
- list_del(&plane->head);
- dev->mode_config.num_plane--;
- }
+
+ BUG_ON(list_empty(&plane->head));
+
+ list_del(&plane->head);
+ dev->mode_config.num_total_plane--;
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ dev->mode_config.num_overlay_plane--;
drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_plane_cleanup);
@@ -1010,50 +1160,6 @@ void drm_plane_force_disable(struct drm_plane *plane)
}
EXPORT_SYMBOL(drm_plane_force_disable);
-/**
- * drm_mode_create - create a new display mode
- * @dev: DRM device
- *
- * Create a new drm_display_mode, give it an ID, and return it.
- *
- * RETURNS:
- * Pointer to new mode on success, NULL on error.
- */
-struct drm_display_mode *drm_mode_create(struct drm_device *dev)
-{
- struct drm_display_mode *nmode;
-
- nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
- if (!nmode)
- return NULL;
-
- if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
- kfree(nmode);
- return NULL;
- }
-
- return nmode;
-}
-EXPORT_SYMBOL(drm_mode_create);
-
-/**
- * drm_mode_destroy - remove a mode
- * @dev: DRM device
- * @mode: mode to remove
- *
- * Free @mode's unique identifier, then free it.
- */
-void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
-{
- if (!mode)
- return;
-
- drm_mode_object_put(dev, &mode->base);
-
- kfree(mode);
-}
-EXPORT_SYMBOL(drm_mode_destroy);
-
static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
{
struct drm_property *edid;
@@ -1075,6 +1181,21 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
return 0;
}
+static int drm_mode_create_standard_plane_properties(struct drm_device *dev)
+{
+ struct drm_property *type;
+
+ /*
+ * Standard properties (apply to all planes)
+ */
+ type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+ "type", drm_plane_type_enum_list,
+ ARRAY_SIZE(drm_plane_type_enum_list));
+ dev->mode_config.plane_type_property = type;
+
+ return 0;
+}
+
/**
* drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
* @dev: DRM device
@@ -1257,6 +1378,10 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
return 0;
}
+/*
+ * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is
+ * the drm core's responsibility to set up mode control groups.
+ */
int drm_mode_group_init_legacy_group(struct drm_device *dev,
struct drm_mode_group *group)
{
@@ -1333,7 +1458,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
* the caller.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
static int drm_crtc_convert_umode(struct drm_display_mode *out,
@@ -1376,7 +1501,7 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out,
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_getresources(struct drm_device *dev, void *data,
@@ -1429,9 +1554,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
mutex_unlock(&file_priv->fbs_lock);
drm_modeset_lock_all(dev);
- mode_group = &file_priv->master->minor->mode_group;
- if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ if (!drm_is_primary_client(file_priv)) {
+ mode_group = NULL;
list_for_each(lh, &dev->mode_config.crtc_list)
crtc_count++;
@@ -1442,6 +1567,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
encoder_count++;
} else {
+ mode_group = &file_priv->master->minor->mode_group;
crtc_count = mode_group->num_crtcs;
connector_count = mode_group->num_connectors;
encoder_count = mode_group->num_encoders;
@@ -1456,7 +1582,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
if (card_res->count_crtcs >= crtc_count) {
copied = 0;
crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
- if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ if (!mode_group) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list,
head) {
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
@@ -1483,7 +1609,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
if (card_res->count_encoders >= encoder_count) {
copied = 0;
encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
- if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ if (!mode_group) {
list_for_each_entry(encoder,
&dev->mode_config.encoder_list,
head) {
@@ -1514,7 +1640,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
if (card_res->count_connectors >= connector_count) {
copied = 0;
connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
- if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ if (!mode_group) {
list_for_each_entry(connector,
&dev->mode_config.connector_list,
head) {
@@ -1561,7 +1687,7 @@ out:
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_getcrtc(struct drm_device *dev,
@@ -1588,8 +1714,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
crtc_resp->x = crtc->x;
crtc_resp->y = crtc->y;
crtc_resp->gamma_size = crtc->gamma_size;
- if (crtc->fb)
- crtc_resp->fb_id = crtc->fb->base.id;
+ if (crtc->primary->fb)
+ crtc_resp->fb_id = crtc->primary->fb->base.id;
else
crtc_resp->fb_id = 0;
@@ -1630,7 +1756,7 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_getconnector(struct drm_device *dev, void *data,
@@ -1765,6 +1891,19 @@ out:
return ret;
}
+/**
+ * drm_mode_getencoder - get encoder configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Construct a encoder configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_getencoder(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -1800,21 +1939,27 @@ out:
}
/**
- * drm_mode_getplane_res - get plane info
+ * drm_mode_getplane_res - enumerate all plane resources
* @dev: DRM device
* @data: ioctl data
* @file_priv: DRM file info
*
- * Return an plane count and set of IDs.
+ * Construct a list of plane ids to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
*/
int drm_mode_getplane_res(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file_priv)
{
struct drm_mode_get_plane_res *plane_resp = data;
struct drm_mode_config *config;
struct drm_plane *plane;
uint32_t __user *plane_ptr;
int copied = 0, ret = 0;
+ unsigned num_planes;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
@@ -1822,15 +1967,28 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
drm_modeset_lock_all(dev);
config = &dev->mode_config;
+ if (file_priv->universal_planes)
+ num_planes = config->num_total_plane;
+ else
+ num_planes = config->num_overlay_plane;
+
/*
* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it.
*/
- if (config->num_plane &&
- (plane_resp->count_planes >= config->num_plane)) {
+ if (num_planes &&
+ (plane_resp->count_planes >= num_planes)) {
plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
list_for_each_entry(plane, &config->plane_list, head) {
+ /*
+ * Unless userspace set the 'universal planes'
+ * capability bit, only advertise overlays.
+ */
+ if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
+ !file_priv->universal_planes)
+ continue;
+
if (put_user(plane->base.id, plane_ptr + copied)) {
ret = -EFAULT;
goto out;
@@ -1838,7 +1996,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
copied++;
}
}
- plane_resp->count_planes = config->num_plane;
+ plane_resp->count_planes = num_planes;
out:
drm_modeset_unlock_all(dev);
@@ -1846,16 +2004,20 @@ out:
}
/**
- * drm_mode_getplane - get plane info
+ * drm_mode_getplane - get plane configuration
* @dev: DRM device
* @data: ioctl data
* @file_priv: DRM file info
*
- * Return plane info, including formats supported, gamma size, any
- * current fb, etc.
+ * Construct a plane configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
*/
int drm_mode_getplane(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file_priv)
{
struct drm_mode_get_plane *plane_resp = data;
struct drm_mode_object *obj;
@@ -1911,16 +2073,19 @@ out:
}
/**
- * drm_mode_setplane - set up or tear down an plane
+ * drm_mode_setplane - configure a plane's configuration
* @dev: DRM device
* @data: ioctl data*
* @file_priv: DRM file info
*
- * Set plane info, including placement, fb, scaling, and other factors.
+ * Set plane configuration, including placement, fb, scaling, and other factors.
* Or pass a NULL fb to disable.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
*/
int drm_mode_setplane(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ struct drm_file *file_priv)
{
struct drm_mode_set_plane *plane_req = data;
struct drm_mode_object *obj;
@@ -2050,6 +2215,9 @@ out:
*
* This is a little helper to wrap internal calls to the ->set_config driver
* interface. The only thing it adds is correct refcounting dance.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
*/
int drm_mode_set_config_internal(struct drm_mode_set *set)
{
@@ -2064,19 +2232,21 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
* crtcs. Atomic modeset will have saner semantics ...
*/
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
- tmp->old_fb = tmp->fb;
+ tmp->old_fb = tmp->primary->fb;
fb = set->fb;
ret = crtc->funcs->set_config(set);
if (ret == 0) {
+ crtc->primary->crtc = crtc;
+
/* crtc->fb must be updated by ->set_config, enforces this. */
- WARN_ON(fb != crtc->fb);
+ WARN_ON(fb != crtc->primary->fb);
}
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
- if (tmp->fb)
- drm_framebuffer_reference(tmp->fb);
+ if (tmp->primary->fb)
+ drm_framebuffer_reference(tmp->primary->fb);
if (tmp->old_fb)
drm_framebuffer_unreference(tmp->old_fb);
}
@@ -2085,14 +2255,19 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
}
EXPORT_SYMBOL(drm_mode_set_config_internal);
-/*
- * Checks that the framebuffer is big enough for the CRTC viewport
- * (x, y, hdisplay, vdisplay)
+/**
+ * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
+ * CRTC viewport
+ * @crtc: CRTC that framebuffer will be displayed on
+ * @x: x panning
+ * @y: y panning
+ * @mode: mode that framebuffer will be displayed under
+ * @fb: framebuffer to check size of
*/
-static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
- int x, int y,
- const struct drm_display_mode *mode,
- const struct drm_framebuffer *fb)
+int drm_crtc_check_viewport(const struct drm_crtc *crtc,
+ int x, int y,
+ const struct drm_display_mode *mode,
+ const struct drm_framebuffer *fb)
{
int hdisplay, vdisplay;
@@ -2123,6 +2298,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
return 0;
}
+EXPORT_SYMBOL(drm_crtc_check_viewport);
/**
* drm_mode_setcrtc - set CRTC configuration
@@ -2134,7 +2310,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_setcrtc(struct drm_device *dev, void *data,
@@ -2174,12 +2350,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
if (crtc_req->fb_id == -1) {
- if (!crtc->fb) {
+ if (!crtc->primary->fb) {
DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
ret = -EINVAL;
goto out;
}
- fb = crtc->fb;
+ fb = crtc->primary->fb;
/* Make refcounting symmetric with the lookup path. */
drm_framebuffer_reference(fb);
} else {
@@ -2336,8 +2512,23 @@ out:
return ret;
}
+
+
+/**
+ * drm_mode_cursor_ioctl - set CRTC's cursor configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Set the cursor configuration based on user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_cursor_ioctl(struct drm_device *dev,
- void *data, struct drm_file *file_priv)
+ void *data, struct drm_file *file_priv)
{
struct drm_mode_cursor *req = data;
struct drm_mode_cursor2 new_req;
@@ -2348,6 +2539,21 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
return drm_mode_cursor_common(dev, &new_req, file_priv);
}
+/**
+ * drm_mode_cursor2_ioctl - set CRTC's cursor configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Set the cursor configuration based on user request. This implements the 2nd
+ * version of the cursor ioctl, which allows userspace to additionally specify
+ * the hotspot of the pointer.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_cursor2_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -2355,7 +2561,14 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev,
return drm_mode_cursor_common(dev, req, file_priv);
}
-/* Original addfb only supported RGB formats, so figure out which one */
+/**
+ * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
+ * @bpp: bits per pixels
+ * @depth: bit depth per pixel
+ *
+ * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
+ * Useful in fbdev emulation code, since that deals in those values.
+ */
uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
{
uint32_t fmt;
@@ -2397,11 +2610,12 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format);
* @data: data pointer for the ioctl
* @file_priv: drm file for the ioctl call
*
- * Add a new FB to the specified CRTC, given a user request.
+ * Add a new FB to the specified CRTC, given a user request. This is the
+ * original addfb ioclt which only supported RGB formats.
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_addfb(struct drm_device *dev,
@@ -2574,11 +2788,13 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
* @data: data pointer for the ioctl
* @file_priv: drm file for the ioctl call
*
- * Add a new FB to the specified CRTC, given a user request with format.
+ * Add a new FB to the specified CRTC, given a user request with format. This is
+ * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
+ * and uses fourcc codes as pixel format specifiers.
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_addfb2(struct drm_device *dev,
@@ -2638,7 +2854,7 @@ int drm_mode_addfb2(struct drm_device *dev,
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_rmfb(struct drm_device *dev,
@@ -2692,7 +2908,7 @@ fail_lookup:
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
int drm_mode_getfb(struct drm_device *dev,
@@ -2715,7 +2931,8 @@ int drm_mode_getfb(struct drm_device *dev,
r->bpp = fb->bits_per_pixel;
r->pitch = fb->pitches[0];
if (fb->funcs->create_handle) {
- if (file_priv->is_master || capable(CAP_SYS_ADMIN)) {
+ if (file_priv->is_master || capable(CAP_SYS_ADMIN) ||
+ drm_is_control_client(file_priv)) {
ret = fb->funcs->create_handle(fb, file_priv,
&r->handle);
} else {
@@ -2736,6 +2953,25 @@ int drm_mode_getfb(struct drm_device *dev,
return ret;
}
+/**
+ * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Lookup the FB and flush out the damaged area supplied by userspace as a clip
+ * rectangle list. Generic userspace which does frontbuffer rendering must call
+ * this ioctl to flush out the changes on manual-update display outputs, e.g.
+ * usb display-link, mipi manual update panels or edp panel self refresh modes.
+ *
+ * Modesetting drivers which always update the frontbuffer do not need to
+ * implement the corresponding ->dirty framebuffer callback.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -2813,7 +3049,7 @@ out_err1:
*
* Called by the user via ioctl.
*
- * RETURNS:
+ * Returns:
* Zero on success, errno on failure.
*/
void drm_fb_release(struct drm_file *priv)
@@ -2837,6 +3073,20 @@ void drm_fb_release(struct drm_file *priv)
mutex_unlock(&priv->fbs_lock);
}
+/**
+ * drm_property_create - create a new property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
struct drm_property *drm_property_create(struct drm_device *dev, int flags,
const char *name, int num_values)
{
@@ -2875,6 +3125,24 @@ fail:
}
EXPORT_SYMBOL(drm_property_create);
+/**
+ * drm_property_create - create a new enumeration property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @props: enumeration lists with property values
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy.
+ *
+ * Userspace is only allowed to set one of the predefined values for enumeration
+ * properties.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
const char *name,
const struct drm_prop_enum_list *props,
@@ -2903,6 +3171,24 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
}
EXPORT_SYMBOL(drm_property_create_enum);
+/**
+ * drm_property_create - create a new bitmask property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @props: enumeration lists with property bitflags
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy.
+ *
+ * Compared to plain enumeration properties userspace is allowed to set any
+ * or'ed together combination of the predefined property bitflag values
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
int flags, const char *name,
const struct drm_prop_enum_list *props,
@@ -2931,6 +3217,24 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_property_create_bitmask);
+/**
+ * drm_property_create - create a new ranged property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @min: minimum value of the property
+ * @max: maximum value of the property
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy.
+ *
+ * Userspace is allowed to set any interger value in the (min, max) range
+ * inclusive.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
const char *name,
uint64_t min, uint64_t max)
@@ -2950,6 +3254,21 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
}
EXPORT_SYMBOL(drm_property_create_range);
+/**
+ * drm_property_add_enum - add a possible value to an enumeration property
+ * @property: enumeration property to change
+ * @index: index of the new enumeration
+ * @value: value of the new enumeration
+ * @name: symbolic name of the new enumeration
+ *
+ * This functions adds enumerations to a property.
+ *
+ * It's use is deprecated, drivers should use one of the more specific helpers
+ * to directly create the property with all enumerations already attached.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
int drm_property_add_enum(struct drm_property *property, int index,
uint64_t value, const char *name)
{
@@ -2989,6 +3308,14 @@ int drm_property_add_enum(struct drm_property *property, int index,
}
EXPORT_SYMBOL(drm_property_add_enum);
+/**
+ * drm_property_destroy - destroy a drm property
+ * @dev: drm device
+ * @property: property to destry
+ *
+ * This function frees a property including any attached resources like
+ * enumeration values.
+ */
void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
{
struct drm_property_enum *prop_enum, *pt;
@@ -3006,6 +3333,16 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
}
EXPORT_SYMBOL(drm_property_destroy);
+/**
+ * drm_object_attach_property - attach a property to a modeset object
+ * @obj: drm modeset object
+ * @property: property to attach
+ * @init_val: initial value of the property
+ *
+ * This attaches the given property to the modeset object with the given initial
+ * value. Currently this function cannot fail since the properties are stored in
+ * a statically sized array.
+ */
void drm_object_attach_property(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t init_val)
@@ -3026,6 +3363,19 @@ void drm_object_attach_property(struct drm_mode_object *obj,
}
EXPORT_SYMBOL(drm_object_attach_property);
+/**
+ * drm_object_property_set_value - set the value of a property
+ * @obj: drm mode object to set property value for
+ * @property: property to set
+ * @val: value the property should be set to
+ *
+ * This functions sets a given property on a given object. This function only
+ * changes the software state of the property, it does not call into the
+ * driver's ->set_property callback.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
int drm_object_property_set_value(struct drm_mode_object *obj,
struct drm_property *property, uint64_t val)
{
@@ -3042,6 +3392,20 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
}
EXPORT_SYMBOL(drm_object_property_set_value);
+/**
+ * drm_object_property_get_value - retrieve the value of a property
+ * @obj: drm mode object to get property value from
+ * @property: property to retrieve
+ * @val: storage for the property value
+ *
+ * This function retrieves the softare state of the given property for the given
+ * property. Since there is no driver callback to retrieve the current property
+ * value this might be out of sync with the hardware, depending upon the driver
+ * and property.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
int drm_object_property_get_value(struct drm_mode_object *obj,
struct drm_property *property, uint64_t *val)
{
@@ -3058,6 +3422,19 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
}
EXPORT_SYMBOL(drm_object_property_get_value);
+/**
+ * drm_mode_getproperty_ioctl - get the current value of a connector's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the current value for an connectors's property.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_getproperty_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3196,6 +3573,20 @@ static void drm_property_destroy_blob(struct drm_device *dev,
kfree(blob);
}
+/**
+ * drm_mode_getblob_ioctl - get the contents of a blob property value
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the contents of a blob property. The value stored in
+ * an object's blob property is just a normal modeset object id.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_getblob_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3230,6 +3621,17 @@ done:
return ret;
}
+/**
+ * drm_mode_connector_update_edid_property - update the edid property of a connector
+ * @connector: drm connector
+ * @edid: new value of the edid property
+ *
+ * This function creates a new blob modeset object and assigns its id to the
+ * connector's edid property.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_connector_update_edid_property(struct drm_connector *connector,
struct edid *edid)
{
@@ -3287,6 +3689,20 @@ static bool drm_property_change_is_valid(struct drm_property *property,
}
}
+/**
+ * drm_mode_connector_property_set_ioctl - set the current value of a connector property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function sets the current value for a connectors's property. It also
+ * calls into a driver's ->set_property callback to update the hardware state
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3353,6 +3769,21 @@ static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
return ret;
}
+/**
+ * drm_mode_getproperty_ioctl - get the current value of a object's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the current value for an object's property. Compared
+ * to the connector specific ioctl this one is extended to also work on crtc and
+ * plane objects.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -3409,6 +3840,22 @@ out:
return ret;
}
+/**
+ * drm_mode_obj_set_property_ioctl - set the current value of an object's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function sets the current value for an object's property. It also calls
+ * into a driver's ->set_property callback to update the hardware state.
+ * Compared to the connector specific ioctl this one is extended to also work on
+ * crtc and plane objects.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -3468,6 +3915,18 @@ out:
return ret;
}
+/**
+ * drm_mode_connector_attach_encoder - attach a connector to an encoder
+ * @connector: connector to attach
+ * @encoder: encoder to attach @connector to
+ *
+ * This function links up a connector to an encoder. Note that the routing
+ * restrictions between encoders and crtcs are exposed to userspace through the
+ * possible_clones and possible_crtcs bitmasks.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_connector_attach_encoder(struct drm_connector *connector,
struct drm_encoder *encoder)
{
@@ -3483,23 +3942,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
-void drm_mode_connector_detach_encoder(struct drm_connector *connector,
- struct drm_encoder *encoder)
-{
- int i;
- for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
- if (connector->encoder_ids[i] == encoder->base.id) {
- connector->encoder_ids[i] = 0;
- if (connector->encoder == encoder)
- connector->encoder = NULL;
- break;
- }
- }
-}
-EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
-
+/**
+ * drm_mode_crtc_set_gamma_size - set the gamma table size
+ * @crtc: CRTC to set the gamma table size for
+ * @gamma_size: size of the gamma table
+ *
+ * Drivers which support gamma tables should set this to the supported gamma
+ * table size when initializing the CRTC. Currently the drm core only supports a
+ * fixed gamma table size.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
- int gamma_size)
+ int gamma_size)
{
crtc->gamma_size = gamma_size;
@@ -3513,6 +3969,20 @@ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
}
EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+/**
+ * drm_mode_gamma_set_ioctl - set the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
+ * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_gamma_set_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3572,6 +4042,21 @@ out:
}
+/**
+ * drm_mode_gamma_get_ioctl - get the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Copy the current gamma table into the storage provided. This also provides
+ * the gamma table size the driver expects, which can be used to size the
+ * allocated storage.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_gamma_get_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3622,6 +4107,24 @@ out:
return ret;
}
+/**
+ * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This schedules an asynchronous update on a given CRTC, called page flip.
+ * Optionally a drm event is generated to signal the completion of the event.
+ * Generic drivers cannot assume that a pageflip with changed framebuffer
+ * properties (including driver specific metadata like tiling layout) will work,
+ * but some drivers support e.g. pixel format changes through the pageflip
+ * ioctl.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_page_flip_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3646,7 +4149,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
crtc = obj_to_crtc(obj);
mutex_lock(&crtc->mutex);
- if (crtc->fb == NULL) {
+ if (crtc->primary->fb == NULL) {
/* The framebuffer is currently unbound, presumably
* due to a hotplug event, that userspace has not
* yet discovered.
@@ -3668,7 +4171,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
if (ret)
goto out;
- if (crtc->fb->pixel_format != fb->pixel_format) {
+ if (crtc->primary->fb->pixel_format != fb->pixel_format) {
DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
ret = -EINVAL;
goto out;
@@ -3701,7 +4204,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
(void (*) (struct drm_pending_event *)) kfree;
}
- old_fb = crtc->fb;
+ old_fb = crtc->primary->fb;
ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
if (ret) {
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
@@ -3719,7 +4222,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
- WARN_ON(crtc->fb != fb);
+ WARN_ON(crtc->primary->fb != fb);
/* Unref only the old framebuffer. */
fb = NULL;
}
@@ -3734,6 +4237,14 @@ out:
return ret;
}
+/**
+ * drm_mode_config_reset - call ->reset callbacks
+ * @dev: drm device
+ *
+ * This functions calls all the crtc's, encoder's and connector's ->reset
+ * callback. Drivers can use this in e.g. their driver load or resume code to
+ * reset hardware and software state.
+ */
void drm_mode_config_reset(struct drm_device *dev)
{
struct drm_crtc *crtc;
@@ -3757,16 +4268,66 @@ void drm_mode_config_reset(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_mode_config_reset);
+/**
+ * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This creates a new dumb buffer in the driver's backing storage manager (GEM,
+ * TTM or something else entirely) and returns the resulting buffer handle. This
+ * handle can then be wrapped up into a framebuffer modeset object.
+ *
+ * Note that userspace is not allowed to use such objects for render
+ * acceleration - drivers must create their own private ioctls for such a use
+ * case.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_create_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_create_dumb *args = data;
+ u32 cpp, stride, size;
if (!dev->driver->dumb_create)
return -ENOSYS;
+ if (!args->width || !args->height || !args->bpp)
+ return -EINVAL;
+
+ /* overflow checks for 32bit size calculations */
+ cpp = DIV_ROUND_UP(args->bpp, 8);
+ if (cpp > 0xffffffffU / args->width)
+ return -EINVAL;
+ stride = cpp * args->width;
+ if (args->height > 0xffffffffU / stride)
+ return -EINVAL;
+
+ /* test for wrap-around */
+ size = args->height * stride;
+ if (PAGE_ALIGN(size) == 0)
+ return -EINVAL;
+
return dev->driver->dumb_create(file_priv, dev, args);
}
+/**
+ * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Allocate an offset in the drm device node's address space to be able to
+ * memory map a dumb buffer.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3779,6 +4340,21 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset);
}
+/**
+ * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This destroys the userspace handle for the given dumb backing storage buffer.
+ * Since buffer objects must be reference counted in the kernel a buffer object
+ * won't be immediately freed if a framebuffer modeset object still uses it.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -3790,9 +4366,14 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
return dev->driver->dumb_destroy(file_priv, dev, args->handle);
}
-/*
- * Just need to support RGB formats here for compat with code that doesn't
- * use pixel formats directly yet.
+/**
+ * drm_fb_get_bpp_depth - get the bpp/depth values for format
+ * @format: pixel format (DRM_FORMAT_*)
+ * @depth: storage for the depth value
+ * @bpp: storage for the bpp value
+ *
+ * This only supports RGB formats here for compat with code that doesn't use
+ * pixel formats directly yet.
*/
void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
int *bpp)
@@ -3864,7 +4445,7 @@ EXPORT_SYMBOL(drm_fb_get_bpp_depth);
* drm_format_num_planes - get the number of planes for format
* @format: pixel format (DRM_FORMAT_*)
*
- * RETURNS:
+ * Returns:
* The number of planes used by the specified pixel format.
*/
int drm_format_num_planes(uint32_t format)
@@ -3899,7 +4480,7 @@ EXPORT_SYMBOL(drm_format_num_planes);
* @format: pixel format (DRM_FORMAT_*)
* @plane: plane index
*
- * RETURNS:
+ * Returns:
* The bytes per pixel value for the specified plane.
*/
int drm_format_plane_cpp(uint32_t format, int plane)
@@ -3945,7 +4526,7 @@ EXPORT_SYMBOL(drm_format_plane_cpp);
* drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor
* @format: pixel format (DRM_FORMAT_*)
*
- * RETURNS:
+ * Returns:
* The horizontal chroma subsampling factor for the
* specified pixel format.
*/
@@ -3980,7 +4561,7 @@ EXPORT_SYMBOL(drm_format_horz_chroma_subsampling);
* drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor
* @format: pixel format (DRM_FORMAT_*)
*
- * RETURNS:
+ * Returns:
* The vertical chroma subsampling factor for the
* specified pixel format.
*/
@@ -4030,6 +4611,7 @@ void drm_mode_config_init(struct drm_device *dev)
drm_modeset_lock_all(dev);
drm_mode_create_standard_connector_properties(dev);
+ drm_mode_create_standard_plane_properties(dev);
drm_modeset_unlock_all(dev);
/* Just to be sure */
@@ -4037,6 +4619,8 @@ void drm_mode_config_init(struct drm_device *dev)
dev->mode_config.num_connector = 0;
dev->mode_config.num_crtc = 0;
dev->mode_config.num_encoder = 0;
+ dev->mode_config.num_overlay_plane = 0;
+ dev->mode_config.num_total_plane = 0;
}
EXPORT_SYMBOL(drm_mode_config_init);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index f7a81209beb3..c43825e8f5c1 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -105,9 +105,6 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
* @maxX: max width for modes
* @maxY: max height for modes
*
- * LOCKING:
- * Caller must hold mode config lock.
- *
* Based on the helper callbacks implemented by @connector try to detect all
* valid modes. Modes will first be added to the connector's probed_modes list,
* then culled (based on validity and the @maxX, @maxY parameters) and put into
@@ -117,8 +114,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
* @connector vfunc for drivers that use the crtc helpers for output mode
* filtering and detection.
*
- * RETURNS:
- * Number of modes found on @connector.
+ * Returns:
+ * The number of modes found on @connector.
*/
int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY)
@@ -131,6 +128,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
int mode_flags = 0;
bool verbose_prune = true;
+ WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
drm_get_connector_name(connector));
/* set all modes to the unverified state */
@@ -176,8 +175,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
drm_mode_connector_list_update(connector);
if (maxX && maxY)
- drm_mode_validate_size(dev, &connector->modes, maxX,
- maxY, 0);
+ drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
if (connector->interlace_allowed)
mode_flags |= DRM_MODE_FLAG_INTERLACE;
@@ -219,18 +217,19 @@ EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
* drm_helper_encoder_in_use - check if a given encoder is in use
* @encoder: encoder to check
*
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * Walk @encoders's DRM device's mode_config and see if it's in use.
+ * Checks whether @encoder is with the current mode setting output configuration
+ * in use by any connector. This doesn't mean that it is actually enabled since
+ * the DPMS state is tracked separately.
*
- * RETURNS:
- * True if @encoder is part of the mode_config, false otherwise.
+ * Returns:
+ * True if @encoder is used, false otherwise.
*/
bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
{
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
+
+ WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder)
return true;
@@ -242,19 +241,19 @@ EXPORT_SYMBOL(drm_helper_encoder_in_use);
* drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
* @crtc: CRTC to check
*
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * Walk @crtc's DRM device's mode_config and see if it's in use.
+ * Checks whether @crtc is with the current mode setting output configuration
+ * in use by any connector. This doesn't mean that it is actually enabled since
+ * the DPMS state is tracked separately.
*
- * RETURNS:
- * True if @crtc is part of the mode_config, false otherwise.
+ * Returns:
+ * True if @crtc is used, false otherwise.
*/
bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
{
struct drm_encoder *encoder;
struct drm_device *dev = crtc->dev;
- /* FIXME: Locking around list access? */
+
+ WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
return true;
@@ -279,27 +278,17 @@ drm_encoder_disable(struct drm_encoder *encoder)
encoder->bridge->funcs->post_disable(encoder->bridge);
}
-/**
- * drm_helper_disable_unused_functions - disable unused objects
- * @dev: DRM device
- *
- * LOCKING:
- * Caller must hold mode config lock.
- *
- * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled
- * by calling its dpms function, which should power it off.
- */
-void drm_helper_disable_unused_functions(struct drm_device *dev)
+static void __drm_helper_disable_unused_functions(struct drm_device *dev)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
struct drm_crtc *crtc;
+ drm_warn_on_modeset_not_all_locked(dev);
+
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder)
continue;
- if (connector->status == connector_status_disconnected)
- connector->encoder = NULL;
}
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
@@ -318,10 +307,27 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
(*crtc_funcs->disable)(crtc);
else
(*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF);
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
}
}
}
+
+/**
+ * drm_helper_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * This function walks through the entire mode setting configuration of @dev. It
+ * will remove any crtc links of unused encoders and encoder links of
+ * disconnected connectors. Then it will disable all unused encoders and crtcs
+ * either by calling their disable callback if available or by calling their
+ * dpms callback with DRM_MODE_DPMS_OFF.
+ */
+void drm_helper_disable_unused_functions(struct drm_device *dev)
+{
+ drm_modeset_lock_all(dev);
+ __drm_helper_disable_unused_functions(dev);
+ drm_modeset_unlock_all(dev);
+}
EXPORT_SYMBOL(drm_helper_disable_unused_functions);
/*
@@ -355,9 +361,6 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
* @y: vertical offset into the surface
* @old_fb: old framebuffer, for cleanup
*
- * LOCKING:
- * Caller must hold mode config lock.
- *
* Try to set @mode on @crtc. Give @crtc and its associated connectors a chance
* to fixup or reject the mode prior to trying to set it. This is an internal
* helper that drivers could e.g. use to update properties that require the
@@ -367,8 +370,8 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
* drm_crtc_helper_set_config() helper function to drive the mode setting
* sequence.
*
- * RETURNS:
- * True if the mode was set successfully, or false otherwise.
+ * Returns:
+ * True if the mode was set successfully, false otherwise.
*/
bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
struct drm_display_mode *mode,
@@ -384,6 +387,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
struct drm_encoder *encoder;
bool ret = true;
+ drm_warn_on_modeset_not_all_locked(dev);
+
saved_enabled = crtc->enabled;
crtc->enabled = drm_helper_crtc_in_use(crtc);
if (!crtc->enabled)
@@ -552,7 +557,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
}
}
- drm_helper_disable_unused_functions(dev);
+ __drm_helper_disable_unused_functions(dev);
return 0;
}
@@ -560,17 +565,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
* drm_crtc_helper_set_config - set a new config from userspace
* @set: mode set configuration
*
- * LOCKING:
- * Caller must hold mode config lock.
- *
* Setup a new configuration, provided by the upper layers (either an ioctl call
* from userspace or internally e.g. from the fbdev support code) in @set, and
* enable it. This is the main helper functions for drivers that implement
* kernel mode setting with the crtc helper functions and the assorted
* ->prepare(), ->modeset() and ->commit() helper callbacks.
*
- * RETURNS:
- * Returns 0 on success, -ERRNO on failure.
+ * Returns:
+ * Returns 0 on success, negative errno numbers on failure.
*/
int drm_crtc_helper_set_config(struct drm_mode_set *set)
{
@@ -612,6 +614,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
dev = set->crtc->dev;
+ drm_warn_on_modeset_not_all_locked(dev);
+
/*
* Allocate space for the backup of all (non-pointer) encoder and
* connector data.
@@ -647,19 +651,19 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
save_set.mode = &set->crtc->mode;
save_set.x = set->crtc->x;
save_set.y = set->crtc->y;
- save_set.fb = set->crtc->fb;
+ save_set.fb = set->crtc->primary->fb;
/* We should be able to check here if the fb has the same properties
* and then just flip_or_move it */
- if (set->crtc->fb != set->fb) {
+ if (set->crtc->primary->fb != set->fb) {
/* If we have no fb then treat it as a full mode set */
- if (set->crtc->fb == NULL) {
+ if (set->crtc->primary->fb == NULL) {
DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
mode_changed = true;
} else if (set->fb == NULL) {
mode_changed = true;
} else if (set->fb->pixel_format !=
- set->crtc->fb->pixel_format) {
+ set->crtc->primary->fb->pixel_format) {
mode_changed = true;
} else
fb_changed = true;
@@ -689,12 +693,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
if (new_encoder == NULL)
/* don't break so fail path works correct */
fail = 1;
- break;
if (connector->dpms != DRM_MODE_DPMS_ON) {
DRM_DEBUG_KMS("connector dpms not on, full mode switch\n");
mode_changed = true;
}
+
+ break;
}
}
@@ -760,13 +765,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
DRM_DEBUG_KMS("attempting to set mode from"
" userspace\n");
drm_mode_debug_printmodeline(set->mode);
- set->crtc->fb = set->fb;
+ set->crtc->primary->fb = set->fb;
if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
set->x, set->y,
save_set.fb)) {
DRM_ERROR("failed to set mode on [CRTC:%d]\n",
set->crtc->base.id);
- set->crtc->fb = save_set.fb;
+ set->crtc->primary->fb = save_set.fb;
ret = -EINVAL;
goto fail;
}
@@ -777,17 +782,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
}
}
- drm_helper_disable_unused_functions(dev);
+ __drm_helper_disable_unused_functions(dev);
} else if (fb_changed) {
set->crtc->x = set->x;
set->crtc->y = set->y;
- set->crtc->fb = set->fb;
+ set->crtc->primary->fb = set->fb;
ret = crtc_funcs->mode_set_base(set->crtc,
set->x, set->y, save_set.fb);
if (ret != 0) {
set->crtc->x = save_set.x;
set->crtc->y = save_set.y;
- set->crtc->fb = save_set.fb;
+ set->crtc->primary->fb = save_set.fb;
goto fail;
}
}
@@ -924,8 +929,16 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
}
EXPORT_SYMBOL(drm_helper_connector_dpms);
-int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
- struct drm_mode_fb_cmd2 *mode_cmd)
+/**
+ * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata
+ * @fb: drm_framebuffer object to fill out
+ * @mode_cmd: metadata from the userspace fb creation request
+ *
+ * This helper can be used in a drivers fb_create callback to pre-fill the fb's
+ * metadata fields.
+ */
+void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+ struct drm_mode_fb_cmd2 *mode_cmd)
{
int i;
@@ -938,26 +951,47 @@ int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
&fb->bits_per_pixel);
fb->pixel_format = mode_cmd->pixel_format;
-
- return 0;
}
EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
-int drm_helper_resume_force_mode(struct drm_device *dev)
+/**
+ * drm_helper_resume_force_mode - force-restore mode setting configuration
+ * @dev: drm_device which should be restored
+ *
+ * Drivers which use the mode setting helpers can use this function to
+ * force-restore the mode setting configuration e.g. on resume or when something
+ * else might have trampled over the hw state (like some overzealous old BIOSen
+ * tended to do).
+ *
+ * This helper doesn't provide a error return value since restoring the old
+ * config should never fail due to resource allocation issues since the driver
+ * has successfully set the restored configuration already. Hence this should
+ * boil down to the equivalent of a few dpms on calls, which also don't provide
+ * an error code.
+ *
+ * Drivers where simply restoring an old configuration again might fail (e.g.
+ * due to slight differences in allocating shared resources when the
+ * configuration is restored in a different order than when userspace set it up)
+ * need to use their own restore logic.
+ */
+void drm_helper_resume_force_mode(struct drm_device *dev)
{
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_crtc_helper_funcs *crtc_funcs;
- int ret, encoder_dpms;
+ int encoder_dpms;
+ bool ret;
+ drm_modeset_lock_all(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (!crtc->enabled)
continue;
ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
- crtc->x, crtc->y, crtc->fb);
+ crtc->x, crtc->y, crtc->primary->fb);
+ /* Restoring the old config should never fail! */
if (ret == false)
DRM_ERROR("failed to set mode on crtc %p\n", crtc);
@@ -980,12 +1014,29 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
drm_helper_choose_crtc_dpms(crtc));
}
}
+
/* disable the unused connectors while restoring the modesetting */
- drm_helper_disable_unused_functions(dev);
- return 0;
+ __drm_helper_disable_unused_functions(dev);
+ drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_helper_resume_force_mode);
+/**
+ * drm_kms_helper_hotplug_event - fire off KMS hotplug events
+ * @dev: drm_device whose connector state changed
+ *
+ * This function fires off the uevent for userspace and also calls the
+ * output_poll_changed function, which is most commonly used to inform the fbdev
+ * emulation code and allow it to update the fbcon output configuration.
+ *
+ * Drivers should call this from their hotplug handling code when a change is
+ * detected. Note that this function does not do any output detection of its
+ * own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the
+ * driver already.
+ *
+ * This function must be called from process context with no mode
+ * setting locks held.
+ */
void drm_kms_helper_hotplug_event(struct drm_device *dev)
{
/* send a uevent + call fbdev */
@@ -1054,6 +1105,16 @@ static void output_poll_execute(struct work_struct *work)
schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
}
+/**
+ * drm_kms_helper_poll_disable - disable output polling
+ * @dev: drm_device
+ *
+ * This function disables the output polling work.
+ *
+ * Drivers can call this helper from their device suspend implementation. It is
+ * not an error to call this even when output polling isn't enabled or arlready
+ * disabled.
+ */
void drm_kms_helper_poll_disable(struct drm_device *dev)
{
if (!dev->mode_config.poll_enabled)
@@ -1062,6 +1123,16 @@ void drm_kms_helper_poll_disable(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_kms_helper_poll_disable);
+/**
+ * drm_kms_helper_poll_enable - re-enable output polling.
+ * @dev: drm_device
+ *
+ * This function re-enables the output polling work.
+ *
+ * Drivers can call this helper from their device resume implementation. It is
+ * an error to call this when the output polling support has not yet been set
+ * up.
+ */
void drm_kms_helper_poll_enable(struct drm_device *dev)
{
bool poll = false;
@@ -1081,6 +1152,25 @@ void drm_kms_helper_poll_enable(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_kms_helper_poll_enable);
+/**
+ * drm_kms_helper_poll_init - initialize and enable output polling
+ * @dev: drm_device
+ *
+ * This function intializes and then also enables output polling support for
+ * @dev. Drivers which do not have reliable hotplug support in hardware can use
+ * this helper infrastructure to regularly poll such connectors for changes in
+ * their connection state.
+ *
+ * Drivers can control which connectors are polled by setting the
+ * DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On
+ * connectors where probing live outputs can result in visual distortion drivers
+ * should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this.
+ * Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are
+ * completely ignored by the polling logic.
+ *
+ * Note that a connector can be both polled and probed from the hotplug handler,
+ * in case the hotplug interrupt is known to be unreliable.
+ */
void drm_kms_helper_poll_init(struct drm_device *dev)
{
INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
@@ -1090,12 +1180,39 @@ void drm_kms_helper_poll_init(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_kms_helper_poll_init);
+/**
+ * drm_kms_helper_poll_fini - disable output polling and clean it up
+ * @dev: drm_device
+ */
void drm_kms_helper_poll_fini(struct drm_device *dev)
{
drm_kms_helper_poll_disable(dev);
}
EXPORT_SYMBOL(drm_kms_helper_poll_fini);
+/**
+ * drm_helper_hpd_irq_event - hotplug processing
+ * @dev: drm_device
+ *
+ * Drivers can use this helper function to run a detect cycle on all connectors
+ * which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All
+ * other connectors are ignored, which is useful to avoid reprobing fixed
+ * panels.
+ *
+ * This helper function is useful for drivers which can't or don't track hotplug
+ * interrupts for each connector.
+ *
+ * Drivers which support hotplug interrupts for each connector individually and
+ * which have a more fine-grained detect logic should bypass this code and
+ * directly call drm_kms_helper_hotplug_event() in case the connector state
+ * changed.
+ *
+ * This function must be called from process context with no mode
+ * setting locks held.
+ *
+ * Note that a connector can be both polled and probed from the hotplug handler,
+ * in case the hotplug interrupt is known to be unreliable.
+ */
bool drm_helper_hpd_irq_event(struct drm_device *dev)
{
struct drm_connector *connector;
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
new file mode 100644
index 000000000000..a2945ee6d675
--- /dev/null
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007-2008 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ * Copyright © 2014 Intel Corporation
+ * Daniel Vetter <daniel.vetter@ffwll.ch>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This header file contains mode setting related functions and definitions
+ * which are only used within the drm module as internal implementation details
+ * and are not exported to drivers.
+ */
+
+int drm_mode_object_get(struct drm_device *dev,
+ struct drm_mode_object *obj, uint32_t obj_type);
+void drm_mode_object_put(struct drm_device *dev,
+ struct drm_mode_object *object);
+
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 9e978aae8972..27671489477d 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -346,3 +346,399 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw)
}
}
EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
+
+/**
+ * DOC: dp helpers
+ *
+ * The DisplayPort AUX channel is an abstraction to allow generic, driver-
+ * independent access to AUX functionality. Drivers can take advantage of
+ * this by filling in the fields of the drm_dp_aux structure.
+ *
+ * Transactions are described using a hardware-independent drm_dp_aux_msg
+ * structure, which is passed into a driver's .transfer() implementation.
+ * Both native and I2C-over-AUX transactions are supported.
+ */
+
+static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
+ unsigned int offset, void *buffer, size_t size)
+{
+ struct drm_dp_aux_msg msg;
+ unsigned int retry;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.address = offset;
+ msg.request = request;
+ msg.buffer = buffer;
+ msg.size = size;
+
+ /*
+ * The specification doesn't give any recommendation on how often to
+ * retry native transactions, so retry 7 times like for I2C-over-AUX
+ * transactions.
+ */
+ for (retry = 0; retry < 7; retry++) {
+ err = aux->transfer(aux, &msg);
+ if (err < 0) {
+ if (err == -EBUSY)
+ continue;
+
+ return err;
+ }
+
+
+ switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
+ case DP_AUX_NATIVE_REPLY_ACK:
+ if (err < size)
+ return -EPROTO;
+ return err;
+
+ case DP_AUX_NATIVE_REPLY_NACK:
+ return -EIO;
+
+ case DP_AUX_NATIVE_REPLY_DEFER:
+ usleep_range(400, 500);
+ break;
+ }
+ }
+
+ DRM_DEBUG_KMS("too many retries, giving up\n");
+ return -EIO;
+}
+
+/**
+ * drm_dp_dpcd_read() - read a series of bytes from the DPCD
+ * @aux: DisplayPort AUX channel
+ * @offset: address of the (first) register to read
+ * @buffer: buffer to store the register values
+ * @size: number of bytes in @buffer
+ *
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -EIO is returned if the request was NAKed by the sink or
+ * if the retry count was exceeded. If not all bytes were transferred, this
+ * function returns -EPROTO. Errors from the underlying AUX channel transfer
+ * function, with the exception of -EBUSY (which causes the transaction to
+ * be retried), are propagated to the caller.
+ */
+ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
+ void *buffer, size_t size)
+{
+ return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer,
+ size);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_read);
+
+/**
+ * drm_dp_dpcd_write() - write a series of bytes to the DPCD
+ * @aux: DisplayPort AUX channel
+ * @offset: address of the (first) register to write
+ * @buffer: buffer containing the values to write
+ * @size: number of bytes in @buffer
+ *
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -EIO is returned if the request was NAKed by the sink or
+ * if the retry count was exceeded. If not all bytes were transferred, this
+ * function returns -EPROTO. Errors from the underlying AUX channel transfer
+ * function, with the exception of -EBUSY (which causes the transaction to
+ * be retried), are propagated to the caller.
+ */
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+ void *buffer, size_t size)
+{
+ return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer,
+ size);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_write);
+
+/**
+ * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207)
+ * @aux: DisplayPort AUX channel
+ * @status: buffer to store the link status in (must be at least 6 bytes)
+ *
+ * Returns the number of bytes transferred on success or a negative error
+ * code on failure.
+ */
+int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
+ u8 status[DP_LINK_STATUS_SIZE])
+{
+ return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status,
+ DP_LINK_STATUS_SIZE);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
+
+/**
+ * drm_dp_link_probe() - probe a DisplayPort link for capabilities
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to structure in which to return link capabilities
+ *
+ * The structure filled in by this function can usually be passed directly
+ * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
+ * configure the link based on the link's capabilities.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+ u8 values[3];
+ int err;
+
+ memset(link, 0, sizeof(*link));
+
+ err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ link->revision = values[0];
+ link->rate = drm_dp_bw_code_to_link_rate(values[1]);
+ link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
+
+ if (values[2] & DP_ENHANCED_FRAME_CAP)
+ link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_probe);
+
+/**
+ * drm_dp_link_power_up() - power up a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+ u8 value;
+ int err;
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (link->revision < 0x11)
+ return 0;
+
+ err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+ if (err < 0)
+ return err;
+
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D0;
+
+ err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+ if (err < 0)
+ return err;
+
+ /*
+ * According to the DP 1.1 specification, a "Sink Device must exit the
+ * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+ * Control Field" (register 0x600).
+ */
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_power_up);
+
+/**
+ * drm_dp_link_configure() - configure a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+ u8 values[2];
+ int err;
+
+ values[0] = drm_dp_link_rate_to_bw_code(link->rate);
+ values[1] = link->num_lanes;
+
+ if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+ values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+ err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_link_configure);
+
+/*
+ * I2C-over-AUX implementation
+ */
+
+static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_10BIT_ADDR;
+}
+
+/*
+ * Transfer a single I2C-over-AUX message and handle various error conditions,
+ * retrying the transaction as appropriate.
+ */
+static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+ unsigned int retry;
+ int err;
+
+ /*
+ * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
+ * is required to retry at least seven times upon receiving AUX_DEFER
+ * before giving up the AUX transaction.
+ */
+ for (retry = 0; retry < 7; retry++) {
+ err = aux->transfer(aux, msg);
+ if (err < 0) {
+ if (err == -EBUSY)
+ continue;
+
+ DRM_DEBUG_KMS("transaction failed: %d\n", err);
+ return err;
+ }
+
+
+ switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) {
+ case DP_AUX_NATIVE_REPLY_ACK:
+ /*
+ * For I2C-over-AUX transactions this isn't enough, we
+ * need to check for the I2C ACK reply.
+ */
+ break;
+
+ case DP_AUX_NATIVE_REPLY_NACK:
+ DRM_DEBUG_KMS("native nack\n");
+ return -EREMOTEIO;
+
+ case DP_AUX_NATIVE_REPLY_DEFER:
+ DRM_DEBUG_KMS("native defer");
+ /*
+ * We could check for I2C bit rate capabilities and if
+ * available adjust this interval. We could also be
+ * more careful with DP-to-legacy adapters where a
+ * long legacy cable may force very low I2C bit rates.
+ *
+ * For now just defer for long enough to hopefully be
+ * safe for all use-cases.
+ */
+ usleep_range(500, 600);
+ continue;
+
+ default:
+ DRM_ERROR("invalid native reply %#04x\n", msg->reply);
+ return -EREMOTEIO;
+ }
+
+ switch (msg->reply & DP_AUX_I2C_REPLY_MASK) {
+ case DP_AUX_I2C_REPLY_ACK:
+ /*
+ * Both native ACK and I2C ACK replies received. We
+ * can assume the transfer was successful.
+ */
+ if (err < msg->size)
+ return -EPROTO;
+ return 0;
+
+ case DP_AUX_I2C_REPLY_NACK:
+ DRM_DEBUG_KMS("I2C nack\n");
+ return -EREMOTEIO;
+
+ case DP_AUX_I2C_REPLY_DEFER:
+ DRM_DEBUG_KMS("I2C defer\n");
+ usleep_range(400, 500);
+ continue;
+
+ default:
+ DRM_ERROR("invalid I2C reply %#04x\n", msg->reply);
+ return -EREMOTEIO;
+ }
+ }
+
+ DRM_DEBUG_KMS("too many retries, giving up\n");
+ return -EREMOTEIO;
+}
+
+static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+ int num)
+{
+ struct drm_dp_aux *aux = adapter->algo_data;
+ unsigned int i, j;
+
+ for (i = 0; i < num; i++) {
+ struct drm_dp_aux_msg msg;
+ int err;
+
+ /*
+ * Many hardware implementations support FIFOs larger than a
+ * single byte, but it has been empirically determined that
+ * transferring data in larger chunks can actually lead to
+ * decreased performance. Therefore each message is simply
+ * transferred byte-by-byte.
+ */
+ for (j = 0; j < msgs[i].len; j++) {
+ memset(&msg, 0, sizeof(msg));
+ msg.address = msgs[i].addr;
+
+ msg.request = (msgs[i].flags & I2C_M_RD) ?
+ DP_AUX_I2C_READ :
+ DP_AUX_I2C_WRITE;
+
+ /*
+ * All messages except the last one are middle-of-
+ * transfer messages.
+ */
+ if ((i < num - 1) || (j < msgs[i].len - 1))
+ msg.request |= DP_AUX_I2C_MOT;
+
+ msg.buffer = msgs[i].buf + j;
+ msg.size = 1;
+
+ err = drm_dp_i2c_do_msg(aux, &msg);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return num;
+}
+
+static const struct i2c_algorithm drm_dp_i2c_algo = {
+ .functionality = drm_dp_i2c_functionality,
+ .master_xfer = drm_dp_i2c_xfer,
+};
+
+/**
+ * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
+{
+ aux->ddc.algo = &drm_dp_i2c_algo;
+ aux->ddc.algo_data = aux;
+ aux->ddc.retries = 3;
+
+ aux->ddc.class = I2C_CLASS_DDC;
+ aux->ddc.owner = THIS_MODULE;
+ aux->ddc.dev.parent = aux->dev;
+ aux->ddc.dev.of_node = aux->dev->of_node;
+
+ strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+ sizeof(aux->ddc.name));
+
+ return i2c_add_adapter(&aux->ddc);
+}
+EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus);
+
+/**
+ * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * @aux: DisplayPort AUX channel
+ */
+void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux)
+{
+ i2c_del_adapter(&aux->ddc);
+}
+EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 345be03c23db..03711d00aaae 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -286,6 +286,45 @@ static int drm_version(struct drm_device *dev, void *data,
}
/**
+ * drm_ioctl_permit - Check ioctl permissions against caller
+ *
+ * @flags: ioctl permission flags.
+ * @file_priv: Pointer to struct drm_file identifying the caller.
+ *
+ * Checks whether the caller is allowed to run an ioctl with the
+ * indicated permissions. If so, returns zero. Otherwise returns an
+ * error code suitable for ioctl return.
+ */
+static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
+{
+ /* ROOT_ONLY is only for CAP_SYS_ADMIN */
+ if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
+ return -EACCES;
+
+ /* AUTH is only for authenticated or render client */
+ if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
+ !file_priv->authenticated))
+ return -EACCES;
+
+ /* MASTER is only for master or control clients */
+ if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
+ !drm_is_control_client(file_priv)))
+ return -EACCES;
+
+ /* Control clients must be explicitly allowed */
+ if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
+ drm_is_control_client(file_priv)))
+ return -EACCES;
+
+ /* Render clients must be explicitly allowed */
+ if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
+ drm_is_render_client(file_priv)))
+ return -EACCES;
+
+ return 0;
+}
+
+/**
* Called whenever a process performs an ioctl on /dev/drm.
*
* \param inode device inode.
@@ -344,65 +383,64 @@ long drm_ioctl(struct file *filp,
DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
task_pid_nr(current),
- (long)old_encode_dev(file_priv->minor->device),
+ (long)old_encode_dev(file_priv->minor->kdev->devt),
file_priv->authenticated, ioctl->name);
/* Do not trust userspace, use our own definition */
func = ioctl->func;
- if (!func) {
+ if (unlikely(!func)) {
DRM_DEBUG("no function\n");
retcode = -EINVAL;
- } else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
- ((ioctl->flags & DRM_AUTH) && !drm_is_render_client(file_priv) && !file_priv->authenticated) ||
- ((ioctl->flags & DRM_MASTER) && !file_priv->is_master) ||
- (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ||
- (!(ioctl->flags & DRM_RENDER_ALLOW) && drm_is_render_client(file_priv))) {
- retcode = -EACCES;
- } else {
- if (cmd & (IOC_IN | IOC_OUT)) {
- if (asize <= sizeof(stack_kdata)) {
- kdata = stack_kdata;
- } else {
- kdata = kmalloc(asize, GFP_KERNEL);
- if (!kdata) {
- retcode = -ENOMEM;
- goto err_i1;
- }
- }
- if (asize > usize)
- memset(kdata + usize, 0, asize - usize);
- }
+ goto err_i1;
+ }
- if (cmd & IOC_IN) {
- if (copy_from_user(kdata, (void __user *)arg,
- usize) != 0) {
- retcode = -EFAULT;
+ retcode = drm_ioctl_permit(ioctl->flags, file_priv);
+ if (unlikely(retcode))
+ goto err_i1;
+
+ if (cmd & (IOC_IN | IOC_OUT)) {
+ if (asize <= sizeof(stack_kdata)) {
+ kdata = stack_kdata;
+ } else {
+ kdata = kmalloc(asize, GFP_KERNEL);
+ if (!kdata) {
+ retcode = -ENOMEM;
goto err_i1;
}
- } else
- memset(kdata, 0, usize);
-
- if (ioctl->flags & DRM_UNLOCKED)
- retcode = func(dev, kdata, file_priv);
- else {
- mutex_lock(&drm_global_mutex);
- retcode = func(dev, kdata, file_priv);
- mutex_unlock(&drm_global_mutex);
}
+ if (asize > usize)
+ memset(kdata + usize, 0, asize - usize);
+ }
- if (cmd & IOC_OUT) {
- if (copy_to_user((void __user *)arg, kdata,
- usize) != 0)
- retcode = -EFAULT;
+ if (cmd & IOC_IN) {
+ if (copy_from_user(kdata, (void __user *)arg,
+ usize) != 0) {
+ retcode = -EFAULT;
+ goto err_i1;
}
+ } else
+ memset(kdata, 0, usize);
+
+ if (ioctl->flags & DRM_UNLOCKED)
+ retcode = func(dev, kdata, file_priv);
+ else {
+ mutex_lock(&drm_global_mutex);
+ retcode = func(dev, kdata, file_priv);
+ mutex_unlock(&drm_global_mutex);
+ }
+
+ if (cmd & IOC_OUT) {
+ if (copy_to_user((void __user *)arg, kdata,
+ usize) != 0)
+ retcode = -EFAULT;
}
err_i1:
if (!ioctl)
DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
task_pid_nr(current),
- (long)old_encode_dev(file_priv->minor->device),
+ (long)old_encode_dev(file_priv->minor->kdev->devt),
file_priv->authenticated, cmd, nr);
if (kdata != stack_kdata)
@@ -412,3 +450,21 @@ long drm_ioctl(struct file *filp,
return retcode;
}
EXPORT_SYMBOL(drm_ioctl);
+
+/**
+ * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
+ *
+ * @nr: Ioctl number.
+ * @flags: Where to return the ioctl permission flags
+ */
+bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
+{
+ if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
+ (nr < DRM_COMMAND_BASE)) {
+ *flags = drm_ioctls[nr].flags;
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(drm_ioctl_flags);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index b924306b8477..d4e3f9d9370f 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1098,10 +1098,14 @@ EXPORT_SYMBOL(drm_edid_is_valid);
/**
* Get EDID information via I2C.
*
- * \param adapter : i2c device adaptor
- * \param buf : EDID data buffer to be filled
- * \param len : EDID data buffer length
- * \return 0 on success or -1 on failure.
+ * @adapter : i2c device adaptor
+ * @buf: EDID data buffer to be filled
+ * @block: 128 byte EDID block to start fetching from
+ * @len: EDID data buffer length to fetch
+ *
+ * Returns:
+ *
+ * 0 on success or -1 on failure.
*
* Try to fetch EDID information by calling i2c driver function.
*/
@@ -1243,9 +1247,11 @@ out:
/**
* Probe DDC presence.
+ * @adapter: i2c adapter to probe
+ *
+ * Returns:
*
- * \param adapter : i2c device adaptor
- * \return 1 on success
+ * 1 on success
*/
bool
drm_probe_ddc(struct i2c_adapter *adapter)
@@ -1586,8 +1592,10 @@ bad_std_timing(u8 a, u8 b)
/**
* drm_mode_std - convert standard mode info (width, height, refresh) into mode
+ * @connector: connector of for the EDID block
+ * @edid: EDID block to scan
* @t: standard timing params
- * @timing_level: standard timing level
+ * @revision: standard timing level
*
* Take the standard timing params (in this case width, aspect, and refresh)
* and convert them into a real mode using CVT/GTF/DMT.
@@ -2132,6 +2140,7 @@ do_established_modes(struct detailed_timing *timing, void *c)
/**
* add_established_modes - get est. modes from EDID and add them
+ * @connector: connector of for the EDID block
* @edid: EDID block to scan
*
* Each EDID block contains a bitmap of the supported "established modes" list
@@ -2194,6 +2203,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
/**
* add_standard_modes - get std. modes from EDID and add them
+ * @connector: connector of for the EDID block
* @edid: EDID block to scan
*
* Standard modes can be calculated using the appropriate standard (DMT,
@@ -2580,6 +2590,9 @@ drm_display_mode_from_vic_index(struct drm_connector *connector,
return NULL;
newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
+ if (!newmode)
+ return NULL;
+
newmode->vrefresh = 0;
return newmode;
@@ -3300,6 +3313,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
/**
* drm_detect_monitor_audio - check monitor audio capability
+ * @edid: EDID block to scan
*
* Monitor should have CEA extension block.
* If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
@@ -3345,6 +3359,7 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
/**
* drm_rgb_quant_range_selectable - is RGB quantization range selectable?
+ * @edid: EDID block to scan
*
* Check whether the monitor reports the RGB quantization range selection
* as supported. The AVI infoframe can then be used to inform the monitor
@@ -3564,8 +3579,8 @@ void drm_set_preferred_mode(struct drm_connector *connector,
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
- if (drm_mode_width(mode) == hpref &&
- drm_mode_height(mode) == vpref)
+ if (mode->hdisplay == hpref &&
+ mode->vdisplay == vpref)
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
}
@@ -3599,6 +3614,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
+ frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
return 0;
}
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 98a03639b413..04d3fd3658f3 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -232,7 +232,7 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
if (crtc->base.id == c->base.id)
- return c->fb;
+ return c->primary->fb;
}
return NULL;
@@ -291,7 +291,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
drm_warn_on_modeset_not_all_locked(dev);
list_for_each_entry(plane, &dev->mode_config.plane_list, head)
- drm_plane_force_disable(plane);
+ if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+ drm_plane_force_disable(plane);
for (i = 0; i < fb_helper->crtc_count; i++) {
struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
@@ -365,9 +366,9 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
return false;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (crtc->fb)
+ if (crtc->primary->fb)
crtcs_bound++;
- if (crtc->fb == fb_helper->fb)
+ if (crtc->primary->fb == fb_helper->fb)
bound++;
}
@@ -516,6 +517,9 @@ int drm_fb_helper_init(struct drm_device *dev,
struct drm_crtc *crtc;
int i;
+ if (!max_conn_count)
+ return -EINVAL;
+
fb_helper->dev = dev;
INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
@@ -809,8 +813,6 @@ int drm_fb_helper_set_par(struct fb_info *info)
struct drm_fb_helper *fb_helper = info->par;
struct drm_device *dev = fb_helper->dev;
struct fb_var_screeninfo *var = &info->var;
- int ret;
- int i;
if (var->pixclock != 0) {
DRM_ERROR("PIXEL CLOCK SET\n");
@@ -818,13 +820,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
}
drm_modeset_lock_all(dev);
- for (i = 0; i < fb_helper->crtc_count; i++) {
- ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set);
- if (ret) {
- drm_modeset_unlock_all(dev);
- return ret;
- }
- }
+ drm_fb_helper_restore_fbdev_mode(fb_helper);
drm_modeset_unlock_all(dev);
if (fb_helper->delayed_hotplug) {
@@ -1136,19 +1132,20 @@ static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
return count;
}
-static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
+struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
{
struct drm_display_mode *mode;
list_for_each_entry(mode, &fb_connector->connector->modes, head) {
- if (drm_mode_width(mode) > width ||
- drm_mode_height(mode) > height)
+ if (mode->hdisplay > width ||
+ mode->vdisplay > height)
continue;
if (mode->type & DRM_MODE_TYPE_PREFERRED)
return mode;
}
return NULL;
}
+EXPORT_SYMBOL(drm_has_preferred_mode);
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
{
@@ -1157,11 +1154,12 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
return cmdline_mode->specified;
}
-static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
+struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
int width, int height)
{
struct drm_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode = NULL;
+ bool prefer_non_interlace;
cmdline_mode = &fb_helper_conn->cmdline_mode;
if (cmdline_mode->specified == false)
@@ -1173,6 +1171,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
if (cmdline_mode->rb || cmdline_mode->margins)
goto create_mode;
+ prefer_non_interlace = !cmdline_mode->interlace;
+ again:
list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
/* check width/height */
if (mode->hdisplay != cmdline_mode->xres ||
@@ -1187,16 +1187,25 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
if (cmdline_mode->interlace) {
if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
continue;
+ } else if (prefer_non_interlace) {
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ continue;
}
return mode;
}
+ if (prefer_non_interlace) {
+ prefer_non_interlace = false;
+ goto again;
+ }
+
create_mode:
mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
cmdline_mode);
list_add(&mode->head, &fb_helper_conn->connector->modes);
return mode;
}
+EXPORT_SYMBOL(drm_pick_cmdline_mode);
static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
{
@@ -1539,9 +1548,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
drm_fb_helper_parse_command_line(fb_helper);
+ mutex_lock(&dev->mode_config.mutex);
count = drm_fb_helper_probe_connector_modes(fb_helper,
dev->mode_config.max_width,
dev->mode_config.max_height);
+ mutex_unlock(&dev->mode_config.mutex);
/*
* we shouldn't end up with no modes here.
*/
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 309023f12d7f..e1eba0b7cd45 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -39,12 +39,12 @@
#include <linux/slab.h>
#include <linux/module.h>
-/* from BKL pushdown: note that nothing else serializes idr_find() */
+/* from BKL pushdown */
DEFINE_MUTEX(drm_global_mutex);
EXPORT_SYMBOL(drm_global_mutex);
static int drm_open_helper(struct inode *inode, struct file *filp,
- struct drm_device * dev);
+ struct drm_minor *minor);
static int drm_setup(struct drm_device * dev)
{
@@ -79,38 +79,23 @@ static int drm_setup(struct drm_device * dev)
*/
int drm_open(struct inode *inode, struct file *filp)
{
- struct drm_device *dev = NULL;
- int minor_id = iminor(inode);
+ struct drm_device *dev;
struct drm_minor *minor;
- int retcode = 0;
+ int retcode;
int need_setup = 0;
- struct address_space *old_mapping;
- struct address_space *old_imapping;
-
- minor = idr_find(&drm_minors_idr, minor_id);
- if (!minor)
- return -ENODEV;
- if (!(dev = minor->dev))
- return -ENODEV;
-
- if (drm_device_is_unplugged(dev))
- return -ENODEV;
+ minor = drm_minor_acquire(iminor(inode));
+ if (IS_ERR(minor))
+ return PTR_ERR(minor);
+ dev = minor->dev;
if (!dev->open_count++)
need_setup = 1;
- mutex_lock(&dev->struct_mutex);
- old_imapping = inode->i_mapping;
- old_mapping = dev->dev_mapping;
- if (old_mapping == NULL)
- dev->dev_mapping = &inode->i_data;
- /* ihold ensures nobody can remove inode with our i_data */
- ihold(container_of(dev->dev_mapping, struct inode, i_data));
- inode->i_mapping = dev->dev_mapping;
- filp->f_mapping = dev->dev_mapping;
- mutex_unlock(&dev->struct_mutex);
- retcode = drm_open_helper(inode, filp, dev);
+ /* share address_space across all char-devs of a single device */
+ filp->f_mapping = dev->anon_inode->i_mapping;
+
+ retcode = drm_open_helper(inode, filp, minor);
if (retcode)
goto err_undo;
if (need_setup) {
@@ -121,13 +106,8 @@ int drm_open(struct inode *inode, struct file *filp)
return 0;
err_undo:
- mutex_lock(&dev->struct_mutex);
- filp->f_mapping = old_imapping;
- inode->i_mapping = old_imapping;
- iput(container_of(dev->dev_mapping, struct inode, i_data));
- dev->dev_mapping = old_mapping;
- mutex_unlock(&dev->struct_mutex);
dev->open_count--;
+ drm_minor_release(minor);
return retcode;
}
EXPORT_SYMBOL(drm_open);
@@ -143,33 +123,30 @@ EXPORT_SYMBOL(drm_open);
*/
int drm_stub_open(struct inode *inode, struct file *filp)
{
- struct drm_device *dev = NULL;
+ struct drm_device *dev;
struct drm_minor *minor;
- int minor_id = iminor(inode);
int err = -ENODEV;
const struct file_operations *new_fops;
DRM_DEBUG("\n");
mutex_lock(&drm_global_mutex);
- minor = idr_find(&drm_minors_idr, minor_id);
- if (!minor)
- goto out;
-
- if (!(dev = minor->dev))
- goto out;
-
- if (drm_device_is_unplugged(dev))
- goto out;
+ minor = drm_minor_acquire(iminor(inode));
+ if (IS_ERR(minor))
+ goto out_unlock;
+ dev = minor->dev;
new_fops = fops_get(dev->driver->fops);
if (!new_fops)
- goto out;
+ goto out_release;
replace_fops(filp, new_fops);
if (filp->f_op->open)
err = filp->f_op->open(inode, filp);
-out:
+
+out_release:
+ drm_minor_release(minor);
+out_unlock:
mutex_unlock(&drm_global_mutex);
return err;
}
@@ -196,16 +173,16 @@ static int drm_cpu_valid(void)
*
* \param inode device inode.
* \param filp file pointer.
- * \param dev device.
+ * \param minor acquired minor-object.
* \return zero on success or a negative number on failure.
*
* Creates and initializes a drm_file structure for the file private data in \p
* filp and add it into the double linked list in \p dev.
*/
static int drm_open_helper(struct inode *inode, struct file *filp,
- struct drm_device * dev)
+ struct drm_minor *minor)
{
- int minor_id = iminor(inode);
+ struct drm_device *dev = minor->dev;
struct drm_file *priv;
int ret;
@@ -216,7 +193,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
return -EINVAL;
- DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id);
+ DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -226,11 +203,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->filp = filp;
priv->uid = current_euid();
priv->pid = get_pid(task_pid(current));
- priv->minor = idr_find(&drm_minors_idr, minor_id);
- if (!priv->minor) {
- ret = -ENODEV;
- goto out_put_pid;
- }
+ priv->minor = minor;
/* for compatibility root is always authenticated */
priv->always_authenticated = capable(CAP_SYS_ADMIN);
@@ -258,12 +231,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
/* if there is no current master make this fd it, but do not create
* any master object for render clients */
- mutex_lock(&dev->struct_mutex);
- if (!priv->minor->master && !drm_is_render_client(priv)) {
+ mutex_lock(&dev->master_mutex);
+ if (drm_is_primary_client(priv) && !priv->minor->master) {
/* create a new master */
priv->minor->master = drm_master_create(priv->minor);
if (!priv->minor->master) {
- mutex_unlock(&dev->struct_mutex);
ret = -ENOMEM;
goto out_close;
}
@@ -271,37 +243,31 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->is_master = 1;
/* take another reference for the copy in the local file priv */
priv->master = drm_master_get(priv->minor->master);
-
priv->authenticated = 1;
- mutex_unlock(&dev->struct_mutex);
if (dev->driver->master_create) {
ret = dev->driver->master_create(dev, priv->master);
if (ret) {
- mutex_lock(&dev->struct_mutex);
/* drop both references if this fails */
drm_master_put(&priv->minor->master);
drm_master_put(&priv->master);
- mutex_unlock(&dev->struct_mutex);
goto out_close;
}
}
- mutex_lock(&dev->struct_mutex);
if (dev->driver->master_set) {
ret = dev->driver->master_set(dev, priv, true);
if (ret) {
/* drop both references if this fails */
drm_master_put(&priv->minor->master);
drm_master_put(&priv->master);
- mutex_unlock(&dev->struct_mutex);
goto out_close;
}
}
- } else if (!drm_is_render_client(priv)) {
+ } else if (drm_is_primary_client(priv)) {
/* get a reference to the master */
priv->master = drm_master_get(priv->minor->master);
}
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&dev->master_mutex);
mutex_lock(&dev->struct_mutex);
list_add(&priv->lhead, &dev->filelist);
@@ -330,6 +296,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
return 0;
out_close:
+ mutex_unlock(&dev->master_mutex);
if (dev->driver->postclose)
dev->driver->postclose(dev, priv);
out_prime_destroy:
@@ -337,7 +304,6 @@ out_prime_destroy:
drm_prime_destroy_file_private(&priv->prime);
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_release(dev, priv);
-out_put_pid:
put_pid(priv->pid);
kfree(priv);
filp->private_data = NULL;
@@ -435,7 +401,6 @@ int drm_lastclose(struct drm_device * dev)
drm_legacy_dma_takedown(dev);
- dev->dev_mapping = NULL;
mutex_unlock(&dev->struct_mutex);
drm_legacy_dev_reinit(dev);
@@ -459,7 +424,8 @@ int drm_lastclose(struct drm_device * dev)
int drm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
- struct drm_device *dev = file_priv->minor->dev;
+ struct drm_minor *minor = file_priv->minor;
+ struct drm_device *dev = minor->dev;
int retcode = 0;
mutex_lock(&drm_global_mutex);
@@ -475,7 +441,7 @@ int drm_release(struct inode *inode, struct file *filp)
DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
task_pid_nr(current),
- (long)old_encode_dev(file_priv->minor->device),
+ (long)old_encode_dev(file_priv->minor->kdev->devt),
dev->open_count);
/* Release any auth tokens that might point to this file_priv,
@@ -518,11 +484,13 @@ int drm_release(struct inode *inode, struct file *filp)
}
mutex_unlock(&dev->ctxlist_mutex);
- mutex_lock(&dev->struct_mutex);
+ mutex_lock(&dev->master_mutex);
if (file_priv->is_master) {
struct drm_master *master = file_priv->master;
struct drm_file *temp;
+
+ mutex_lock(&dev->struct_mutex);
list_for_each_entry(temp, &dev->filelist, lhead) {
if ((temp->master == file_priv->master) &&
(temp != file_priv))
@@ -541,6 +509,7 @@ int drm_release(struct inode *inode, struct file *filp)
master->lock.file_priv = NULL;
wake_up_interruptible_all(&master->lock.lock_queue);
}
+ mutex_unlock(&dev->struct_mutex);
if (file_priv->minor->master == file_priv->master) {
/* drop the reference held my the minor */
@@ -550,13 +519,13 @@ int drm_release(struct inode *inode, struct file *filp)
}
}
- BUG_ON(dev->dev_mapping == NULL);
- iput(container_of(dev->dev_mapping, struct inode, i_data));
-
- /* drop the reference held my the file priv */
+ /* drop the master reference held by the file priv */
if (file_priv->master)
drm_master_put(&file_priv->master);
file_priv->is_master = 0;
+ mutex_unlock(&dev->master_mutex);
+
+ mutex_lock(&dev->struct_mutex);
list_del(&file_priv->lhead);
mutex_unlock(&dev->struct_mutex);
@@ -581,6 +550,8 @@ int drm_release(struct inode *inode, struct file *filp)
}
mutex_unlock(&drm_global_mutex);
+ drm_minor_release(minor);
+
return retcode;
}
EXPORT_SYMBOL(drm_release);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 5bbad873c798..9909bef59800 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -85,9 +85,9 @@
#endif
/**
- * Initialize the GEM device fields
+ * drm_gem_init - Initialize the GEM device fields
+ * @dev: drm_devic structure to initialize
*/
-
int
drm_gem_init(struct drm_device *dev)
{
@@ -120,6 +120,11 @@ drm_gem_destroy(struct drm_device *dev)
}
/**
+ * drm_gem_object_init - initialize an allocated shmem-backed GEM object
+ * @dev: drm_device the object should be initialized for
+ * @obj: drm_gem_object to initialize
+ * @size: object size
+ *
* Initialize an already allocated GEM object of the specified size with
* shmfs backing store.
*/
@@ -141,6 +146,11 @@ int drm_gem_object_init(struct drm_device *dev,
EXPORT_SYMBOL(drm_gem_object_init);
/**
+ * drm_gem_object_init - initialize an allocated private GEM object
+ * @dev: drm_device the object should be initialized for
+ * @obj: drm_gem_object to initialize
+ * @size: object size
+ *
* Initialize an already allocated GEM object of the specified size with
* no GEM provided backing store. Instead the caller is responsible for
* backing the object and handling it.
@@ -176,6 +186,9 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
}
/**
+ * drm_gem_object_free - release resources bound to userspace handles
+ * @obj: GEM object to clean up.
+ *
* Called after the last handle to the object has been closed
*
* Removes any name for the object. Note that this must be
@@ -225,7 +238,12 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
}
/**
- * Removes the mapping from handle to filp for this object.
+ * drm_gem_handle_delete - deletes the given file-private handle
+ * @filp: drm file-private structure to use for the handle look up
+ * @handle: userspace handle to delete
+ *
+ * Removes the GEM handle from the @filp lookup table and if this is the last
+ * handle also cleans up linked resources like GEM names.
*/
int
drm_gem_handle_delete(struct drm_file *filp, u32 handle)
@@ -270,6 +288,9 @@ EXPORT_SYMBOL(drm_gem_handle_delete);
/**
* drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers
+ * @file: drm file-private structure to remove the dumb handle from
+ * @dev: corresponding drm_device
+ * @handle: the dumb handle to remove
*
* This implements the ->dumb_destroy kms driver callback for drivers which use
* gem to manage their backing storage.
@@ -284,6 +305,9 @@ EXPORT_SYMBOL(drm_gem_dumb_destroy);
/**
* drm_gem_handle_create_tail - internal functions to create a handle
+ * @file_priv: drm file-private structure to register the handle for
+ * @obj: object to register
+ * @handlep: pionter to return the created handle to the caller
*
* This expects the dev->object_name_lock to be held already and will drop it
* before returning. Used to avoid races in establishing new handles when
@@ -336,6 +360,11 @@ drm_gem_handle_create_tail(struct drm_file *file_priv,
}
/**
+ * gem_handle_create - create a gem handle for an object
+ * @file_priv: drm file-private structure to register the handle for
+ * @obj: object to register
+ * @handlep: pionter to return the created handle to the caller
+ *
* Create a handle for this object. This adds a handle reference
* to the object, which includes a regular reference count. Callers
* will likely want to dereference the object afterwards.
@@ -536,6 +565,11 @@ drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
EXPORT_SYMBOL(drm_gem_object_lookup);
/**
+ * drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl
+ * @dev: drm_device
+ * @data: ioctl data
+ * @file_priv: drm file-private structure
+ *
* Releases the handle to an mm object.
*/
int
@@ -554,6 +588,11 @@ drm_gem_close_ioctl(struct drm_device *dev, void *data,
}
/**
+ * drm_gem_flink_ioctl - implementation of the GEM_FLINK ioctl
+ * @dev: drm_device
+ * @data: ioctl data
+ * @file_priv: drm file-private structure
+ *
* Create a global name for an object, returning the name.
*
* Note that the name does not hold a reference; when the object
@@ -601,6 +640,11 @@ err:
}
/**
+ * drm_gem_open - implementation of the GEM_OPEN ioctl
+ * @dev: drm_device
+ * @data: ioctl data
+ * @file_priv: drm file-private structure
+ *
* Open an object using the global name, returning a handle and the size.
*
* This handle (of course) holds a reference to the object, so the object
@@ -640,6 +684,10 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
}
/**
+ * gem_gem_open - initalizes GEM file-private structures at devnode open time
+ * @dev: drm_device which is being opened by userspace
+ * @file_private: drm file-private structure to set up
+ *
* Called at device open time, sets up the structure for handling refcounting
* of mm objects.
*/
@@ -650,7 +698,7 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
spin_lock_init(&file_private->table_lock);
}
-/**
+/*
* Called at device close to release the file's
* handle references on objects.
*/
@@ -674,6 +722,10 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
}
/**
+ * drm_gem_release - release file-private GEM resources
+ * @dev: drm_device which is being closed by userspace
+ * @file_private: drm file-private structure to clean up
+ *
* Called at close time when the filp is going away.
*
* Releases any remaining references on objects by this filp.
@@ -692,11 +744,16 @@ drm_gem_object_release(struct drm_gem_object *obj)
WARN_ON(obj->dma_buf);
if (obj->filp)
- fput(obj->filp);
+ fput(obj->filp);
+
+ drm_gem_free_mmap_offset(obj);
}
EXPORT_SYMBOL(drm_gem_object_release);
/**
+ * drm_gem_object_free - free a GEM object
+ * @kref: kref of the object to free
+ *
* Called after the last reference to the object has been lost.
* Must be called holding struct_ mutex
*
@@ -782,7 +839,7 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_ops = dev->driver->gem_vm_ops;
vma->vm_private_data = obj;
- vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+ vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
/* Take a ref for this mapping of the object, so that the fault
* handler can dereference the mmap offset's pointer to the object.
@@ -818,7 +875,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_device *dev = priv->minor->dev;
struct drm_gem_object *obj;
struct drm_vma_offset_node *node;
- int ret = 0;
+ int ret;
if (drm_device_is_unplugged(dev))
return -ENODEV;
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index 6b51bf90df0e..05c97c5350a1 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -79,7 +79,6 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
unsigned int size)
{
struct drm_gem_cma_object *cma_obj;
- struct sg_table *sgt = NULL;
int ret;
size = round_up(size, PAGE_SIZE);
@@ -97,23 +96,9 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
goto error;
}
- sgt = kzalloc(sizeof(*cma_obj->sgt), GFP_KERNEL);
- if (sgt == NULL) {
- ret = -ENOMEM;
- goto error;
- }
-
- ret = dma_get_sgtable(drm->dev, sgt, cma_obj->vaddr,
- cma_obj->paddr, size);
- if (ret < 0)
- goto error;
-
- cma_obj->sgt = sgt;
-
return cma_obj;
error:
- kfree(sgt);
drm_gem_cma_free_object(&cma_obj->base);
return ERR_PTR(ret);
}
@@ -175,10 +160,6 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
if (cma_obj->vaddr) {
dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size,
cma_obj->vaddr, cma_obj->paddr);
- if (cma_obj->sgt) {
- sg_free_table(cma_obj->sgt);
- kfree(cma_obj->sgt);
- }
} else if (gem_obj->import_attach) {
drm_prime_gem_destroy(gem_obj, cma_obj->sgt);
}
@@ -253,8 +234,17 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj,
{
int ret;
- ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
+ /*
+ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
+ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
+ * the whole buffer.
+ */
+ vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_pgoff = 0;
+
+ ret = dma_mmap_writecombine(cma_obj->base.dev->dev, vma,
+ cma_obj->vaddr, cma_obj->paddr,
+ vma->vm_end - vma->vm_start);
if (ret)
drm_gem_vm_close(vma);
@@ -292,9 +282,9 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
off = drm_vma_node_start(&obj->vma_node);
- seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d",
+ seq_printf(m, "%2d (%2d) %08llx %pad %p %d",
obj->name, obj->refcount.refcount.counter,
- off, cma_obj->paddr, cma_obj->vaddr, obj->size);
+ off, &cma_obj->paddr, cma_obj->vaddr, obj->size);
seq_printf(m, "\n");
}
@@ -342,7 +332,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
cma_obj->paddr = sg_dma_address(sgt->sgl);
cma_obj->sgt = sgt;
- DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, size);
+ DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, size);
return &cma_obj->base;
}
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index f4dc9b7a3831..93a42040bedb 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -328,6 +328,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
return -EINVAL;
file_priv->stereo_allowed = req->value;
break;
+ case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
+ if (!drm_universal_planes)
+ return -EINVAL;
+ if (req->value > 1)
+ return -EINVAL;
+ file_priv->universal_planes = req->value;
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index b155ee2ffa17..09821f46d768 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -142,8 +142,12 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host)
{
struct device_node *node;
- for_each_available_child_of_node(host->dev->of_node, node)
+ for_each_available_child_of_node(host->dev->of_node, node) {
+ /* skip nodes without reg property */
+ if (!of_find_property(node, "reg", NULL))
+ continue;
of_mipi_dsi_device_add(host, node);
+ }
return 0;
}
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index af93cc55259f..71e2d3fcd6ee 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -47,7 +47,48 @@
#include <linux/seq_file.h>
#include <linux/export.h>
-#define MM_UNUSED_TARGET 4
+/**
+ * DOC: Overview
+ *
+ * drm_mm provides a simple range allocator. The drivers are free to use the
+ * resource allocator from the linux core if it suits them, the upside of drm_mm
+ * is that it's in the DRM core. Which means that it's easier to extend for
+ * some of the crazier special purpose needs of gpus.
+ *
+ * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
+ * Drivers are free to embed either of them into their own suitable
+ * datastructures. drm_mm itself will not do any allocations of its own, so if
+ * drivers choose not to embed nodes they need to still allocate them
+ * themselves.
+ *
+ * The range allocator also supports reservation of preallocated blocks. This is
+ * useful for taking over initial mode setting configurations from the firmware,
+ * where an object needs to be created which exactly matches the firmware's
+ * scanout target. As long as the range is still free it can be inserted anytime
+ * after the allocator is initialized, which helps with avoiding looped
+ * depencies in the driver load sequence.
+ *
+ * drm_mm maintains a stack of most recently freed holes, which of all
+ * simplistic datastructures seems to be a fairly decent approach to clustering
+ * allocations and avoiding too much fragmentation. This means free space
+ * searches are O(num_holes). Given that all the fancy features drm_mm supports
+ * something better would be fairly complex and since gfx thrashing is a fairly
+ * steep cliff not a real concern. Removing a node again is O(1).
+ *
+ * drm_mm supports a few features: Alignment and range restrictions can be
+ * supplied. Further more every &drm_mm_node has a color value (which is just an
+ * opaqua unsigned long) which in conjunction with a driver callback can be used
+ * to implement sophisticated placement restrictions. The i915 DRM driver uses
+ * this to implement guard pages between incompatible caching domains in the
+ * graphics TT.
+ *
+ * Two behaviors are supported for searching and allocating: bottom-up and top-down.
+ * The default is bottom-up. Top-down allocation can be used if the memory area
+ * has different restrictions, or just to reduce fragmentation.
+ *
+ * Finally iteration helpers to walk all nodes and all holes are provided as are
+ * some basic allocator dumpers for debugging.
+ */
static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
unsigned long size,
@@ -65,7 +106,8 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
unsigned long size, unsigned alignment,
- unsigned long color)
+ unsigned long color,
+ enum drm_mm_allocator_flags flags)
{
struct drm_mm *mm = hole_node->mm;
unsigned long hole_start = drm_mm_hole_node_start(hole_node);
@@ -78,12 +120,22 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
if (mm->color_adjust)
mm->color_adjust(hole_node, color, &adj_start, &adj_end);
+ if (flags & DRM_MM_CREATE_TOP)
+ adj_start = adj_end - size;
+
if (alignment) {
unsigned tmp = adj_start % alignment;
- if (tmp)
- adj_start += alignment - tmp;
+ if (tmp) {
+ if (flags & DRM_MM_CREATE_TOP)
+ adj_start -= tmp;
+ else
+ adj_start += alignment - tmp;
+ }
}
+ BUG_ON(adj_start < hole_start);
+ BUG_ON(adj_end > hole_end);
+
if (adj_start == hole_start) {
hole_node->hole_follows = 0;
list_del(&hole_node->hole_stack);
@@ -107,6 +159,20 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
}
}
+/**
+ * drm_mm_reserve_node - insert an pre-initialized node
+ * @mm: drm_mm allocator to insert @node into
+ * @node: drm_mm_node to insert
+ *
+ * This functions inserts an already set-up drm_mm_node into the allocator,
+ * meaning that start, size and color must be set by the caller. This is useful
+ * to initialize the allocator with preallocated objects which must be set-up
+ * before the range allocator can be set-up, e.g. when taking over a firmware
+ * framebuffer.
+ *
+ * Returns:
+ * 0 on success, -ENOSPC if there's no hole where @node is.
+ */
int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
{
struct drm_mm_node *hole;
@@ -148,23 +214,34 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
EXPORT_SYMBOL(drm_mm_reserve_node);
/**
- * Search for free space and insert a preallocated memory node. Returns
- * -ENOSPC if no suitable free area is available. The preallocated memory node
- * must be cleared.
+ * drm_mm_insert_node_generic - search for space and insert @node
+ * @mm: drm_mm to allocate from
+ * @node: preallocate node to insert
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for this node
+ * @sflags: flags to fine-tune the allocation search
+ * @aflags: flags to fine-tune the allocation behavior
+ *
+ * The preallocated node must be cleared to 0.
+ *
+ * Returns:
+ * 0 on success, -ENOSPC if there's no suitable hole.
*/
int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
unsigned long size, unsigned alignment,
unsigned long color,
- enum drm_mm_search_flags flags)
+ enum drm_mm_search_flags sflags,
+ enum drm_mm_allocator_flags aflags)
{
struct drm_mm_node *hole_node;
hole_node = drm_mm_search_free_generic(mm, size, alignment,
- color, flags);
+ color, sflags);
if (!hole_node)
return -ENOSPC;
- drm_mm_insert_helper(hole_node, node, size, alignment, color);
+ drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags);
return 0;
}
EXPORT_SYMBOL(drm_mm_insert_node_generic);
@@ -173,7 +250,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
unsigned long size, unsigned alignment,
unsigned long color,
- unsigned long start, unsigned long end)
+ unsigned long start, unsigned long end,
+ enum drm_mm_allocator_flags flags)
{
struct drm_mm *mm = hole_node->mm;
unsigned long hole_start = drm_mm_hole_node_start(hole_node);
@@ -188,13 +266,20 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
if (adj_end > end)
adj_end = end;
+ if (flags & DRM_MM_CREATE_TOP)
+ adj_start = adj_end - size;
+
if (mm->color_adjust)
mm->color_adjust(hole_node, color, &adj_start, &adj_end);
if (alignment) {
unsigned tmp = adj_start % alignment;
- if (tmp)
- adj_start += alignment - tmp;
+ if (tmp) {
+ if (flags & DRM_MM_CREATE_TOP)
+ adj_start -= tmp;
+ else
+ adj_start += alignment - tmp;
+ }
}
if (adj_start == hole_start) {
@@ -211,6 +296,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
INIT_LIST_HEAD(&node->hole_stack);
list_add(&node->node_list, &hole_node->node_list);
+ BUG_ON(node->start < start);
+ BUG_ON(node->start < adj_start);
BUG_ON(node->start + node->size > adj_end);
BUG_ON(node->start + node->size > end);
@@ -222,32 +309,51 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
}
/**
- * Search for free space and insert a preallocated memory node. Returns
- * -ENOSPC if no suitable free area is available. This is for range
- * restricted allocations. The preallocated memory node must be cleared.
+ * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node
+ * @mm: drm_mm to allocate from
+ * @node: preallocate node to insert
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for this node
+ * @start: start of the allowed range for this node
+ * @end: end of the allowed range for this node
+ * @sflags: flags to fine-tune the allocation search
+ * @aflags: flags to fine-tune the allocation behavior
+ *
+ * The preallocated node must be cleared to 0.
+ *
+ * Returns:
+ * 0 on success, -ENOSPC if there's no suitable hole.
*/
int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
- unsigned long size, unsigned alignment, unsigned long color,
+ unsigned long size, unsigned alignment,
+ unsigned long color,
unsigned long start, unsigned long end,
- enum drm_mm_search_flags flags)
+ enum drm_mm_search_flags sflags,
+ enum drm_mm_allocator_flags aflags)
{
struct drm_mm_node *hole_node;
hole_node = drm_mm_search_free_in_range_generic(mm,
size, alignment, color,
- start, end, flags);
+ start, end, sflags);
if (!hole_node)
return -ENOSPC;
drm_mm_insert_helper_range(hole_node, node,
size, alignment, color,
- start, end);
+ start, end, aflags);
return 0;
}
EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
/**
- * Remove a memory node from the allocator.
+ * drm_mm_remove_node - Remove a memory node from the allocator.
+ * @node: drm_mm_node to remove
+ *
+ * This just removes a node from its drm_mm allocator. The node does not need to
+ * be cleared again before it can be re-inserted into this or any other drm_mm
+ * allocator. It is a bug to call this function on a un-allocated node.
*/
void drm_mm_remove_node(struct drm_mm_node *node)
{
@@ -315,7 +421,10 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
best = NULL;
best_size = ~0UL;
- drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
+ __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
+ flags & DRM_MM_SEARCH_BELOW) {
+ unsigned long hole_size = adj_end - adj_start;
+
if (mm->color_adjust) {
mm->color_adjust(entry, color, &adj_start, &adj_end);
if (adj_end <= adj_start)
@@ -328,9 +437,9 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
if (!(flags & DRM_MM_SEARCH_BEST))
return entry;
- if (entry->size < best_size) {
+ if (hole_size < best_size) {
best = entry;
- best_size = entry->size;
+ best_size = hole_size;
}
}
@@ -356,7 +465,10 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
best = NULL;
best_size = ~0UL;
- drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
+ __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
+ flags & DRM_MM_SEARCH_BELOW) {
+ unsigned long hole_size = adj_end - adj_start;
+
if (adj_start < start)
adj_start = start;
if (adj_end > end)
@@ -374,9 +486,9 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
if (!(flags & DRM_MM_SEARCH_BEST))
return entry;
- if (entry->size < best_size) {
+ if (hole_size < best_size) {
best = entry;
- best_size = entry->size;
+ best_size = hole_size;
}
}
@@ -384,7 +496,13 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
}
/**
- * Moves an allocation. To be used with embedded struct drm_mm_node.
+ * drm_mm_replace_node - move an allocation from @old to @new
+ * @old: drm_mm_node to remove from the allocator
+ * @new: drm_mm_node which should inherit @old's allocation
+ *
+ * This is useful for when drivers embed the drm_mm_node structure and hence
+ * can't move allocations by reassigning pointers. It's a combination of remove
+ * and insert with the guarantee that the allocation start will match.
*/
void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
{
@@ -402,12 +520,46 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
EXPORT_SYMBOL(drm_mm_replace_node);
/**
- * Initializa lru scanning.
+ * DOC: lru scan roaster
+ *
+ * Very often GPUs need to have continuous allocations for a given object. When
+ * evicting objects to make space for a new one it is therefore not most
+ * efficient when we simply start to select all objects from the tail of an LRU
+ * until there's a suitable hole: Especially for big objects or nodes that
+ * otherwise have special allocation constraints there's a good chance we evict
+ * lots of (smaller) objects unecessarily.
+ *
+ * The DRM range allocator supports this use-case through the scanning
+ * interfaces. First a scan operation needs to be initialized with
+ * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds
+ * objects to the roaster (probably by walking an LRU list, but this can be
+ * freely implemented) until a suitable hole is found or there's no further
+ * evitable object.
+ *
+ * The the driver must walk through all objects again in exactly the reverse
+ * order to restore the allocator state. Note that while the allocator is used
+ * in the scan mode no other operation is allowed.
+ *
+ * Finally the driver evicts all objects selected in the scan. Adding and
+ * removing an object is O(1), and since freeing a node is also O(1) the overall
+ * complexity is O(scanned_objects). So like the free stack which needs to be
+ * walked before a scan operation even begins this is linear in the number of
+ * objects. It doesn't seem to hurt badly.
+ */
+
+/**
+ * drm_mm_init_scan - initialize lru scanning
+ * @mm: drm_mm to scan
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for the allocation
*
* This simply sets up the scanning routines with the parameters for the desired
- * hole.
+ * hole. Note that there's no need to specify allocation flags, since they only
+ * change the place a node is allocated from within a suitable hole.
*
- * Warning: As long as the scan list is non-empty, no other operations than
+ * Warning:
+ * As long as the scan list is non-empty, no other operations than
* adding/removing nodes to/from the scan list are allowed.
*/
void drm_mm_init_scan(struct drm_mm *mm,
@@ -427,12 +579,20 @@ void drm_mm_init_scan(struct drm_mm *mm,
EXPORT_SYMBOL(drm_mm_init_scan);
/**
- * Initializa lru scanning.
+ * drm_mm_init_scan - initialize range-restricted lru scanning
+ * @mm: drm_mm to scan
+ * @size: size of the allocation
+ * @alignment: alignment of the allocation
+ * @color: opaque tag value to use for the allocation
+ * @start: start of the allowed range for the allocation
+ * @end: end of the allowed range for the allocation
*
* This simply sets up the scanning routines with the parameters for the desired
- * hole. This version is for range-restricted scans.
+ * hole. Note that there's no need to specify allocation flags, since they only
+ * change the place a node is allocated from within a suitable hole.
*
- * Warning: As long as the scan list is non-empty, no other operations than
+ * Warning:
+ * As long as the scan list is non-empty, no other operations than
* adding/removing nodes to/from the scan list are allowed.
*/
void drm_mm_init_scan_with_range(struct drm_mm *mm,
@@ -456,12 +616,16 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm,
EXPORT_SYMBOL(drm_mm_init_scan_with_range);
/**
+ * drm_mm_scan_add_block - add a node to the scan list
+ * @node: drm_mm_node to add
+ *
* Add a node to the scan list that might be freed to make space for the desired
* hole.
*
- * Returns non-zero, if a hole has been found, zero otherwise.
+ * Returns:
+ * True if a hole has been found, false otherwise.
*/
-int drm_mm_scan_add_block(struct drm_mm_node *node)
+bool drm_mm_scan_add_block(struct drm_mm_node *node)
{
struct drm_mm *mm = node->mm;
struct drm_mm_node *prev_node;
@@ -501,15 +665,16 @@ int drm_mm_scan_add_block(struct drm_mm_node *node)
mm->scan_size, mm->scan_alignment)) {
mm->scan_hit_start = hole_start;
mm->scan_hit_end = hole_end;
- return 1;
+ return true;
}
- return 0;
+ return false;
}
EXPORT_SYMBOL(drm_mm_scan_add_block);
/**
- * Remove a node from the scan list.
+ * drm_mm_scan_remove_block - remove a node from the scan list
+ * @node: drm_mm_node to remove
*
* Nodes _must_ be removed in the exact same order from the scan list as they
* have been added, otherwise the internal state of the memory manager will be
@@ -519,10 +684,11 @@ EXPORT_SYMBOL(drm_mm_scan_add_block);
* immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
* return the just freed block (because its at the top of the free_stack list).
*
- * Returns one if this block should be evicted, zero otherwise. Will always
- * return zero when no hole has been found.
+ * Returns:
+ * True if this block should be evicted, false otherwise. Will always
+ * return false when no hole has been found.
*/
-int drm_mm_scan_remove_block(struct drm_mm_node *node)
+bool drm_mm_scan_remove_block(struct drm_mm_node *node)
{
struct drm_mm *mm = node->mm;
struct drm_mm_node *prev_node;
@@ -543,7 +709,15 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node)
}
EXPORT_SYMBOL(drm_mm_scan_remove_block);
-int drm_mm_clean(struct drm_mm * mm)
+/**
+ * drm_mm_clean - checks whether an allocator is clean
+ * @mm: drm_mm allocator to check
+ *
+ * Returns:
+ * True if the allocator is completely free, false if there's still a node
+ * allocated in it.
+ */
+bool drm_mm_clean(struct drm_mm * mm)
{
struct list_head *head = &mm->head_node.node_list;
@@ -551,6 +725,14 @@ int drm_mm_clean(struct drm_mm * mm)
}
EXPORT_SYMBOL(drm_mm_clean);
+/**
+ * drm_mm_init - initialize a drm-mm allocator
+ * @mm: the drm_mm structure to initialize
+ * @start: start of the range managed by @mm
+ * @size: end of the range managed by @mm
+ *
+ * Note that @mm must be cleared to 0 before calling this function.
+ */
void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
{
INIT_LIST_HEAD(&mm->hole_stack);
@@ -572,6 +754,13 @@ void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
}
EXPORT_SYMBOL(drm_mm_init);
+/**
+ * drm_mm_takedown - clean up a drm_mm allocator
+ * @mm: drm_mm allocator to clean up
+ *
+ * Note that it is a bug to call this function on an allocator which is not
+ * clean.
+ */
void drm_mm_takedown(struct drm_mm * mm)
{
WARN(!list_empty(&mm->head_node.node_list),
@@ -597,6 +786,11 @@ static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry,
return 0;
}
+/**
+ * drm_mm_debug_table - dump allocator state to dmesg
+ * @mm: drm_mm allocator to dump
+ * @prefix: prefix to use for dumping to dmesg
+ */
void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
{
struct drm_mm_node *entry;
@@ -635,6 +829,11 @@ static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *en
return 0;
}
+/**
+ * drm_mm_dump_table - dump allocator state to a seq_file
+ * @m: seq_file to dump to
+ * @mm: drm_mm allocator to dump
+ */
int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
{
struct drm_mm_node *entry;
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index b0733153dfd2..8b410576fce4 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -37,15 +37,14 @@
#include <drm/drm_crtc.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
+#include <drm/drm_modes.h>
+
+#include "drm_crtc_internal.h"
/**
- * drm_mode_debug_printmodeline - debug print a mode
- * @dev: DRM device
+ * drm_mode_debug_printmodeline - print a mode to dmesg
* @mode: mode to print
*
- * LOCKING:
- * None.
- *
* Describe @mode using DRM_DEBUG.
*/
void drm_mode_debug_printmodeline(const struct drm_display_mode *mode)
@@ -61,18 +60,77 @@ void drm_mode_debug_printmodeline(const struct drm_display_mode *mode)
EXPORT_SYMBOL(drm_mode_debug_printmodeline);
/**
- * drm_cvt_mode -create a modeline based on CVT algorithm
+ * drm_mode_create - create a new display mode
* @dev: DRM device
- * @hdisplay: hdisplay size
- * @vdisplay: vdisplay size
- * @vrefresh : vrefresh rate
- * @reduced : Whether the GTF calculation is simplified
- * @interlaced:Whether the interlace is supported
*
- * LOCKING:
- * none.
+ * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it
+ * and return it.
*
- * return the modeline based on CVT algorithm
+ * Returns:
+ * Pointer to new mode on success, NULL on error.
+ */
+struct drm_display_mode *drm_mode_create(struct drm_device *dev)
+{
+ struct drm_display_mode *nmode;
+
+ nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+ if (!nmode)
+ return NULL;
+
+ if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
+ kfree(nmode);
+ return NULL;
+ }
+
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_create);
+
+/**
+ * drm_mode_destroy - remove a mode
+ * @dev: DRM device
+ * @mode: mode to remove
+ *
+ * Release @mode's unique ID, then free it @mode structure itself using kfree.
+ */
+void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ if (!mode)
+ return;
+
+ drm_mode_object_put(dev, &mode->base);
+
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_destroy);
+
+/**
+ * drm_mode_probed_add - add a mode to a connector's probed_mode list
+ * @connector: connector the new mode
+ * @mode: mode data
+ *
+ * Add @mode to @connector's probed_mode list for later use. This list should
+ * then in a second step get filtered and all the modes actually supported by
+ * the hardware moved to the @connector's modes list.
+ */
+void drm_mode_probed_add(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
+
+ list_add_tail(&mode->head, &connector->probed_modes);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_cvt_mode -create a modeline based on the CVT algorithm
+ * @dev: drm device
+ * @hdisplay: hdisplay size
+ * @vdisplay: vdisplay size
+ * @vrefresh: vrefresh rate
+ * @reduced: whether to use reduced blanking
+ * @interlaced: whether to compute an interlaced mode
+ * @margins: whether to add margins (borders)
*
* This function is called to generate the modeline based on CVT algorithm
* according to the hdisplay, vdisplay, vrefresh.
@@ -82,12 +140,17 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline);
*
* And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c.
* What I have done is to translate it by using integer calculation.
+ *
+ * Returns:
+ * The modeline based on the CVT algorithm stored in a drm_display_mode object.
+ * The display mode object is allocated with drm_mode_create(). Returns NULL
+ * when no mode could be allocated.
*/
-#define HV_FACTOR 1000
struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
int vdisplay, int vrefresh,
bool reduced, bool interlaced, bool margins)
{
+#define HV_FACTOR 1000
/* 1) top/bottom margin size (% of height) - default: 1.8, */
#define CVT_MARGIN_PERCENTAGE 18
/* 2) character cell horizontal granularity (pixels) - default 8 */
@@ -281,23 +344,25 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
EXPORT_SYMBOL(drm_cvt_mode);
/**
- * drm_gtf_mode_complex - create the modeline based on full GTF algorithm
- *
- * @dev :drm device
- * @hdisplay :hdisplay size
- * @vdisplay :vdisplay size
- * @vrefresh :vrefresh rate.
- * @interlaced :whether the interlace is supported
- * @margins :desired margin size
- * @GTF_[MCKJ] :extended GTF formula parameters
- *
- * LOCKING.
- * none.
- *
- * return the modeline based on full GTF algorithm.
+ * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm
+ * @dev: drm device
+ * @hdisplay: hdisplay size
+ * @vdisplay: vdisplay size
+ * @vrefresh: vrefresh rate.
+ * @interlaced: whether to compute an interlaced mode
+ * @margins: desired margin (borders) size
+ * @GTF_M: extended GTF formula parameters
+ * @GTF_2C: extended GTF formula parameters
+ * @GTF_K: extended GTF formula parameters
+ * @GTF_2J: extended GTF formula parameters
*
* GTF feature blocks specify C and J in multiples of 0.5, so we pass them
* in here multiplied by two. For a C of 40, pass in 80.
+ *
+ * Returns:
+ * The modeline based on the full GTF algorithm stored in a drm_display_mode object.
+ * The display mode object is allocated with drm_mode_create(). Returns NULL
+ * when no mode could be allocated.
*/
struct drm_display_mode *
drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,
@@ -467,17 +532,13 @@ drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,
EXPORT_SYMBOL(drm_gtf_mode_complex);
/**
- * drm_gtf_mode - create the modeline based on GTF algorithm
- *
- * @dev :drm device
- * @hdisplay :hdisplay size
- * @vdisplay :vdisplay size
- * @vrefresh :vrefresh rate.
- * @interlaced :whether the interlace is supported
- * @margins :whether the margin is supported
- *
- * LOCKING.
- * none.
+ * drm_gtf_mode - create the modeline based on the GTF algorithm
+ * @dev: drm device
+ * @hdisplay: hdisplay size
+ * @vdisplay: vdisplay size
+ * @vrefresh: vrefresh rate.
+ * @interlaced: whether to compute an interlaced mode
+ * @margins: desired margin (borders) size
*
* return the modeline based on GTF algorithm
*
@@ -496,19 +557,32 @@ EXPORT_SYMBOL(drm_gtf_mode_complex);
* C = 40
* K = 128
* J = 20
+ *
+ * Returns:
+ * The modeline based on the GTF algorithm stored in a drm_display_mode object.
+ * The display mode object is allocated with drm_mode_create(). Returns NULL
+ * when no mode could be allocated.
*/
struct drm_display_mode *
drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh,
- bool lace, int margins)
+ bool interlaced, int margins)
{
- return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, lace,
- margins, 600, 40 * 2, 128, 20 * 2);
+ return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh,
+ interlaced, margins,
+ 600, 40 * 2, 128, 20 * 2);
}
EXPORT_SYMBOL(drm_gtf_mode);
#ifdef CONFIG_VIDEOMODE_HELPERS
-int drm_display_mode_from_videomode(const struct videomode *vm,
- struct drm_display_mode *dmode)
+/**
+ * drm_display_mode_from_videomode - fill in @dmode using @vm,
+ * @vm: videomode structure to use as source
+ * @dmode: drm_display_mode structure to use as destination
+ *
+ * Fills out @dmode using the display mode specified in @vm.
+ */
+void drm_display_mode_from_videomode(const struct videomode *vm,
+ struct drm_display_mode *dmode)
{
dmode->hdisplay = vm->hactive;
dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
@@ -538,8 +612,6 @@ int drm_display_mode_from_videomode(const struct videomode *vm,
if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
dmode->flags |= DRM_MODE_FLAG_DBLCLK;
drm_mode_set_name(dmode);
-
- return 0;
}
EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
@@ -553,6 +625,9 @@ EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
* This function is expensive and should only be used, if only one mode is to be
* read from DT. To get multiple modes start with of_get_display_timings and
* work with that instead.
+ *
+ * Returns:
+ * 0 on success, a negative errno code when no of videomode node was found.
*/
int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode, int index)
@@ -580,10 +655,8 @@ EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
* drm_mode_set_name - set the name on a mode
* @mode: name will be set in this mode
*
- * LOCKING:
- * None.
- *
- * Set the name of @mode to a standard format.
+ * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay>
+ * with an optional 'i' suffix for interlaced modes.
*/
void drm_mode_set_name(struct drm_display_mode *mode)
{
@@ -595,54 +668,12 @@ void drm_mode_set_name(struct drm_display_mode *mode)
}
EXPORT_SYMBOL(drm_mode_set_name);
-/**
- * drm_mode_width - get the width of a mode
- * @mode: mode
- *
- * LOCKING:
- * None.
- *
- * Return @mode's width (hdisplay) value.
- *
- * FIXME: is this needed?
- *
- * RETURNS:
- * @mode->hdisplay
- */
-int drm_mode_width(const struct drm_display_mode *mode)
-{
- return mode->hdisplay;
-
-}
-EXPORT_SYMBOL(drm_mode_width);
-
-/**
- * drm_mode_height - get the height of a mode
- * @mode: mode
- *
- * LOCKING:
- * None.
- *
- * Return @mode's height (vdisplay) value.
- *
- * FIXME: is this needed?
- *
- * RETURNS:
- * @mode->vdisplay
- */
-int drm_mode_height(const struct drm_display_mode *mode)
-{
- return mode->vdisplay;
-}
-EXPORT_SYMBOL(drm_mode_height);
-
/** drm_mode_hsync - get the hsync of a mode
* @mode: mode
*
- * LOCKING:
- * None.
- *
- * Return @modes's hsync rate in kHz, rounded to the nearest int.
+ * Returns:
+ * @modes's hsync rate in kHz, rounded to the nearest integer. Calculates the
+ * value first if it is not yet set.
*/
int drm_mode_hsync(const struct drm_display_mode *mode)
{
@@ -666,17 +697,9 @@ EXPORT_SYMBOL(drm_mode_hsync);
* drm_mode_vrefresh - get the vrefresh of a mode
* @mode: mode
*
- * LOCKING:
- * None.
- *
- * Return @mode's vrefresh rate in Hz or calculate it if necessary.
- *
- * FIXME: why is this needed? shouldn't vrefresh be set already?
- *
- * RETURNS:
- * Vertical refresh rate. It will be the result of actual value plus 0.5.
- * If it is 70.288, it will return 70Hz.
- * If it is 59.6, it will return 60Hz.
+ * Returns:
+ * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the
+ * value first if it is not yet set.
*/
int drm_mode_vrefresh(const struct drm_display_mode *mode)
{
@@ -705,14 +728,11 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode)
EXPORT_SYMBOL(drm_mode_vrefresh);
/**
- * drm_mode_set_crtcinfo - set CRTC modesetting parameters
+ * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
* @p: mode
* @adjust_flags: a combination of adjustment flags
*
- * LOCKING:
- * None.
- *
- * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
+ * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
*
* - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
* interlaced modes.
@@ -780,15 +800,11 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
}
EXPORT_SYMBOL(drm_mode_set_crtcinfo);
-
/**
* drm_mode_copy - copy the mode
* @dst: mode to overwrite
* @src: mode to copy
*
- * LOCKING:
- * None.
- *
* Copy an existing mode into another mode, preserving the object id and
* list head of the destination mode.
*/
@@ -805,13 +821,14 @@ EXPORT_SYMBOL(drm_mode_copy);
/**
* drm_mode_duplicate - allocate and duplicate an existing mode
- * @m: mode to duplicate
- *
- * LOCKING:
- * None.
+ * @dev: drm_device to allocate the duplicated mode for
+ * @mode: mode to duplicate
*
* Just allocate a new mode, copy the existing mode into it, and return
* a pointer to it. Used to create new instances of established modes.
+ *
+ * Returns:
+ * Pointer to duplicated mode on success, NULL on error.
*/
struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
const struct drm_display_mode *mode)
@@ -833,12 +850,9 @@ EXPORT_SYMBOL(drm_mode_duplicate);
* @mode1: first mode
* @mode2: second mode
*
- * LOCKING:
- * None.
- *
* Check to see if @mode1 and @mode2 are equivalent.
*
- * RETURNS:
+ * Returns:
* True if the modes are equal, false otherwise.
*/
bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2)
@@ -864,13 +878,10 @@ EXPORT_SYMBOL(drm_mode_equal);
* @mode1: first mode
* @mode2: second mode
*
- * LOCKING:
- * None.
- *
* Check to see if @mode1 and @mode2 are equivalent, but
* don't check the pixel clocks nor the stereo layout.
*
- * RETURNS:
+ * Returns:
* True if the modes are equal, false otherwise.
*/
bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1,
@@ -900,25 +911,19 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo);
* @mode_list: list of modes to check
* @maxX: maximum width
* @maxY: maximum height
- * @maxPitch: max pitch
*
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
- * The DRM device (@dev) has size and pitch limits. Here we validate the
- * modes we probed for @dev against those limits and set their status as
- * necessary.
+ * This function is a helper which can be used to validate modes against size
+ * limitations of the DRM device/connector. If a mode is too big its status
+ * memeber is updated with the appropriate validation failure code. The list
+ * itself is not changed.
*/
void drm_mode_validate_size(struct drm_device *dev,
struct list_head *mode_list,
- int maxX, int maxY, int maxPitch)
+ int maxX, int maxY)
{
struct drm_display_mode *mode;
list_for_each_entry(mode, mode_list, head) {
- if (maxPitch > 0 && mode->hdisplay > maxPitch)
- mode->status = MODE_BAD_WIDTH;
-
if (maxX > 0 && mode->hdisplay > maxX)
mode->status = MODE_VIRTUAL_X;
@@ -934,12 +939,10 @@ EXPORT_SYMBOL(drm_mode_validate_size);
* @mode_list: list of modes to check
* @verbose: be verbose about it
*
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
- * Once mode list generation is complete, a caller can use this routine to
- * remove invalid modes from a mode list. If any of the modes have a
- * status other than %MODE_OK, they are removed from @mode_list and freed.
+ * This helper function can be used to prune a display mode list after
+ * validation has been completed. All modes who's status is not MODE_OK will be
+ * removed from the list, and if @verbose the status code and mode name is also
+ * printed to dmesg.
*/
void drm_mode_prune_invalid(struct drm_device *dev,
struct list_head *mode_list, bool verbose)
@@ -966,13 +969,10 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
* @lh_a: list_head for first mode
* @lh_b: list_head for second mode
*
- * LOCKING:
- * None.
- *
* Compare two modes, given by @lh_a and @lh_b, returning a value indicating
* which is better.
*
- * RETURNS:
+ * Returns:
* Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
* positive if @lh_b is better than @lh_a.
*/
@@ -1000,12 +1000,9 @@ static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head
/**
* drm_mode_sort - sort mode list
- * @mode_list: list to sort
+ * @mode_list: list of drm_display_mode structures to sort
*
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
- * Sort @mode_list by favorability, putting good modes first.
+ * Sort @mode_list by favorability, moving good modes to the head of the list.
*/
void drm_mode_sort(struct list_head *mode_list)
{
@@ -1017,13 +1014,12 @@ EXPORT_SYMBOL(drm_mode_sort);
* drm_mode_connector_list_update - update the mode list for the connector
* @connector: the connector to update
*
- * LOCKING:
- * Caller must hold a lock protecting @mode_list.
- *
* This moves the modes from the @connector probed_modes list
* to the actual mode list. It compares the probed mode against the current
- * list and only adds different modes. All modes unverified after this point
- * will be removed by the prune invalid modes.
+ * list and only adds different/new modes.
+ *
+ * This is just a helper functions doesn't validate any modes itself and also
+ * doesn't prune any invalid modes. Callers need to do that themselves.
*/
void drm_mode_connector_list_update(struct drm_connector *connector)
{
@@ -1031,6 +1027,8 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
struct drm_display_mode *pmode, *pt;
int found_it;
+ WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
+
list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
head) {
found_it = 0;
@@ -1056,17 +1054,25 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
EXPORT_SYMBOL(drm_mode_connector_list_update);
/**
- * drm_mode_parse_command_line_for_connector - parse command line for connector
- * @mode_option - per connector mode option
- * @connector - connector to parse line for
+ * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
+ * @mode_option: optional per connector mode option
+ * @connector: connector to parse modeline for
+ * @mode: preallocated drm_cmdline_mode structure to fill out
+ *
+ * This parses @mode_option command line modeline for modes and options to
+ * configure the connector. If @mode_option is NULL the default command line
+ * modeline in fb_mode_option will be parsed instead.
*
- * This parses the connector specific then generic command lines for
- * modes and options to configure the connector.
+ * This uses the same parameters as the fb modedb.c, except for an extra
+ * force-enable, force-enable-digital and force-disable bit at the end:
*
- * This uses the same parameters as the fb modedb.c, except for extra
* <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
*
- * enable/enable Digital/disable bit at the end
+ * The intermediate drm_cmdline_mode structure is required to store additional
+ * options from the command line modline like the force-enabel/disable flag.
+ *
+ * Returns:
+ * True if a valid modeline has been parsed, false otherwise.
*/
bool drm_mode_parse_command_line_for_connector(const char *mode_option,
struct drm_connector *connector,
@@ -1219,6 +1225,14 @@ done:
}
EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
+/**
+ * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode
+ * @dev: DRM device to create the new mode for
+ * @cmd: input command line modeline
+ *
+ * Returns:
+ * Pointer to converted mode on success, NULL on error.
+ */
struct drm_display_mode *
drm_mode_create_from_cmdline_mode(struct drm_device *dev,
struct drm_cmdline_mode *cmd)
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index f7af69bcf3f4..9c696a5ad74d 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -351,7 +351,7 @@ err_agp:
drm_pci_agp_destroy(dev);
pci_disable_device(pdev);
err_free:
- drm_dev_free(dev);
+ drm_dev_unref(dev);
return ret;
}
EXPORT_SYMBOL(drm_get_pci_dev);
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
new file mode 100644
index 000000000000..e768d35ff22e
--- /dev/null
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * DRM universal plane helper functions
+ *
+ * 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.
+ */
+
+#include <linux/list.h>
+#include <drm/drmP.h>
+#include <drm/drm_rect.h>
+
+#define SUBPIXEL_MASK 0xffff
+
+/*
+ * This is the minimal list of formats that seem to be safe for modeset use
+ * with all current DRM drivers. Most hardware can actually support more
+ * formats than this and drivers may specify a more accurate list when
+ * creating the primary plane. However drivers that still call
+ * drm_plane_init() will use this minimal format list as the default.
+ */
+const static uint32_t safe_modeset_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+};
+
+/*
+ * Returns the connectors currently associated with a CRTC. This function
+ * should be called twice: once with a NULL connector list to retrieve
+ * the list size, and once with the properly allocated list to be filled in.
+ */
+static int get_connectors_for_crtc(struct drm_crtc *crtc,
+ struct drm_connector **connector_list,
+ int num_connectors)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_connector *connector;
+ int count = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder && connector->encoder->crtc == crtc) {
+ if (connector_list != NULL && count < num_connectors)
+ *(connector_list++) = connector;
+
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * drm_primary_helper_update() - Helper for primary plane update
+ * @plane: plane object to update
+ * @crtc: owning CRTC of owning plane
+ * @fb: framebuffer to flip onto plane
+ * @crtc_x: x offset of primary plane on crtc
+ * @crtc_y: y offset of primary plane on crtc
+ * @crtc_w: width of primary plane rectangle on crtc
+ * @crtc_h: height of primary plane rectangle on crtc
+ * @src_x: x offset of @fb for panning
+ * @src_y: y offset of @fb for panning
+ * @src_w: width of source rectangle in @fb
+ * @src_h: height of source rectangle in @fb
+ *
+ * Provides a default plane update handler for primary planes. This is handler
+ * is called in response to a userspace SetPlane operation on the plane with a
+ * non-NULL framebuffer. We call the driver's modeset handler to update the
+ * framebuffer.
+ *
+ * SetPlane() on a primary plane of a disabled CRTC is not supported, and will
+ * return an error.
+ *
+ * Note that we make some assumptions about hardware limitations that may not be
+ * true for all hardware --
+ * 1) Primary plane cannot be repositioned.
+ * 2) Primary plane cannot be scaled.
+ * 3) Primary plane must cover the entire CRTC.
+ * 4) Subpixel positioning is not supported.
+ * Drivers for hardware that don't have these restrictions can provide their
+ * own implementation rather than using this helper.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct drm_mode_set set = {
+ .crtc = crtc,
+ .fb = fb,
+ .mode = &crtc->mode,
+ .x = src_x >> 16,
+ .y = src_y >> 16,
+ };
+ struct drm_rect dest = {
+ .x1 = crtc_x,
+ .y1 = crtc_y,
+ .x2 = crtc_x + crtc_w,
+ .y2 = crtc_y + crtc_h,
+ };
+ struct drm_rect clip = {
+ .x2 = crtc->mode.hdisplay,
+ .y2 = crtc->mode.vdisplay,
+ };
+ struct drm_connector **connector_list;
+ struct drm_framebuffer *tmpfb;
+ int num_connectors, ret;
+
+ if (!crtc->enabled) {
+ DRM_DEBUG_KMS("Cannot update primary plane of a disabled CRTC.\n");
+ return -EINVAL;
+ }
+
+ /* Disallow subpixel positioning */
+ if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) {
+ DRM_DEBUG_KMS("Primary plane does not support subpixel positioning\n");
+ return -EINVAL;
+ }
+
+ /* Primary planes are locked to their owning CRTC */
+ if (plane->possible_crtcs != drm_crtc_mask(crtc)) {
+ DRM_DEBUG_KMS("Cannot change primary plane CRTC\n");
+ return -EINVAL;
+ }
+
+ /* Disallow scaling */
+ if (crtc_w != src_w || crtc_h != src_h) {
+ DRM_DEBUG_KMS("Can't scale primary plane\n");
+ return -EINVAL;
+ }
+
+ /* Make sure primary plane covers entire CRTC */
+ drm_rect_intersect(&dest, &clip);
+ if (dest.x1 != 0 || dest.y1 != 0 ||
+ dest.x2 != crtc->mode.hdisplay || dest.y2 != crtc->mode.vdisplay) {
+ DRM_DEBUG_KMS("Primary plane must cover entire CRTC\n");
+ return -EINVAL;
+ }
+
+ /* Framebuffer must be big enough to cover entire plane */
+ ret = drm_crtc_check_viewport(crtc, crtc_x, crtc_y, &crtc->mode, fb);
+ if (ret)
+ return ret;
+
+ /* Find current connectors for CRTC */
+ num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
+ BUG_ON(num_connectors == 0);
+ connector_list = kzalloc(num_connectors * sizeof(*connector_list),
+ GFP_KERNEL);
+ if (!connector_list)
+ return -ENOMEM;
+ get_connectors_for_crtc(crtc, connector_list, num_connectors);
+
+ set.connectors = connector_list;
+ set.num_connectors = num_connectors;
+
+ /*
+ * set_config() adjusts crtc->primary->fb; however the DRM setplane
+ * code that called us expects to handle the framebuffer update and
+ * reference counting; save and restore the current fb before
+ * calling it.
+ *
+ * N.B., we call set_config() directly here rather than using
+ * drm_mode_set_config_internal. We're reprogramming the same
+ * connectors that were already in use, so we shouldn't need the extra
+ * cross-CRTC fb refcounting to accomodate stealing connectors.
+ * drm_mode_setplane() already handles the basic refcounting for the
+ * framebuffers involved in this operation.
+ */
+ tmpfb = plane->fb;
+ ret = crtc->funcs->set_config(&set);
+ plane->fb = tmpfb;
+
+ kfree(connector_list);
+ return ret;
+}
+EXPORT_SYMBOL(drm_primary_helper_update);
+
+/**
+ * drm_primary_helper_disable() - Helper for primary plane disable
+ * @plane: plane to disable
+ *
+ * Provides a default plane disable handler for primary planes. This is handler
+ * is called in response to a userspace SetPlane operation on the plane with a
+ * NULL framebuffer parameter. We call the driver's modeset handler with a NULL
+ * framebuffer to disable the CRTC if no other planes are currently enabled.
+ * If other planes are still enabled on the same CRTC, we return -EBUSY.
+ *
+ * Note that some hardware may be able to disable the primary plane without
+ * disabling the whole CRTC. Drivers for such hardware should provide their
+ * own disable handler that disables just the primary plane (and they'll likely
+ * need to provide their own update handler as well to properly re-enable a
+ * disabled primary plane).
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+int drm_primary_helper_disable(struct drm_plane *plane)
+{
+ struct drm_plane *p;
+ struct drm_mode_set set = {
+ .crtc = plane->crtc,
+ .fb = NULL,
+ };
+
+ if (plane->crtc == NULL || plane->fb == NULL)
+ /* Already disabled */
+ return 0;
+
+ list_for_each_entry(p, &plane->dev->mode_config.plane_list, head)
+ if (p != plane && p->fb) {
+ DRM_DEBUG_KMS("Cannot disable primary plane while other planes are still active on CRTC.\n");
+ return -EBUSY;
+ }
+
+ /*
+ * N.B. We call set_config() directly here rather than
+ * drm_mode_set_config_internal() since drm_mode_setplane() already
+ * handles the basic refcounting and we don't need the special
+ * cross-CRTC refcounting (no chance of stealing connectors from
+ * other CRTC's with this update).
+ */
+ return plane->crtc->funcs->set_config(&set);
+}
+EXPORT_SYMBOL(drm_primary_helper_disable);
+
+/**
+ * drm_primary_helper_destroy() - Helper for primary plane destruction
+ * @plane: plane to destroy
+ *
+ * Provides a default plane destroy handler for primary planes. This handler
+ * is called during CRTC destruction. We disable the primary plane, remove
+ * it from the DRM plane list, and deallocate the plane structure.
+ */
+void drm_primary_helper_destroy(struct drm_plane *plane)
+{
+ plane->funcs->disable_plane(plane);
+ drm_plane_cleanup(plane);
+ kfree(plane);
+}
+EXPORT_SYMBOL(drm_primary_helper_destroy);
+
+const struct drm_plane_funcs drm_primary_helper_funcs = {
+ .update_plane = drm_primary_helper_update,
+ .disable_plane = drm_primary_helper_disable,
+ .destroy = drm_primary_helper_destroy,
+};
+EXPORT_SYMBOL(drm_primary_helper_funcs);
+
+/**
+ * drm_primary_helper_create_plane() - Create a generic primary plane
+ * @dev: drm device
+ * @formats: pixel formats supported, or NULL for a default safe list
+ * @num_formats: size of @formats; ignored if @formats is NULL
+ *
+ * Allocates and initializes a primary plane that can be used with the primary
+ * plane helpers. Drivers that wish to use driver-specific plane structures or
+ * provide custom handler functions may perform their own allocation and
+ * initialization rather than calling this function.
+ */
+struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
+ const uint32_t *formats,
+ int num_formats)
+{
+ struct drm_plane *primary;
+ int ret;
+
+ primary = kzalloc(sizeof(*primary), GFP_KERNEL);
+ if (primary == NULL) {
+ DRM_DEBUG_KMS("Failed to allocate primary plane\n");
+ return NULL;
+ }
+
+ if (formats == NULL) {
+ formats = safe_modeset_formats;
+ num_formats = ARRAY_SIZE(safe_modeset_formats);
+ }
+
+ /* possible_crtc's will be filled in later by crtc_init */
+ ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs,
+ formats, num_formats,
+ DRM_PLANE_TYPE_PRIMARY);
+ if (ret) {
+ kfree(primary);
+ primary = NULL;
+ }
+
+ return primary;
+}
+EXPORT_SYMBOL(drm_primary_helper_create_plane);
+
+/**
+ * drm_crtc_init - Legacy CRTC initialization function
+ * @dev: DRM device
+ * @crtc: CRTC object to init
+ * @funcs: callbacks for the new CRTC
+ *
+ * Initialize a CRTC object with a default helper-provided primary plane and no
+ * cursor plane.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+ const struct drm_crtc_funcs *funcs)
+{
+ struct drm_plane *primary;
+
+ primary = drm_primary_helper_create_plane(dev, NULL, 0);
+ return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs);
+}
+EXPORT_SYMBOL(drm_crtc_init);
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 21fc82006b78..319ff5385601 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -64,7 +64,7 @@ static int drm_get_platform_dev(struct platform_device *platdev,
return 0;
err_free:
- drm_dev_free(dev);
+ drm_dev_unref(dev);
return ret;
}
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index bb516fdd195d..304ca8cacbc4 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -68,7 +68,8 @@ struct drm_prime_attachment {
enum dma_data_direction dir;
};
-static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle)
+static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv,
+ struct dma_buf *dma_buf, uint32_t handle)
{
struct drm_prime_member *member;
@@ -174,7 +175,7 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr
}
static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
- enum dma_data_direction dir)
+ enum dma_data_direction dir)
{
struct drm_prime_attachment *prime_attach = attach->priv;
struct drm_gem_object *obj = attach->dmabuf->priv;
@@ -211,11 +212,19 @@ static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
}
static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
- struct sg_table *sgt, enum dma_data_direction dir)
+ struct sg_table *sgt,
+ enum dma_data_direction dir)
{
/* nothing to be done here */
}
+/**
+ * drm_gem_dmabuf_release - dma_buf release implementation for GEM
+ * @dma_buf: buffer to be released
+ *
+ * Generic release function for dma_bufs exported as PRIME buffers. GEM drivers
+ * must use this in their dma_buf ops structure as the release callback.
+ */
void drm_gem_dmabuf_release(struct dma_buf *dma_buf)
{
struct drm_gem_object *obj = dma_buf->priv;
@@ -242,30 +251,30 @@ static void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
}
static void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
- unsigned long page_num)
+ unsigned long page_num)
{
return NULL;
}
static void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
- unsigned long page_num, void *addr)
+ unsigned long page_num, void *addr)
{
}
static void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf,
- unsigned long page_num)
+ unsigned long page_num)
{
return NULL;
}
static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
- unsigned long page_num, void *addr)
+ unsigned long page_num, void *addr)
{
}
static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf,
- struct vm_area_struct *vma)
+ struct vm_area_struct *vma)
{
struct drm_gem_object *obj = dma_buf->priv;
struct drm_device *dev = obj->dev;
@@ -315,6 +324,15 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
* driver's scatter/gather table
*/
+/**
+ * drm_gem_prime_export - helper library implemention of the export callback
+ * @dev: drm_device to export from
+ * @obj: GEM object to export
+ * @flags: flags like DRM_CLOEXEC
+ *
+ * This is the implementation of the gem_prime_export functions for GEM drivers
+ * using the PRIME helpers.
+ */
struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *obj, int flags)
{
@@ -355,9 +373,23 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev,
return dmabuf;
}
+/**
+ * drm_gem_prime_handle_to_fd - PRIME export function for GEM drivers
+ * @dev: dev to export the buffer from
+ * @file_priv: drm file-private structure
+ * @handle: buffer handle to export
+ * @flags: flags like DRM_CLOEXEC
+ * @prime_fd: pointer to storage for the fd id of the create dma-buf
+ *
+ * This is the PRIME export function which must be used mandatorily by GEM
+ * drivers to ensure correct lifetime management of the underlying GEM object.
+ * The actual exporting from GEM object to a dma-buf is done through the
+ * gem_prime_export driver callback.
+ */
int drm_gem_prime_handle_to_fd(struct drm_device *dev,
- struct drm_file *file_priv, uint32_t handle, uint32_t flags,
- int *prime_fd)
+ struct drm_file *file_priv, uint32_t handle,
+ uint32_t flags,
+ int *prime_fd)
{
struct drm_gem_object *obj;
int ret = 0;
@@ -441,6 +473,14 @@ out_unlock:
}
EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
+/**
+ * drm_gem_prime_import - helper library implemention of the import callback
+ * @dev: drm_device to import into
+ * @dma_buf: dma-buf object to import
+ *
+ * This is the implementation of the gem_prime_import functions for GEM drivers
+ * using the PRIME helpers.
+ */
struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf)
{
@@ -496,8 +536,21 @@ fail_detach:
}
EXPORT_SYMBOL(drm_gem_prime_import);
+/**
+ * drm_gem_prime_fd_to_handle - PRIME import function for GEM drivers
+ * @dev: dev to export the buffer from
+ * @file_priv: drm file-private structure
+ * @prime_fd: fd id of the dma-buf which should be imported
+ * @handle: pointer to storage for the handle of the imported buffer object
+ *
+ * This is the PRIME import function which must be used mandatorily by GEM
+ * drivers to ensure correct lifetime management of the underlying GEM object.
+ * The actual importing of GEM object from the dma-buf is done through the
+ * gem_import_export driver callback.
+ */
int drm_gem_prime_fd_to_handle(struct drm_device *dev,
- struct drm_file *file_priv, int prime_fd, uint32_t *handle)
+ struct drm_file *file_priv, int prime_fd,
+ uint32_t *handle)
{
struct dma_buf *dma_buf;
struct drm_gem_object *obj;
@@ -598,12 +651,14 @@ int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data,
args->fd, &args->handle);
}
-/*
- * drm_prime_pages_to_sg
+/**
+ * drm_prime_pages_to_sg - converts a page array into an sg list
+ * @pages: pointer to the array of page pointers to convert
+ * @nr_pages: length of the page vector
*
- * this helper creates an sg table object from a set of pages
+ * This helper creates an sg table object from a set of pages
* the driver is responsible for mapping the pages into the
- * importers address space
+ * importers address space for use with dma_buf itself.
*/
struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages)
{
@@ -628,9 +683,16 @@ out:
}
EXPORT_SYMBOL(drm_prime_pages_to_sg);
-/* export an sg table into an array of pages and addresses
- this is currently required by the TTM driver in order to do correct fault
- handling */
+/**
+ * drm_prime_sg_to_page_addr_arrays - convert an sg table into a page array
+ * @sgt: scatter-gather table to convert
+ * @pages: array of page pointers to store the page array in
+ * @addrs: optional array to store the dma bus address of each page
+ * @max_pages: size of both the passed-in arrays
+ *
+ * Exports an sg table into an array of pages and addresses. This is currently
+ * required by the TTM driver in order to do correct fault handling.
+ */
int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages,
dma_addr_t *addrs, int max_pages)
{
@@ -663,7 +725,15 @@ int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages,
return 0;
}
EXPORT_SYMBOL(drm_prime_sg_to_page_addr_arrays);
-/* helper function to cleanup a GEM/prime object */
+
+/**
+ * drm_prime_gem_destroy - helper to clean up a PRIME-imported GEM object
+ * @obj: GEM object which was created from a dma-buf
+ * @sg: the sg-table which was pinned at import time
+ *
+ * This is the cleanup functions which GEM drivers need to call when they use
+ * @drm_gem_prime_import to import dma-bufs.
+ */
void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg)
{
struct dma_buf_attachment *attach;
@@ -683,11 +753,9 @@ void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv)
INIT_LIST_HEAD(&prime_fpriv->head);
mutex_init(&prime_fpriv->lock);
}
-EXPORT_SYMBOL(drm_prime_init_file_private);
void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv)
{
/* by now drm_gem_release should've made sure the list is empty */
WARN_ON(!list_empty(&prime_fpriv->head));
}
-EXPORT_SYMBOL(drm_prime_destroy_file_private);
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 98a33c580ca1..4c24c3ac1efa 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -31,8 +31,10 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/mount.h>
#include <linux/slab.h>
#include <drm/drmP.h>
#include <drm/drm_core.h>
@@ -43,6 +45,10 @@ EXPORT_SYMBOL(drm_debug);
unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */
EXPORT_SYMBOL(drm_rnodes);
+/* 1 to allow user space to request universal planes (experimental) */
+unsigned int drm_universal_planes = 0;
+EXPORT_SYMBOL(drm_universal_planes);
+
unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
EXPORT_SYMBOL(drm_vblank_offdelay);
@@ -66,10 +72,12 @@ MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
module_param_named(debug, drm_debug, int, 0600);
module_param_named(rnodes, drm_rnodes, int, 0600);
+module_param_named(universal_planes, drm_universal_planes, int, 0600);
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
+static DEFINE_SPINLOCK(drm_minor_lock);
struct idr drm_minors_idr;
struct class *drm_class;
@@ -94,48 +102,20 @@ int drm_err(const char *func, const char *format, ...)
}
EXPORT_SYMBOL(drm_err);
-void drm_ut_debug_printk(unsigned int request_level,
- const char *prefix,
- const char *function_name,
- const char *format, ...)
+void drm_ut_debug_printk(const char *function_name, const char *format, ...)
{
struct va_format vaf;
va_list args;
- if (drm_debug & request_level) {
- va_start(args, format);
- vaf.fmt = format;
- vaf.va = &args;
-
- if (function_name)
- printk(KERN_DEBUG "[%s:%s], %pV", prefix,
- function_name, &vaf);
- else
- printk(KERN_DEBUG "%pV", &vaf);
- va_end(args);
- }
-}
-EXPORT_SYMBOL(drm_ut_debug_printk);
-
-static int drm_minor_get_id(struct drm_device *dev, int type)
-{
- int ret;
- int base = 0, limit = 63;
-
- if (type == DRM_MINOR_CONTROL) {
- base += 64;
- limit = base + 63;
- } else if (type == DRM_MINOR_RENDER) {
- base += 128;
- limit = base + 63;
- }
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
- mutex_lock(&dev->struct_mutex);
- ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL);
- mutex_unlock(&dev->struct_mutex);
+ printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf);
- return ret == -ENOSPC ? -EINVAL : ret;
+ va_end(args);
}
+EXPORT_SYMBOL(drm_ut_debug_printk);
struct drm_master *drm_master_create(struct drm_minor *minor)
{
@@ -152,8 +132,6 @@ struct drm_master *drm_master_create(struct drm_minor *minor)
INIT_LIST_HEAD(&master->magicfree);
master->minor = minor;
- list_add_tail(&master->head, &minor->master_list);
-
return master;
}
@@ -171,8 +149,7 @@ static void drm_master_destroy(struct kref *kref)
struct drm_device *dev = master->minor->dev;
struct drm_map_list *r_list, *list_temp;
- list_del(&master->head);
-
+ mutex_lock(&dev->struct_mutex);
if (dev->driver->master_destroy)
dev->driver->master_destroy(dev, master);
@@ -200,6 +177,7 @@ static void drm_master_destroy(struct kref *kref)
drm_ht_remove(&master->magiclist);
+ mutex_unlock(&dev->struct_mutex);
kfree(master);
}
@@ -215,19 +193,20 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
{
int ret = 0;
+ mutex_lock(&dev->master_mutex);
if (file_priv->is_master)
- return 0;
-
- if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
- return -EINVAL;
+ goto out_unlock;
- if (!file_priv->master)
- return -EINVAL;
+ if (file_priv->minor->master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
- if (file_priv->minor->master)
- return -EINVAL;
+ if (!file_priv->master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
- mutex_lock(&dev->struct_mutex);
file_priv->minor->master = drm_master_get(file_priv->master);
file_priv->is_master = 1;
if (dev->driver->master_set) {
@@ -237,142 +216,211 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
drm_master_put(&file_priv->minor->master);
}
}
- mutex_unlock(&dev->struct_mutex);
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
return ret;
}
int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
+ int ret = -EINVAL;
+
+ mutex_lock(&dev->master_mutex);
if (!file_priv->is_master)
- return -EINVAL;
+ goto out_unlock;
if (!file_priv->minor->master)
- return -EINVAL;
+ goto out_unlock;
- mutex_lock(&dev->struct_mutex);
+ ret = 0;
if (dev->driver->master_drop)
dev->driver->master_drop(dev, file_priv, false);
drm_master_put(&file_priv->minor->master);
file_priv->is_master = 0;
- mutex_unlock(&dev->struct_mutex);
- return 0;
+
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
+ return ret;
}
-/**
- * drm_get_minor - Allocate and register new DRM minor
- * @dev: DRM device
- * @minor: Pointer to where new minor is stored
- * @type: Type of minor
- *
- * Allocate a new minor of the given type and register it. A pointer to the new
- * minor is returned in @minor.
- * Caller must hold the global DRM mutex.
+/*
+ * DRM Minors
+ * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
+ * of them is represented by a drm_minor object. Depending on the capabilities
+ * of the device-driver, different interfaces are registered.
*
- * RETURNS:
- * 0 on success, negative error code on failure.
+ * Minors can be accessed via dev->$minor_name. This pointer is either
+ * NULL or a valid drm_minor pointer and stays valid as long as the device is
+ * valid. This means, DRM minors have the same life-time as the underlying
+ * device. However, this doesn't mean that the minor is active. Minors are
+ * registered and unregistered dynamically according to device-state.
*/
-static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor,
- int type)
+
+static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
+ unsigned int type)
+{
+ switch (type) {
+ case DRM_MINOR_LEGACY:
+ return &dev->primary;
+ case DRM_MINOR_RENDER:
+ return &dev->render;
+ case DRM_MINOR_CONTROL:
+ return &dev->control;
+ default:
+ return NULL;
+ }
+}
+
+static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
+{
+ struct drm_minor *minor;
+
+ minor = kzalloc(sizeof(*minor), GFP_KERNEL);
+ if (!minor)
+ return -ENOMEM;
+
+ minor->type = type;
+ minor->dev = dev;
+
+ *drm_minor_get_slot(dev, type) = minor;
+ return 0;
+}
+
+static void drm_minor_free(struct drm_device *dev, unsigned int type)
+{
+ struct drm_minor **slot;
+
+ slot = drm_minor_get_slot(dev, type);
+ if (*slot) {
+ kfree(*slot);
+ *slot = NULL;
+ }
+}
+
+static int drm_minor_register(struct drm_device *dev, unsigned int type)
{
struct drm_minor *new_minor;
+ unsigned long flags;
int ret;
int minor_id;
DRM_DEBUG("\n");
- minor_id = drm_minor_get_id(dev, type);
+ new_minor = *drm_minor_get_slot(dev, type);
+ if (!new_minor)
+ return 0;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ minor_id = idr_alloc(&drm_minors_idr,
+ NULL,
+ 64 * type,
+ 64 * (type + 1),
+ GFP_NOWAIT);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+ idr_preload_end();
+
if (minor_id < 0)
return minor_id;
- new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
- if (!new_minor) {
- ret = -ENOMEM;
- goto err_idr;
- }
-
- new_minor->type = type;
- new_minor->device = MKDEV(DRM_MAJOR, minor_id);
- new_minor->dev = dev;
new_minor->index = minor_id;
- INIT_LIST_HEAD(&new_minor->master_list);
-
- idr_replace(&drm_minors_idr, new_minor, minor_id);
-#if defined(CONFIG_DEBUG_FS)
ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root);
if (ret) {
DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
- goto err_mem;
+ goto err_id;
}
-#endif
ret = drm_sysfs_device_add(new_minor);
if (ret) {
- printk(KERN_ERR
- "DRM: Error sysfs_device_add.\n");
+ DRM_ERROR("DRM: Error sysfs_device_add.\n");
goto err_debugfs;
}
- *minor = new_minor;
+
+ /* replace NULL with @minor so lookups will succeed from now on */
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ idr_replace(&drm_minors_idr, new_minor, new_minor->index);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
DRM_DEBUG("new minor assigned %d\n", minor_id);
return 0;
-
err_debugfs:
-#if defined(CONFIG_DEBUG_FS)
drm_debugfs_cleanup(new_minor);
-err_mem:
-#endif
- kfree(new_minor);
-err_idr:
+err_id:
+ spin_lock_irqsave(&drm_minor_lock, flags);
idr_remove(&drm_minors_idr, minor_id);
- *minor = NULL;
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+ new_minor->index = 0;
return ret;
}
-/**
- * drm_unplug_minor - Unplug DRM minor
- * @minor: Minor to unplug
- *
- * Unplugs the given DRM minor but keeps the object. So after this returns,
- * minor->dev is still valid so existing open-files can still access it to get
- * device information from their drm_file ojects.
- * If the minor is already unplugged or if @minor is NULL, nothing is done.
- * The global DRM mutex must be held by the caller.
- */
-static void drm_unplug_minor(struct drm_minor *minor)
+static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
{
+ struct drm_minor *minor;
+ unsigned long flags;
+
+ minor = *drm_minor_get_slot(dev, type);
if (!minor || !minor->kdev)
return;
-#if defined(CONFIG_DEBUG_FS)
- drm_debugfs_cleanup(minor);
-#endif
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ idr_remove(&drm_minors_idr, minor->index);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+ minor->index = 0;
+ drm_debugfs_cleanup(minor);
drm_sysfs_device_remove(minor);
- idr_remove(&drm_minors_idr, minor->index);
}
/**
- * drm_put_minor - Destroy DRM minor
- * @minor: Minor to destroy
+ * drm_minor_acquire - Acquire a DRM minor
+ * @minor_id: Minor ID of the DRM-minor
+ *
+ * Looks up the given minor-ID and returns the respective DRM-minor object. The
+ * refence-count of the underlying device is increased so you must release this
+ * object with drm_minor_release().
*
- * This calls drm_unplug_minor() on the given minor and then frees it. Nothing
- * is done if @minor is NULL. It is fine to call this on already unplugged
- * minors.
- * The global DRM mutex must be held by the caller.
+ * As long as you hold this minor, it is guaranteed that the object and the
+ * minor->dev pointer will stay valid! However, the device may get unplugged and
+ * unregistered while you hold the minor.
+ *
+ * Returns:
+ * Pointer to minor-object with increased device-refcount, or PTR_ERR on
+ * failure.
*/
-static void drm_put_minor(struct drm_minor *minor)
+struct drm_minor *drm_minor_acquire(unsigned int minor_id)
{
- if (!minor)
- return;
+ struct drm_minor *minor;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ minor = idr_find(&drm_minors_idr, minor_id);
+ if (minor)
+ drm_dev_ref(minor->dev);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+ if (!minor) {
+ return ERR_PTR(-ENODEV);
+ } else if (drm_device_is_unplugged(minor->dev)) {
+ drm_dev_unref(minor->dev);
+ return ERR_PTR(-ENODEV);
+ }
- DRM_DEBUG("release secondary minor %d\n", minor->index);
+ return minor;
+}
- drm_unplug_minor(minor);
- kfree(minor);
+/**
+ * drm_minor_release - Release DRM minor
+ * @minor: Pointer to DRM minor object
+ *
+ * Release a minor that was previously acquired via drm_minor_acquire().
+ */
+void drm_minor_release(struct drm_minor *minor)
+{
+ drm_dev_unref(minor->dev);
}
/**
@@ -392,18 +440,16 @@ void drm_put_dev(struct drm_device *dev)
}
drm_dev_unregister(dev);
- drm_dev_free(dev);
+ drm_dev_unref(dev);
}
EXPORT_SYMBOL(drm_put_dev);
void drm_unplug_dev(struct drm_device *dev)
{
/* for a USB device */
- if (drm_core_check_feature(dev, DRIVER_MODESET))
- drm_unplug_minor(dev->control);
- if (dev->render)
- drm_unplug_minor(dev->render);
- drm_unplug_minor(dev->primary);
+ drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+ drm_minor_unregister(dev, DRM_MINOR_RENDER);
+ drm_minor_unregister(dev, DRM_MINOR_CONTROL);
mutex_lock(&drm_global_mutex);
@@ -416,6 +462,78 @@ void drm_unplug_dev(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_unplug_dev);
+/*
+ * DRM internal mount
+ * We want to be able to allocate our own "struct address_space" to control
+ * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
+ * stand-alone address_space objects, so we need an underlying inode. As there
+ * is no way to allocate an independent inode easily, we need a fake internal
+ * VFS mount-point.
+ *
+ * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
+ * frees it again. You are allowed to use iget() and iput() to get references to
+ * the inode. But each drm_fs_inode_new() call must be paired with exactly one
+ * drm_fs_inode_free() call (which does not have to be the last iput()).
+ * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
+ * between multiple inode-users. You could, technically, call
+ * iget() + drm_fs_inode_free() directly after alloc and sometime later do an
+ * iput(), but this way you'd end up with a new vfsmount for each inode.
+ */
+
+static int drm_fs_cnt;
+static struct vfsmount *drm_fs_mnt;
+
+static const struct dentry_operations drm_fs_dops = {
+ .d_dname = simple_dname,
+};
+
+static const struct super_operations drm_fs_sops = {
+ .statfs = simple_statfs,
+};
+
+static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data)
+{
+ return mount_pseudo(fs_type,
+ "drm:",
+ &drm_fs_sops,
+ &drm_fs_dops,
+ 0x010203ff);
+}
+
+static struct file_system_type drm_fs_type = {
+ .name = "drm",
+ .owner = THIS_MODULE,
+ .mount = drm_fs_mount,
+ .kill_sb = kill_anon_super,
+};
+
+static struct inode *drm_fs_inode_new(void)
+{
+ struct inode *inode;
+ int r;
+
+ r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
+ if (r < 0) {
+ DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
+ return ERR_PTR(r);
+ }
+
+ inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
+ if (IS_ERR(inode))
+ simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
+
+ return inode;
+}
+
+static void drm_fs_inode_free(struct inode *inode)
+{
+ if (inode) {
+ iput(inode);
+ simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
+ }
+}
+
/**
* drm_dev_alloc - Allocate new drm device
* @driver: DRM driver to allocate device for
@@ -425,6 +543,9 @@ EXPORT_SYMBOL(drm_unplug_dev);
* Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems.
*
+ * The initial ref-count of the object is 1. Use drm_dev_ref() and
+ * drm_dev_unref() to take and drop further ref-counts.
+ *
* RETURNS:
* Pointer to new DRM device, or NULL if out of memory.
*/
@@ -438,6 +559,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
if (!dev)
return NULL;
+ kref_init(&dev->ref);
dev->dev = parent;
dev->driver = driver;
@@ -451,9 +573,33 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
+ mutex_init(&dev->master_mutex);
- if (drm_ht_create(&dev->map_hash, 12))
+ dev->anon_inode = drm_fs_inode_new();
+ if (IS_ERR(dev->anon_inode)) {
+ ret = PTR_ERR(dev->anon_inode);
+ DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
goto err_free;
+ }
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
+ if (ret)
+ goto err_minors;
+ }
+
+ if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
+ ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_minors;
+ }
+
+ ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY);
+ if (ret)
+ goto err_minors;
+
+ if (drm_ht_create(&dev->map_hash, 12))
+ goto err_minors;
ret = drm_ctxbitmap_init(dev);
if (ret) {
@@ -475,38 +621,71 @@ err_ctxbitmap:
drm_ctxbitmap_cleanup(dev);
err_ht:
drm_ht_remove(&dev->map_hash);
+err_minors:
+ drm_minor_free(dev, DRM_MINOR_LEGACY);
+ drm_minor_free(dev, DRM_MINOR_RENDER);
+ drm_minor_free(dev, DRM_MINOR_CONTROL);
+ drm_fs_inode_free(dev->anon_inode);
err_free:
+ mutex_destroy(&dev->master_mutex);
kfree(dev);
return NULL;
}
EXPORT_SYMBOL(drm_dev_alloc);
-/**
- * drm_dev_free - Free DRM device
- * @dev: DRM device to free
- *
- * Free a DRM device that has previously been allocated via drm_dev_alloc().
- * You must not use kfree() instead or you will leak memory.
- *
- * This must not be called once the device got registered. Use drm_put_dev()
- * instead, which then calls drm_dev_free().
- */
-void drm_dev_free(struct drm_device *dev)
+static void drm_dev_release(struct kref *ref)
{
- drm_put_minor(dev->control);
- drm_put_minor(dev->render);
- drm_put_minor(dev->primary);
+ struct drm_device *dev = container_of(ref, struct drm_device, ref);
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_destroy(dev);
drm_ctxbitmap_cleanup(dev);
drm_ht_remove(&dev->map_hash);
+ drm_fs_inode_free(dev->anon_inode);
+
+ drm_minor_free(dev, DRM_MINOR_LEGACY);
+ drm_minor_free(dev, DRM_MINOR_RENDER);
+ drm_minor_free(dev, DRM_MINOR_CONTROL);
kfree(dev->devname);
+
+ mutex_destroy(&dev->master_mutex);
kfree(dev);
}
-EXPORT_SYMBOL(drm_dev_free);
+
+/**
+ * drm_dev_ref - Take reference of a DRM device
+ * @dev: device to take reference of or NULL
+ *
+ * This increases the ref-count of @dev by one. You *must* already own a
+ * reference when calling this. Use drm_dev_unref() to drop this reference
+ * again.
+ *
+ * This function never fails. However, this function does not provide *any*
+ * guarantee whether the device is alive or running. It only provides a
+ * reference to the object and the memory associated with it.
+ */
+void drm_dev_ref(struct drm_device *dev)
+{
+ if (dev)
+ kref_get(&dev->ref);
+}
+EXPORT_SYMBOL(drm_dev_ref);
+
+/**
+ * drm_dev_unref - Drop reference of a DRM device
+ * @dev: device to drop reference of or NULL
+ *
+ * This decreases the ref-count of @dev by one. The device is destroyed if the
+ * ref-count drops to zero.
+ */
+void drm_dev_unref(struct drm_device *dev)
+{
+ if (dev)
+ kref_put(&dev->ref, drm_dev_release);
+}
+EXPORT_SYMBOL(drm_dev_unref);
/**
* drm_dev_register - Register DRM device
@@ -527,26 +706,22 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
mutex_lock(&drm_global_mutex);
- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
- if (ret)
- goto out_unlock;
- }
+ ret = drm_minor_register(dev, DRM_MINOR_CONTROL);
+ if (ret)
+ goto err_minors;
- if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
- ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER);
- if (ret)
- goto err_control_node;
- }
+ ret = drm_minor_register(dev, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_minors;
- ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
+ ret = drm_minor_register(dev, DRM_MINOR_LEGACY);
if (ret)
- goto err_render_node;
+ goto err_minors;
if (dev->driver->load) {
ret = dev->driver->load(dev, flags);
if (ret)
- goto err_primary_node;
+ goto err_minors;
}
/* setup grouping for legacy outputs */
@@ -563,12 +738,10 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
err_unload:
if (dev->driver->unload)
dev->driver->unload(dev);
-err_primary_node:
- drm_unplug_minor(dev->primary);
-err_render_node:
- drm_unplug_minor(dev->render);
-err_control_node:
- drm_unplug_minor(dev->control);
+err_minors:
+ drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+ drm_minor_unregister(dev, DRM_MINOR_RENDER);
+ drm_minor_unregister(dev, DRM_MINOR_CONTROL);
out_unlock:
mutex_unlock(&drm_global_mutex);
return ret;
@@ -581,7 +754,7 @@ EXPORT_SYMBOL(drm_dev_register);
*
* Unregister the DRM device from the system. This does the reverse of
* drm_dev_register() but does not deallocate the device. The caller must call
- * drm_dev_free() to free all resources.
+ * drm_dev_unref() to drop their final reference.
*/
void drm_dev_unregister(struct drm_device *dev)
{
@@ -600,8 +773,8 @@ void drm_dev_unregister(struct drm_device *dev)
list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
drm_rmmap(dev, r_list->map);
- drm_unplug_minor(dev->control);
- drm_unplug_minor(dev->render);
- drm_unplug_minor(dev->primary);
+ drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+ drm_minor_unregister(dev, DRM_MINOR_RENDER);
+ drm_minor_unregister(dev, DRM_MINOR_CONTROL);
}
EXPORT_SYMBOL(drm_dev_unregister);
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index 0f8cb1ae7607..c3406aad2944 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -30,7 +30,7 @@ int drm_get_usb_dev(struct usb_interface *interface,
return 0;
err_free:
- drm_dev_free(dev);
+ drm_dev_unref(dev);
return ret;
}
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 6e1a1a20cf6b..5bf5bca94f56 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -31,6 +31,30 @@ config DRM_EXYNOS_FIMD
help
Choose this option if you want to use Exynos FIMD for DRM.
+config DRM_EXYNOS_DPI
+ bool "EXYNOS DRM parallel output support"
+ depends on DRM_EXYNOS
+ select DRM_PANEL
+ default n
+ help
+ This enables support for Exynos parallel output.
+
+config DRM_EXYNOS_DSI
+ bool "EXYNOS DRM MIPI-DSI driver support"
+ depends on DRM_EXYNOS
+ select DRM_MIPI_DSI
+ select DRM_PANEL
+ default n
+ help
+ This enables support for Exynos MIPI-DSI device.
+
+config DRM_EXYNOS_DP
+ bool "EXYNOS DRM DP driver support"
+ depends on DRM_EXYNOS && ARCH_EXYNOS
+ default DRM_EXYNOS
+ help
+ This enables support for DP device.
+
config DRM_EXYNOS_HDMI
bool "Exynos DRM HDMI"
depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 639b49e1ec05..33ae3652b8da 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -3,7 +3,7 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
-exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
+exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \
exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \
exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
exynos_drm_plane.o
@@ -11,9 +11,10 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
-exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
- exynos_ddc.o exynos_hdmiphy.o \
- exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 5e1a71580051..aed533bbfd31 100644
--- a/drivers/video/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
@@ -20,9 +19,25 @@
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/bridge/ptn3460.h>
+
+#include "exynos_drm_drv.h"
#include "exynos_dp_core.h"
+#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
+ connector)
+
+struct bridge_init {
+ struct i2c_client *client;
+ struct device_node *node;
+};
+
static int exynos_dp_init_dp(struct exynos_dp_device *dp)
{
exynos_dp_reset(dp);
@@ -893,6 +908,214 @@ static void exynos_dp_hotplug(struct work_struct *work)
dev_err(dp->dev, "unable to config video\n");
}
+static enum drm_connector_status exynos_dp_detect(
+ struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void exynos_dp_connector_destroy(struct drm_connector *connector)
+{
+}
+
+static struct drm_connector_funcs exynos_dp_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = exynos_dp_detect,
+ .destroy = exynos_dp_connector_destroy,
+};
+
+static int exynos_dp_get_modes(struct drm_connector *connector)
+{
+ struct exynos_dp_device *dp = ctx_from_connector(connector);
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ if (!mode) {
+ DRM_ERROR("failed to create a new display mode.\n");
+ return 0;
+ }
+
+ drm_display_mode_from_videomode(&dp->panel.vm, mode);
+ mode->width_mm = dp->panel.width_mm;
+ mode->height_mm = dp->panel.height_mm;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static int exynos_dp_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_encoder *exynos_dp_best_encoder(
+ struct drm_connector *connector)
+{
+ struct exynos_dp_device *dp = ctx_from_connector(connector);
+
+ return dp->encoder;
+}
+
+static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
+ .get_modes = exynos_dp_get_modes,
+ .mode_valid = exynos_dp_mode_valid,
+ .best_encoder = exynos_dp_best_encoder,
+};
+
+static int exynos_dp_initialize(struct exynos_drm_display *display,
+ struct drm_device *drm_dev)
+{
+ struct exynos_dp_device *dp = display->ctx;
+
+ dp->drm_dev = drm_dev;
+
+ return 0;
+}
+
+static bool find_bridge(const char *compat, struct bridge_init *bridge)
+{
+ bridge->client = NULL;
+ bridge->node = of_find_compatible_node(NULL, NULL, compat);
+ if (!bridge->node)
+ return false;
+
+ bridge->client = of_find_i2c_device_by_node(bridge->node);
+ if (!bridge->client)
+ return false;
+
+ return true;
+}
+
+/* returns the number of bridges attached */
+static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct bridge_init bridge;
+ int ret;
+
+ if (find_bridge("nxp,ptn3460", &bridge)) {
+ ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
+ if (!ret)
+ return 1;
+ }
+ return 0;
+}
+
+static int exynos_dp_create_connector(struct exynos_drm_display *display,
+ struct drm_encoder *encoder)
+{
+ struct exynos_dp_device *dp = display->ctx;
+ struct drm_connector *connector = &dp->connector;
+ int ret;
+
+ dp->encoder = encoder;
+
+ /* Pre-empt DP connector creation if there's a bridge */
+ ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder);
+ if (ret)
+ return 0;
+
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(dp->drm_dev, connector,
+ &exynos_dp_connector_funcs, DRM_MODE_CONNECTOR_eDP);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static void exynos_dp_phy_init(struct exynos_dp_device *dp)
+{
+ if (dp->phy) {
+ phy_power_on(dp->phy);
+ } else if (dp->phy_addr) {
+ u32 reg;
+
+ reg = __raw_readl(dp->phy_addr);
+ reg |= dp->enable_mask;
+ __raw_writel(reg, dp->phy_addr);
+ }
+}
+
+static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
+{
+ if (dp->phy) {
+ phy_power_off(dp->phy);
+ } else if (dp->phy_addr) {
+ u32 reg;
+
+ reg = __raw_readl(dp->phy_addr);
+ reg &= ~(dp->enable_mask);
+ __raw_writel(reg, dp->phy_addr);
+ }
+}
+
+static void exynos_dp_poweron(struct exynos_dp_device *dp)
+{
+ if (dp->dpms_mode == DRM_MODE_DPMS_ON)
+ return;
+
+ clk_prepare_enable(dp->clock);
+ exynos_dp_phy_init(dp);
+ exynos_dp_init_dp(dp);
+ enable_irq(dp->irq);
+}
+
+static void exynos_dp_poweroff(struct exynos_dp_device *dp)
+{
+ if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+ return;
+
+ disable_irq(dp->irq);
+ flush_work(&dp->hotplug_work);
+ exynos_dp_phy_exit(dp);
+ clk_disable_unprepare(dp->clock);
+}
+
+static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
+{
+ struct exynos_dp_device *dp = display->ctx;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ exynos_dp_poweron(dp);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ exynos_dp_poweroff(dp);
+ break;
+ default:
+ break;
+ };
+ dp->dpms_mode = mode;
+}
+
+static struct exynos_drm_display_ops exynos_dp_display_ops = {
+ .initialize = exynos_dp_initialize,
+ .create_connector = exynos_dp_create_connector,
+ .dpms = exynos_dp_dpms,
+};
+
+static struct exynos_drm_display exynos_dp_display = {
+ .type = EXYNOS_DISPLAY_TYPE_LCD,
+ .ops = &exynos_dp_display_ops,
+};
+
static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
{
struct device_node *dp_node = dev->of_node;
@@ -994,30 +1217,17 @@ err:
return ret;
}
-static void exynos_dp_phy_init(struct exynos_dp_device *dp)
-{
- if (dp->phy) {
- phy_power_on(dp->phy);
- } else if (dp->phy_addr) {
- u32 reg;
-
- reg = __raw_readl(dp->phy_addr);
- reg |= dp->enable_mask;
- __raw_writel(reg, dp->phy_addr);
- }
-}
-
-static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
+static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
{
- if (dp->phy) {
- phy_power_off(dp->phy);
- } else if (dp->phy_addr) {
- u32 reg;
+ int ret;
- reg = __raw_readl(dp->phy_addr);
- reg &= ~(dp->enable_mask);
- __raw_writel(reg, dp->phy_addr);
+ ret = of_get_videomode(dp->dev->of_node, &dp->panel.vm,
+ OF_USE_NATIVE_MODE);
+ if (ret) {
+ DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
+ return ret;
}
+ return 0;
}
static int exynos_dp_probe(struct platform_device *pdev)
@@ -1035,6 +1245,7 @@ static int exynos_dp_probe(struct platform_device *pdev)
}
dp->dev = &pdev->dev;
+ dp->dpms_mode = DRM_MODE_DPMS_OFF;
dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
if (IS_ERR(dp->video_info))
@@ -1044,6 +1255,10 @@ static int exynos_dp_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = exynos_dp_dt_parse_panel(dp);
+ if (ret)
+ return ret;
+
dp->clock = devm_clk_get(&pdev->dev, "dp");
if (IS_ERR(dp->clock)) {
dev_err(&pdev->dev, "failed to get clock\n");
@@ -1076,22 +1291,22 @@ static int exynos_dp_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
+ disable_irq(dp->irq);
+
+ exynos_dp_display.ctx = dp;
- platform_set_drvdata(pdev, dp);
+ platform_set_drvdata(pdev, &exynos_dp_display);
+ exynos_drm_display_register(&exynos_dp_display);
return 0;
}
static int exynos_dp_remove(struct platform_device *pdev)
{
- struct exynos_dp_device *dp = platform_get_drvdata(pdev);
-
- flush_work(&dp->hotplug_work);
-
- exynos_dp_phy_exit(dp);
-
- clk_disable_unprepare(dp->clock);
+ struct exynos_drm_display *display = platform_get_drvdata(pdev);
+ exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
+ exynos_drm_display_unregister(&exynos_dp_display);
return 0;
}
@@ -1099,31 +1314,19 @@ static int exynos_dp_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int exynos_dp_suspend(struct device *dev)
{
- struct exynos_dp_device *dp = dev_get_drvdata(dev);
-
- disable_irq(dp->irq);
-
- flush_work(&dp->hotplug_work);
-
- exynos_dp_phy_exit(dp);
-
- clk_disable_unprepare(dp->clock);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_drm_display *display = platform_get_drvdata(pdev);
+ exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
return 0;
}
static int exynos_dp_resume(struct device *dev)
{
- struct exynos_dp_device *dp = dev_get_drvdata(dev);
-
- exynos_dp_phy_init(dp);
-
- clk_prepare_enable(dp->clock);
-
- exynos_dp_init_dp(dp);
-
- enable_irq(dp->irq);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_drm_display *display = platform_get_drvdata(pdev);
+ exynos_dp_dpms(display, DRM_MODE_DPMS_ON);
return 0;
}
#endif
@@ -1136,9 +1339,8 @@ static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
};
-MODULE_DEVICE_TABLE(of, exynos_dp_match);
-static struct platform_driver exynos_dp_driver = {
+struct platform_driver dp_driver = {
.probe = exynos_dp_probe,
.remove = exynos_dp_remove,
.driver = {
@@ -1149,8 +1351,6 @@ static struct platform_driver exynos_dp_driver = {
},
};
-module_platform_driver(exynos_dp_driver);
-
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DP Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index 607e36d0c147..d6a900d4ee40 100644
--- a/drivers/video/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -13,6 +13,9 @@
#ifndef _EXYNOS_DP_CORE_H
#define _EXYNOS_DP_CORE_H
+#include <drm/drm_crtc.h>
+#include <drm/exynos_drm.h>
+
#define DP_TIMEOUT_LOOP_COUNT 100
#define MAX_CR_LOOP 5
#define MAX_EQ_LOOP 5
@@ -142,6 +145,9 @@ struct link_train {
struct exynos_dp_device {
struct device *dev;
+ struct drm_device *drm_dev;
+ struct drm_connector connector;
+ struct drm_encoder *encoder;
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;
@@ -152,6 +158,9 @@ struct exynos_dp_device {
struct link_train link_train;
struct work_struct hotplug_work;
struct phy *phy;
+ int dpms_mode;
+
+ struct exynos_drm_panel_info panel;
};
/* exynos_dp_reg.c */
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/gpu/drm/exynos/exynos_dp_reg.c
index b70da5052ff0..b70da5052ff0 100644
--- a/drivers/video/exynos/exynos_dp_reg.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_reg.c
diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/gpu/drm/exynos/exynos_dp_reg.h
index 2e9bd0e0b9f2..2e9bd0e0b9f2 100644
--- a/drivers/video/exynos/exynos_dp_reg.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_reg.h
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index e082efb2fece..9a16dbe121d1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -23,27 +23,20 @@
drm_connector)
struct exynos_drm_connector {
- struct drm_connector drm_connector;
- uint32_t encoder_id;
- struct exynos_drm_manager *manager;
- uint32_t dpms;
+ struct drm_connector drm_connector;
+ uint32_t encoder_id;
+ struct exynos_drm_display *display;
};
static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
- struct exynos_drm_manager *manager = exynos_connector->manager;
- struct exynos_drm_display_ops *display_ops = manager->display_ops;
+ struct exynos_drm_display *display = exynos_connector->display;
struct edid *edid = NULL;
unsigned int count = 0;
int ret;
- if (!display_ops) {
- DRM_DEBUG_KMS("display_ops is null.\n");
- return 0;
- }
-
/*
* if get_edid() exists then get_edid() callback of hdmi side
* is called to get edid data through i2c interface else
@@ -52,8 +45,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
* P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode.
*/
- if (display_ops->get_edid) {
- edid = display_ops->get_edid(manager->dev, connector);
+ if (display->ops->get_edid) {
+ edid = display->ops->get_edid(display, connector);
if (IS_ERR_OR_NULL(edid)) {
ret = PTR_ERR(edid);
edid = NULL;
@@ -76,8 +69,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
return 0;
}
- if (display_ops->get_panel)
- panel = display_ops->get_panel(manager->dev);
+ if (display->ops->get_panel)
+ panel = display->ops->get_panel(display);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
@@ -106,20 +99,20 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
- struct exynos_drm_manager *manager = exynos_connector->manager;
- struct exynos_drm_display_ops *display_ops = manager->display_ops;
+ struct exynos_drm_display *display = exynos_connector->display;
int ret = MODE_BAD;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (display_ops && display_ops->check_mode)
- if (!display_ops->check_mode(manager->dev, mode))
+ if (display->ops->check_mode)
+ if (!display->ops->check_mode(display, mode))
ret = MODE_OK;
return ret;
}
-struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
+static struct drm_encoder *exynos_drm_best_encoder(
+ struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
@@ -146,48 +139,12 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.best_encoder = exynos_drm_best_encoder,
};
-void exynos_drm_display_power(struct drm_connector *connector, int mode)
-{
- struct drm_encoder *encoder = exynos_drm_best_encoder(connector);
- struct exynos_drm_connector *exynos_connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_display_ops *display_ops = manager->display_ops;
-
- exynos_connector = to_exynos_connector(connector);
-
- if (exynos_connector->dpms == mode) {
- DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
- return;
- }
-
- if (display_ops && display_ops->power_on)
- display_ops->power_on(manager->dev, mode);
-
- exynos_connector->dpms = mode;
-}
-
-static void exynos_drm_connector_dpms(struct drm_connector *connector,
- int mode)
-{
- /*
- * in case that drm_crtc_helper_set_mode() is called,
- * encoder/crtc->funcs->dpms() will be just returned
- * because they already were DRM_MODE_DPMS_ON so only
- * exynos_drm_display_power() will be called.
- */
- drm_helper_connector_dpms(connector, mode);
-
- exynos_drm_display_power(connector, mode);
-
-}
-
static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
unsigned int max_width, unsigned int max_height)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
- struct exynos_drm_manager *manager = exynos_connector->manager;
- struct exynos_drm_manager_ops *ops = manager->ops;
+ struct exynos_drm_display *display = exynos_connector->display;
unsigned int width, height;
width = max_width;
@@ -197,8 +154,8 @@ static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
* if specific driver want to find desired_mode using maxmum
* resolution then get max width and height from that driver.
*/
- if (ops && ops->get_max_resol)
- ops->get_max_resol(manager->dev, &width, &height);
+ if (display->ops->get_max_resol)
+ display->ops->get_max_resol(display, &width, &height);
return drm_helper_probe_single_connector_modes(connector, width,
height);
@@ -210,13 +167,11 @@ exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
- struct exynos_drm_manager *manager = exynos_connector->manager;
- struct exynos_drm_display_ops *display_ops =
- manager->display_ops;
+ struct exynos_drm_display *display = exynos_connector->display;
enum drm_connector_status status = connector_status_disconnected;
- if (display_ops && display_ops->is_connected) {
- if (display_ops->is_connected(manager->dev))
+ if (display->ops->is_connected) {
+ if (display->ops->is_connected(display))
status = connector_status_connected;
else
status = connector_status_disconnected;
@@ -236,7 +191,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_connector_funcs = {
- .dpms = exynos_drm_connector_dpms,
+ .dpms = drm_helper_connector_dpms,
.fill_modes = exynos_drm_connector_fill_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
@@ -246,7 +201,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct exynos_drm_connector *exynos_connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct drm_connector *connector;
int type;
int err;
@@ -257,7 +212,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
connector = &exynos_connector->drm_connector;
- switch (manager->display_ops->type) {
+ switch (display->type) {
case EXYNOS_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
@@ -280,8 +235,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
goto err_connector;
exynos_connector->encoder_id = encoder->base.id;
- exynos_connector->manager = manager;
- exynos_connector->dpms = DRM_MODE_DPMS_OFF;
+ exynos_connector->display = display;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->encoder = encoder;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.h b/drivers/gpu/drm/exynos/exynos_drm_connector.h
index 547c6b590357..4eb20d78379a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.h
@@ -17,8 +17,4 @@
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder);
-struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector);
-
-void exynos_drm_display_power(struct drm_connector *connector, int mode);
-
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 1bef6dc77478..0e9e06ce36b8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -14,43 +14,42 @@
#include <drm/drmP.h>
#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
-#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
+static LIST_HEAD(exynos_drm_manager_list);
+static LIST_HEAD(exynos_drm_display_list);
static int exynos_drm_create_enc_conn(struct drm_device *dev,
- struct exynos_drm_subdrv *subdrv)
+ struct exynos_drm_display *display)
{
struct drm_encoder *encoder;
- struct drm_connector *connector;
+ struct exynos_drm_manager *manager;
int ret;
+ unsigned long possible_crtcs = 0;
- subdrv->manager->dev = subdrv->dev;
+ /* Find possible crtcs for this display */
+ list_for_each_entry(manager, &exynos_drm_manager_list, list)
+ if (manager->type == display->type)
+ possible_crtcs |= 1 << manager->pipe;
/* create and initialize a encoder for this sub driver. */
- encoder = exynos_drm_encoder_create(dev, subdrv->manager,
- (1 << MAX_CRTC) - 1);
+ encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
- /*
- * create and initialize a connector for this sub driver and
- * attach the encoder created above to the connector.
- */
- connector = exynos_drm_connector_create(dev, encoder);
- if (!connector) {
- DRM_ERROR("failed to create connector\n");
- ret = -EFAULT;
+ display->encoder = encoder;
+
+ ret = display->ops->create_connector(display, encoder);
+ if (ret) {
+ DRM_ERROR("failed to create connector ret = %d\n", ret);
goto err_destroy_encoder;
}
- subdrv->encoder = encoder;
- subdrv->connector = connector;
-
return 0;
err_destroy_encoder:
@@ -58,21 +57,6 @@ err_destroy_encoder:
return ret;
}
-static void exynos_drm_destroy_enc_conn(struct exynos_drm_subdrv *subdrv)
-{
- if (subdrv->encoder) {
- struct drm_encoder *encoder = subdrv->encoder;
- encoder->funcs->destroy(encoder);
- subdrv->encoder = NULL;
- }
-
- if (subdrv->connector) {
- struct drm_connector *connector = subdrv->connector;
- connector->funcs->destroy(connector);
- subdrv->connector = NULL;
- }
-}
-
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
@@ -104,10 +88,98 @@ static void exynos_drm_subdrv_remove(struct drm_device *dev,
subdrv->remove(dev, subdrv->dev);
}
+int exynos_drm_initialize_managers(struct drm_device *dev)
+{
+ struct exynos_drm_manager *manager, *n;
+ int ret, pipe = 0;
+
+ list_for_each_entry(manager, &exynos_drm_manager_list, list) {
+ if (manager->ops->initialize) {
+ ret = manager->ops->initialize(manager, dev, pipe);
+ if (ret) {
+ DRM_ERROR("Mgr init [%d] failed with %d\n",
+ manager->type, ret);
+ goto err;
+ }
+ }
+
+ manager->drm_dev = dev;
+ manager->pipe = pipe++;
+
+ ret = exynos_drm_crtc_create(manager);
+ if (ret) {
+ DRM_ERROR("CRTC create [%d] failed with %d\n",
+ manager->type, ret);
+ goto err;
+ }
+ }
+ return 0;
+
+err:
+ list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
+ if (pipe-- > 0)
+ exynos_drm_manager_unregister(manager);
+ else
+ list_del(&manager->list);
+ }
+ return ret;
+}
+
+void exynos_drm_remove_managers(struct drm_device *dev)
+{
+ struct exynos_drm_manager *manager, *n;
+
+ list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
+ exynos_drm_manager_unregister(manager);
+}
+
+int exynos_drm_initialize_displays(struct drm_device *dev)
+{
+ struct exynos_drm_display *display, *n;
+ int ret, initialized = 0;
+
+ list_for_each_entry(display, &exynos_drm_display_list, list) {
+ if (display->ops->initialize) {
+ ret = display->ops->initialize(display, dev);
+ if (ret) {
+ DRM_ERROR("Display init [%d] failed with %d\n",
+ display->type, ret);
+ goto err;
+ }
+ }
+
+ initialized++;
+
+ ret = exynos_drm_create_enc_conn(dev, display);
+ if (ret) {
+ DRM_ERROR("Encoder create [%d] failed with %d\n",
+ display->type, ret);
+ goto err;
+ }
+ }
+ return 0;
+
+err:
+ list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
+ if (initialized-- > 0)
+ exynos_drm_display_unregister(display);
+ else
+ list_del(&display->list);
+ }
+ return ret;
+}
+
+void exynos_drm_remove_displays(struct drm_device *dev)
+{
+ struct exynos_drm_display *display, *n;
+
+ list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
+ exynos_drm_display_unregister(display);
+}
+
int exynos_drm_device_register(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
- unsigned int fine_cnt = 0;
int err;
if (!dev)
@@ -120,30 +192,8 @@ int exynos_drm_device_register(struct drm_device *dev)
list_del(&subdrv->list);
continue;
}
-
- /*
- * if manager is null then it means that this sub driver
- * doesn't need encoder and connector.
- */
- if (!subdrv->manager) {
- fine_cnt++;
- continue;
- }
-
- err = exynos_drm_create_enc_conn(dev, subdrv);
- if (err) {
- DRM_DEBUG("failed to create encoder and connector.\n");
- exynos_drm_subdrv_remove(dev, subdrv);
- list_del(&subdrv->list);
- continue;
- }
-
- fine_cnt++;
}
- if (!fine_cnt)
- return -EINVAL;
-
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
@@ -159,13 +209,44 @@ int exynos_drm_device_unregister(struct drm_device *dev)
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
exynos_drm_subdrv_remove(dev, subdrv);
- exynos_drm_destroy_enc_conn(subdrv);
}
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
+int exynos_drm_manager_register(struct exynos_drm_manager *manager)
+{
+ BUG_ON(!manager->ops);
+ list_add_tail(&manager->list, &exynos_drm_manager_list);
+ return 0;
+}
+
+int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
+{
+ if (manager->ops->remove)
+ manager->ops->remove(manager);
+
+ list_del(&manager->list);
+ return 0;
+}
+
+int exynos_drm_display_register(struct exynos_drm_display *display)
+{
+ BUG_ON(!display->ops);
+ list_add_tail(&display->list, &exynos_drm_display_list);
+ return 0;
+}
+
+int exynos_drm_display_unregister(struct exynos_drm_display *display)
+{
+ if (display->ops->remove)
+ display->ops->remove(display);
+
+ list_del(&display->list);
+ return 0;
+}
+
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
if (!subdrv)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 6f3400f3978a..e930d4fe29c7 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -33,6 +33,7 @@ enum exynos_crtc_mode {
*
* @drm_crtc: crtc object.
* @drm_plane: pointer of private plane object for this crtc
+ * @manager: the manager associated with this crtc
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
@@ -46,6 +47,7 @@ enum exynos_crtc_mode {
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
struct drm_plane *plane;
+ struct exynos_drm_manager *manager;
unsigned int pipe;
unsigned int dpms;
enum exynos_crtc_mode mode;
@@ -56,6 +58,7 @@ struct exynos_drm_crtc {
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
@@ -71,7 +74,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
drm_vblank_off(crtc->dev, exynos_crtc->pipe);
}
- exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
+ if (manager->ops->dpms)
+ manager->ops->dpms(manager, mode);
+
exynos_crtc->dpms = mode;
}
@@ -83,9 +88,15 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+
exynos_plane_commit(exynos_crtc->plane);
+
+ if (manager->ops->commit)
+ manager->ops->commit(manager);
+
exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
}
@@ -94,7 +105,12 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- /* drm framework doesn't check NULL */
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
+
+ if (manager->ops->mode_fixup)
+ return manager->ops->mode_fixup(manager, mode, adjusted_mode);
+
return true;
}
@@ -104,10 +120,10 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_framebuffer *old_fb)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
struct drm_plane *plane = exynos_crtc->plane;
unsigned int crtc_w;
unsigned int crtc_h;
- int pipe = exynos_crtc->pipe;
int ret;
/*
@@ -116,18 +132,19 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
- crtc_w = crtc->fb->width - x;
- crtc_h = crtc->fb->height - y;
+ crtc_w = crtc->primary->fb->width - x;
+ crtc_h = crtc->primary->fb->height - y;
+
+ if (manager->ops->mode_set)
+ manager->ops->mode_set(manager, &crtc->mode);
- ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+ ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
x, y, crtc_w, crtc_h);
if (ret)
return ret;
plane->crtc = crtc;
- plane->fb = crtc->fb;
-
- exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
+ plane->fb = crtc->primary->fb;
return 0;
}
@@ -147,10 +164,10 @@ static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,
return -EPERM;
}
- crtc_w = crtc->fb->width - x;
- crtc_h = crtc->fb->height - y;
+ crtc_w = crtc->primary->fb->width - x;
+ crtc_h = crtc->primary->fb->height - y;
- ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+ ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
x, y, crtc_w, crtc_h);
if (ret)
return ret;
@@ -168,10 +185,19 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
{
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct drm_plane *plane;
+ int ret;
- exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+
+ drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
+ if (plane->crtc != crtc)
+ continue;
+
+ ret = plane->funcs->disable_plane(plane);
+ if (ret)
+ DRM_ERROR("Failed to disable plane %d\n", ret);
+ }
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
@@ -192,7 +218,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct exynos_drm_private *dev_priv = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- struct drm_framebuffer *old_fb = crtc->fb;
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
int ret = -EINVAL;
/* when the page flip is requested, crtc's dpms should be on */
@@ -223,11 +249,11 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
atomic_set(&exynos_crtc->pending_flip, 1);
spin_unlock_irq(&dev->event_lock);
- crtc->fb = fb;
+ crtc->primary->fb = fb;
ret = exynos_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y,
NULL);
if (ret) {
- crtc->fb = old_fb;
+ crtc->primary->fb = old_fb;
spin_lock_irq(&dev->event_lock);
drm_vblank_put(dev, exynos_crtc->pipe);
@@ -318,21 +344,24 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
drm_object_attach_property(&crtc->base, prop, 0);
}
-int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
+int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
{
struct exynos_drm_crtc *exynos_crtc;
- struct exynos_drm_private *private = dev->dev_private;
+ struct exynos_drm_private *private = manager->drm_dev->dev_private;
struct drm_crtc *crtc;
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc)
return -ENOMEM;
- exynos_crtc->pipe = nr;
- exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
atomic_set(&exynos_crtc->pending_flip, 0);
- exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
+
+ exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
+ exynos_crtc->manager = manager;
+ exynos_crtc->pipe = manager->pipe;
+ exynos_crtc->plane = exynos_plane_init(manager->drm_dev,
+ 1 << manager->pipe, true);
if (!exynos_crtc->plane) {
kfree(exynos_crtc);
return -ENOMEM;
@@ -340,9 +369,9 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
crtc = &exynos_crtc->drm_crtc;
- private->crtc[nr] = crtc;
+ private->crtc[manager->pipe] = crtc;
- drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
+ drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs);
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
exynos_drm_crtc_attach_mode_property(crtc);
@@ -350,39 +379,41 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
return 0;
}
-int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
- to_exynos_crtc(private->crtc[crtc]);
+ to_exynos_crtc(private->crtc[pipe]);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return -EPERM;
- exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
- exynos_drm_enable_vblank);
+ if (manager->ops->enable_vblank)
+ manager->ops->enable_vblank(manager);
return 0;
}
-void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
- to_exynos_crtc(private->crtc[crtc]);
+ to_exynos_crtc(private->crtc[pipe]);
+ struct exynos_drm_manager *manager = exynos_crtc->manager;
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return;
- exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
- exynos_drm_disable_vblank);
+ if (manager->ops->disable_vblank)
+ manager->ops->disable_vblank(manager);
}
-void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
+void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *dev_priv = dev->dev_private;
struct drm_pending_vblank_event *e, *t;
- struct drm_crtc *drm_crtc = dev_priv->crtc[crtc];
+ struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
unsigned long flags;
@@ -391,15 +422,71 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
base.link) {
/* if event's pipe isn't same as crtc then ignore it. */
- if (crtc != e->pipe)
+ if (pipe != e->pipe)
continue;
list_del(&e->base.link);
drm_send_vblank_event(dev, -1, e);
- drm_vblank_put(dev, crtc);
+ drm_vblank_put(dev, pipe);
atomic_set(&exynos_crtc->pending_flip, 0);
wake_up(&exynos_crtc->pending_flip_queue);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
+
+void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
+ struct exynos_drm_overlay *overlay)
+{
+ struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
+
+ if (manager->ops->win_mode_set)
+ manager->ops->win_mode_set(manager, overlay);
+}
+
+void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos)
+{
+ struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
+
+ if (manager->ops->win_commit)
+ manager->ops->win_commit(manager, zpos);
+}
+
+void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos)
+{
+ struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
+
+ if (manager->ops->win_enable)
+ manager->ops->win_enable(manager, zpos);
+}
+
+void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos)
+{
+ struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
+
+ if (manager->ops->win_disable)
+ manager->ops->win_disable(manager, zpos);
+}
+
+void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
+{
+ struct exynos_drm_manager *manager;
+ struct drm_device *dev = fb->dev;
+ struct drm_crtc *crtc;
+
+ /*
+ * make sure that overlay data are updated to real hardware
+ * for all encoders.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ manager = to_exynos_crtc(crtc)->manager;
+
+ /*
+ * wait for vblank interrupt
+ * - this makes sure that overlay data are updated to
+ * real hardware.
+ */
+ if (manager->ops->wait_for_vblank)
+ manager->ops->wait_for_vblank(manager);
+ }
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index 3e197e6ae7d9..c27b66cc5d24 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -15,9 +15,21 @@
#ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
-int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
-int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
-void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
-void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc);
+struct drm_device;
+struct drm_crtc;
+struct exynos_drm_manager;
+struct exynos_drm_overlay;
+
+int exynos_drm_crtc_create(struct exynos_drm_manager *manager);
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
+void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
+
+void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
+ struct exynos_drm_overlay *overlay);
+void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
+void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
+void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
new file mode 100644
index 000000000000..2b09c7c0bfcc
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -0,0 +1,339 @@
+/*
+ * Exynos DRM Parallel output support.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * Contacts: Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "exynos_drm_drv.h"
+
+struct exynos_dpi {
+ struct device *dev;
+ struct device_node *panel_node;
+
+ struct drm_panel *panel;
+ struct drm_connector connector;
+ struct drm_encoder *encoder;
+
+ struct videomode *vm;
+ int dpms_mode;
+};
+
+#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
+
+static enum drm_connector_status
+exynos_dpi_detect(struct drm_connector *connector, bool force)
+{
+ struct exynos_dpi *ctx = connector_to_dpi(connector);
+
+ /* panels supported only by boot-loader are always connected */
+ if (!ctx->panel_node)
+ return connector_status_connected;
+
+ if (!ctx->panel) {
+ ctx->panel = of_drm_find_panel(ctx->panel_node);
+ if (ctx->panel)
+ drm_panel_attach(ctx->panel, &ctx->connector);
+ }
+
+ if (ctx->panel)
+ return connector_status_connected;
+
+ return connector_status_disconnected;
+}
+
+static void exynos_dpi_connector_destroy(struct drm_connector *connector)
+{
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs exynos_dpi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = exynos_dpi_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = exynos_dpi_connector_destroy,
+};
+
+static int exynos_dpi_get_modes(struct drm_connector *connector)
+{
+ struct exynos_dpi *ctx = connector_to_dpi(connector);
+
+ /* fimd timings gets precedence over panel modes */
+ if (ctx->vm) {
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ if (!mode) {
+ DRM_ERROR("failed to create a new display mode\n");
+ return 0;
+ }
+ drm_display_mode_from_videomode(ctx->vm, mode);
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+
+ if (ctx->panel)
+ return ctx->panel->funcs->get_modes(ctx->panel);
+
+ return 0;
+}
+
+static int exynos_dpi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+exynos_dpi_best_encoder(struct drm_connector *connector)
+{
+ struct exynos_dpi *ctx = connector_to_dpi(connector);
+
+ return ctx->encoder;
+}
+
+static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
+ .get_modes = exynos_dpi_get_modes,
+ .mode_valid = exynos_dpi_mode_valid,
+ .best_encoder = exynos_dpi_best_encoder,
+};
+
+static int exynos_dpi_create_connector(struct exynos_drm_display *display,
+ struct drm_encoder *encoder)
+{
+ struct exynos_dpi *ctx = display->ctx;
+ struct drm_connector *connector = &ctx->connector;
+ int ret;
+
+ ctx->encoder = encoder;
+
+ if (ctx->panel_node)
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ else
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(encoder->dev, connector,
+ &exynos_dpi_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA);
+ if (ret) {
+ DRM_ERROR("failed to initialize connector with drm\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static void exynos_dpi_poweron(struct exynos_dpi *ctx)
+{
+ if (ctx->panel)
+ drm_panel_enable(ctx->panel);
+}
+
+static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
+{
+ if (ctx->panel)
+ drm_panel_disable(ctx->panel);
+}
+
+static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
+{
+ struct exynos_dpi *ctx = display->ctx;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (ctx->dpms_mode != DRM_MODE_DPMS_ON)
+ exynos_dpi_poweron(ctx);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ if (ctx->dpms_mode == DRM_MODE_DPMS_ON)
+ exynos_dpi_poweroff(ctx);
+ break;
+ default:
+ break;
+ };
+ ctx->dpms_mode = mode;
+}
+
+static struct exynos_drm_display_ops exynos_dpi_display_ops = {
+ .create_connector = exynos_dpi_create_connector,
+ .dpms = exynos_dpi_dpms
+};
+
+static struct exynos_drm_display exynos_dpi_display = {
+ .type = EXYNOS_DISPLAY_TYPE_LCD,
+ .ops = &exynos_dpi_display_ops,
+};
+
+/* of_* functions will be removed after merge of of_graph patches */
+static struct device_node *
+of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg)
+{
+ struct device_node *np;
+
+ for_each_child_of_node(parent, np) {
+ u32 r;
+
+ if (!np->name || of_node_cmp(np->name, name))
+ continue;
+
+ if (of_property_read_u32(np, "reg", &r) < 0)
+ r = 0;
+
+ if (reg == r)
+ break;
+ }
+
+ return np;
+}
+
+static struct device_node *of_graph_get_port_by_reg(struct device_node *parent,
+ u32 reg)
+{
+ struct device_node *ports, *port;
+
+ ports = of_get_child_by_name(parent, "ports");
+ if (ports)
+ parent = ports;
+
+ port = of_get_child_by_name_reg(parent, "port", reg);
+
+ of_node_put(ports);
+
+ return port;
+}
+
+static struct device_node *
+of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg)
+{
+ return of_get_child_by_name_reg(port, "endpoint", reg);
+}
+
+static struct device_node *
+of_graph_get_remote_port_parent(const struct device_node *node)
+{
+ struct device_node *np;
+ unsigned int depth;
+
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+
+ /* Walk 3 levels up only if there is 'ports' node. */
+ for (depth = 3; depth && np; depth--) {
+ np = of_get_next_parent(np);
+ if (depth == 2 && of_node_cmp(np->name, "ports"))
+ break;
+ }
+ return np;
+}
+
+enum {
+ FIMD_PORT_IN0,
+ FIMD_PORT_IN1,
+ FIMD_PORT_IN2,
+ FIMD_PORT_RGB,
+ FIMD_PORT_WRB,
+};
+
+static struct device_node *exynos_dpi_of_find_panel_node(struct device *dev)
+{
+ struct device_node *np, *ep;
+
+ np = of_graph_get_port_by_reg(dev->of_node, FIMD_PORT_RGB);
+ if (!np)
+ return NULL;
+
+ ep = of_graph_get_endpoint_by_reg(np, 0);
+ of_node_put(np);
+ if (!ep)
+ return NULL;
+
+ np = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+
+ return np;
+}
+
+static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct device_node *dn = dev->of_node;
+ struct device_node *np;
+
+ ctx->panel_node = exynos_dpi_of_find_panel_node(dev);
+
+ np = of_get_child_by_name(dn, "display-timings");
+ if (np) {
+ struct videomode *vm;
+ int ret;
+
+ of_node_put(np);
+
+ vm = devm_kzalloc(dev, sizeof(*ctx->vm), GFP_KERNEL);
+ if (!vm)
+ return -ENOMEM;
+
+ ret = of_get_videomode(dn, vm, 0);
+ if (ret < 0)
+ return ret;
+
+ ctx->vm = vm;
+
+ return 0;
+ }
+
+ if (!ctx->panel_node)
+ return -EINVAL;
+
+ return 0;
+}
+
+int exynos_dpi_probe(struct device *dev)
+{
+ struct exynos_dpi *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+ exynos_dpi_display.ctx = ctx;
+ ctx->dpms_mode = DRM_MODE_DPMS_OFF;
+
+ ret = exynos_dpi_parse_dt(ctx);
+ if (ret < 0)
+ return ret;
+
+ exynos_drm_display_register(&exynos_dpi_display);
+
+ return 0;
+}
+
+int exynos_dpi_remove(struct device *dev)
+{
+ exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
+ exynos_drm_display_unregister(&exynos_dpi_display);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index c204b4e3356e..2d27ba23a6a8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -11,6 +11,7 @@
* option) any later version.
*/
+#include <linux/pm_runtime.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@@ -53,6 +54,7 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
return -ENOMEM;
INIT_LIST_HEAD(&private->pageflip_event_list);
+ dev_set_drvdata(dev->dev, dev);
dev->dev_private = (void *)private;
/*
@@ -64,38 +66,36 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
ret = drm_create_iommu_mapping(dev);
if (ret < 0) {
DRM_ERROR("failed to create iommu mapping.\n");
- goto err_crtc;
+ goto err_free_private;
}
drm_mode_config_init(dev);
- /* init kms poll for handling hpd */
- drm_kms_helper_poll_init(dev);
-
exynos_drm_mode_config_init(dev);
- /*
- * EXYNOS4 is enough to have two CRTCs and each crtc would be used
- * without dependency of hardware.
- */
- for (nr = 0; nr < MAX_CRTC; nr++) {
- ret = exynos_drm_crtc_create(dev, nr);
- if (ret)
- goto err_release_iommu_mapping;
- }
+ ret = exynos_drm_initialize_managers(dev);
+ if (ret)
+ goto err_mode_config_cleanup;
for (nr = 0; nr < MAX_PLANE; nr++) {
struct drm_plane *plane;
- unsigned int possible_crtcs = (1 << MAX_CRTC) - 1;
+ unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
plane = exynos_plane_init(dev, possible_crtcs, false);
if (!plane)
- goto err_release_iommu_mapping;
+ goto err_manager_cleanup;
}
+ ret = exynos_drm_initialize_displays(dev);
+ if (ret)
+ goto err_manager_cleanup;
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(dev);
+
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
- goto err_release_iommu_mapping;
+ goto err_display_cleanup;
/*
* probe sub drivers such as display controller and hdmi driver,
@@ -109,30 +109,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
/* setup possible_clones. */
exynos_drm_encoder_setup(dev);
- /*
- * create and configure fb helper and also exynos specific
- * fbdev object.
- */
- ret = exynos_drm_fbdev_init(dev);
- if (ret) {
- DRM_ERROR("failed to initialize drm fbdev\n");
- goto err_drm_device;
- }
-
drm_vblank_offdelay = VBLANK_OFF_DELAY;
platform_set_drvdata(dev->platformdev, dev);
+ /* force connectors detection */
+ drm_helper_hpd_irq_event(dev);
+
return 0;
-err_drm_device:
- exynos_drm_device_unregister(dev);
err_vblank:
drm_vblank_cleanup(dev);
-err_release_iommu_mapping:
- drm_release_iommu_mapping(dev);
-err_crtc:
+err_display_cleanup:
+ exynos_drm_remove_displays(dev);
+err_manager_cleanup:
+ exynos_drm_remove_managers(dev);
+err_mode_config_cleanup:
drm_mode_config_cleanup(dev);
+ drm_release_iommu_mapping(dev);
+err_free_private:
kfree(private);
return ret;
@@ -144,6 +139,8 @@ static int exynos_drm_unload(struct drm_device *dev)
exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
+ exynos_drm_remove_displays(dev);
+ exynos_drm_remove_managers(dev);
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
@@ -158,6 +155,41 @@ static const struct file_operations exynos_drm_gem_fops = {
.mmap = exynos_drm_gem_mmap_buffer,
};
+static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ int old_dpms = connector->dpms;
+
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+ /* Set the old mode back to the connector for resume */
+ connector->dpms = old_dpms;
+ }
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
+static int exynos_drm_resume(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, connector->dpms);
+ }
+
+ drm_helper_resume_force_mode(dev);
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_exynos_file_private *file_priv;
@@ -295,6 +327,8 @@ static struct drm_driver exynos_drm_driver = {
DRIVER_GEM | DRIVER_PRIME,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
+ .suspend = exynos_drm_suspend,
+ .resume = exynos_drm_resume,
.open = exynos_drm_open,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
@@ -329,6 +363,9 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
if (ret)
return ret;
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
return drm_platform_init(&exynos_drm_driver, pdev);
}
@@ -339,12 +376,67 @@ static int exynos_drm_platform_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int exynos_drm_sys_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ pm_message_t message;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ message.event = PM_EVENT_SUSPEND;
+ return exynos_drm_suspend(drm_dev, message);
+}
+
+static int exynos_drm_sys_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return exynos_drm_resume(drm_dev);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int exynos_drm_runtime_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ pm_message_t message;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ message.event = PM_EVENT_SUSPEND;
+ return exynos_drm_suspend(drm_dev, message);
+}
+
+static int exynos_drm_runtime_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ return 0;
+
+ return exynos_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops exynos_drm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
+ SET_RUNTIME_PM_OPS(exynos_drm_runtime_suspend,
+ exynos_drm_runtime_resume, NULL)
+};
+
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
.driver = {
.owner = THIS_MODULE,
.name = "exynos-drm",
+ .pm = &exynos_drm_pm_ops,
},
};
@@ -352,6 +444,18 @@ static int __init exynos_drm_init(void)
{
int ret;
+#ifdef CONFIG_DRM_EXYNOS_DP
+ ret = platform_driver_register(&dp_driver);
+ if (ret < 0)
+ goto out_dp;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_DSI
+ ret = platform_driver_register(&dsi_driver);
+ if (ret < 0)
+ goto out_dsi;
+#endif
+
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
@@ -365,13 +469,6 @@ static int __init exynos_drm_init(void)
ret = platform_driver_register(&mixer_driver);
if (ret < 0)
goto out_mixer;
- ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
- if (ret < 0)
- goto out_common_hdmi;
-
- ret = exynos_platform_device_hdmi_register();
- if (ret < 0)
- goto out_common_hdmi_dev;
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
@@ -464,10 +561,6 @@ out_vidi:
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
- exynos_platform_device_hdmi_unregister();
-out_common_hdmi_dev:
- platform_driver_unregister(&exynos_drm_common_hdmi_driver);
-out_common_hdmi:
platform_driver_unregister(&mixer_driver);
out_mixer:
platform_driver_unregister(&hdmi_driver);
@@ -478,6 +571,16 @@ out_hdmi:
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
+
+#ifdef CONFIG_DRM_EXYNOS_DSI
+ platform_driver_unregister(&dsi_driver);
+out_dsi:
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_DP
+ platform_driver_unregister(&dp_driver);
+out_dp:
+#endif
return ret;
}
@@ -509,8 +612,6 @@ static void __exit exynos_drm_exit(void)
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
- exynos_platform_device_hdmi_unregister();
- platform_driver_unregister(&exynos_drm_common_hdmi_driver);
platform_driver_unregister(&mixer_driver);
platform_driver_unregister(&hdmi_driver);
#endif
@@ -522,6 +623,14 @@ static void __exit exynos_drm_exit(void)
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
+
+#ifdef CONFIG_DRM_EXYNOS_DSI
+ platform_driver_unregister(&dsi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_DP
+ platform_driver_unregister(&dp_driver);
+#endif
}
module_init(exynos_drm_init);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index a8f9dba2a816..ce3e6a30deaa 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -54,22 +54,6 @@ enum exynos_drm_output_type {
};
/*
- * Exynos drm overlay ops structure.
- *
- * @mode_set: copy drm overlay info to hw specific overlay info.
- * @commit: apply hardware specific overlay data to registers.
- * @enable: enable hardware specific overlay.
- * @disable: disable hardware specific overlay.
- */
-struct exynos_drm_overlay_ops {
- void (*mode_set)(struct device *subdrv_dev,
- struct exynos_drm_overlay *overlay);
- void (*commit)(struct device *subdrv_dev, int zpos);
- void (*enable)(struct device *subdrv_dev, int zpos);
- void (*disable)(struct device *subdrv_dev, int zpos);
-};
-
-/*
* Exynos drm common overlay structure.
*
* @fb_x: offset x on a framebuffer to be displayed.
@@ -138,77 +122,110 @@ struct exynos_drm_overlay {
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
- * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
- * @is_connected: check for that display is connected or not.
- * @get_edid: get edid modes from display driver.
- * @get_panel: get panel object from display driver.
+ * @initialize: initializes the display with drm_dev
+ * @remove: cleans up the display for removal
+ * @mode_fixup: fix mode data comparing to hw specific display mode.
+ * @mode_set: convert drm_display_mode to hw specific display mode and
+ * would be called by encoder->mode_set().
* @check_mode: check if mode is valid or not.
- * @power_on: display device on or off.
+ * @dpms: display device on or off.
+ * @commit: apply changes to hw
*/
+struct exynos_drm_display;
struct exynos_drm_display_ops {
+ int (*initialize)(struct exynos_drm_display *display,
+ struct drm_device *drm_dev);
+ int (*create_connector)(struct exynos_drm_display *display,
+ struct drm_encoder *encoder);
+ void (*remove)(struct exynos_drm_display *display);
+ void (*mode_fixup)(struct exynos_drm_display *display,
+ struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*mode_set)(struct exynos_drm_display *display,
+ struct drm_display_mode *mode);
+ int (*check_mode)(struct exynos_drm_display *display,
+ struct drm_display_mode *mode);
+ void (*dpms)(struct exynos_drm_display *display, int mode);
+ void (*commit)(struct exynos_drm_display *display);
+};
+
+/*
+ * Exynos drm display structure, maps 1:1 with an encoder/connector
+ *
+ * @list: the list entry for this manager
+ * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
+ * @encoder: encoder object this display maps to
+ * @connector: connector object this display maps to
+ * @ops: pointer to callbacks for exynos drm specific functionality
+ * @ctx: A pointer to the display's implementation specific context
+ */
+struct exynos_drm_display {
+ struct list_head list;
enum exynos_drm_output_type type;
- bool (*is_connected)(struct device *dev);
- struct edid *(*get_edid)(struct device *dev,
- struct drm_connector *connector);
- void *(*get_panel)(struct device *dev);
- int (*check_mode)(struct device *dev, struct drm_display_mode *mode);
- int (*power_on)(struct device *dev, int mode);
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct exynos_drm_display_ops *ops;
+ void *ctx;
};
/*
* Exynos drm manager ops
*
+ * @initialize: initializes the manager with drm_dev
+ * @remove: cleans up the manager for removal
* @dpms: control device power.
- * @apply: set timing, vblank and overlay data to registers.
- * @mode_fixup: fix mode data comparing to hw specific display mode.
- * @mode_set: convert drm_display_mode to hw specific display mode and
- * would be called by encoder->mode_set().
- * @get_max_resol: get maximum resolution to specific hardware.
+ * @mode_fixup: fix mode data before applying it
+ * @mode_set: set the given mode to the manager
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
* @wait_for_vblank: wait for vblank interrupt to make sure that
* hardware overlay is updated.
+ * @win_mode_set: copy drm overlay info to hw specific overlay info.
+ * @win_commit: apply hardware specific overlay data to registers.
+ * @win_enable: enable hardware specific overlay.
+ * @win_disable: disable hardware specific overlay.
*/
+struct exynos_drm_manager;
struct exynos_drm_manager_ops {
- void (*dpms)(struct device *subdrv_dev, int mode);
- void (*apply)(struct device *subdrv_dev);
- void (*mode_fixup)(struct device *subdrv_dev,
- struct drm_connector *connector,
+ int (*initialize)(struct exynos_drm_manager *mgr,
+ struct drm_device *drm_dev, int pipe);
+ void (*remove)(struct exynos_drm_manager *mgr);
+ void (*dpms)(struct exynos_drm_manager *mgr, int mode);
+ bool (*mode_fixup)(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
- void (*mode_set)(struct device *subdrv_dev, void *mode);
- void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
- unsigned int *height);
- void (*commit)(struct device *subdrv_dev);
- int (*enable_vblank)(struct device *subdrv_dev);
- void (*disable_vblank)(struct device *subdrv_dev);
- void (*wait_for_vblank)(struct device *subdrv_dev);
+ void (*mode_set)(struct exynos_drm_manager *mgr,
+ const struct drm_display_mode *mode);
+ void (*commit)(struct exynos_drm_manager *mgr);
+ int (*enable_vblank)(struct exynos_drm_manager *mgr);
+ void (*disable_vblank)(struct exynos_drm_manager *mgr);
+ void (*wait_for_vblank)(struct exynos_drm_manager *mgr);
+ void (*win_mode_set)(struct exynos_drm_manager *mgr,
+ struct exynos_drm_overlay *overlay);
+ void (*win_commit)(struct exynos_drm_manager *mgr, int zpos);
+ void (*win_enable)(struct exynos_drm_manager *mgr, int zpos);
+ void (*win_disable)(struct exynos_drm_manager *mgr, int zpos);
};
/*
- * Exynos drm common manager structure.
+ * Exynos drm common manager structure, maps 1:1 with a crtc
*
- * @dev: pointer to device object for subdrv device driver.
- * sub drivers such as display controller or hdmi driver,
- * have their own device object.
- * @ops: pointer to callbacks for exynos drm specific framebuffer.
- * these callbacks should be set by specific drivers such fimd
- * or hdmi driver and are used to control hardware global registers.
- * @overlay_ops: pointer to callbacks for exynos drm specific framebuffer.
- * these callbacks should be set by specific drivers such fimd
- * or hdmi driver and are used to control hardware overlay reigsters.
- * @display: pointer to callbacks for exynos drm specific framebuffer.
- * these callbacks should be set by specific drivers such fimd
- * or hdmi driver and are used to control display devices such as
- * analog tv, digital tv and lcd panel and also get timing data for them.
+ * @list: the list entry for this manager
+ * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
+ * @drm_dev: pointer to the drm device
+ * @pipe: the pipe number for this crtc/manager
+ * @ops: pointer to callbacks for exynos drm specific functionality
+ * @ctx: A pointer to the manager's implementation specific context
*/
struct exynos_drm_manager {
- struct device *dev;
+ struct list_head list;
+ enum exynos_drm_output_type type;
+ struct drm_device *drm_dev;
int pipe;
struct exynos_drm_manager_ops *ops;
- struct exynos_drm_overlay_ops *overlay_ops;
- struct exynos_drm_display_ops *display_ops;
+ void *ctx;
};
struct exynos_drm_g2d_private {
@@ -271,14 +288,11 @@ struct exynos_drm_private {
* by probe callback.
* @open: this would be called with drm device file open.
* @close: this would be called with drm device file close.
- * @encoder: encoder object owned by this sub driver.
- * @connector: connector object owned by this sub driver.
*/
struct exynos_drm_subdrv {
struct list_head list;
struct device *dev;
struct drm_device *drm_dev;
- struct exynos_drm_manager *manager;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *drm_dev, struct device *dev);
@@ -286,9 +300,6 @@ struct exynos_drm_subdrv {
struct drm_file *file);
void (*close)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
-
- struct drm_encoder *encoder;
- struct drm_connector *connector;
};
/*
@@ -303,6 +314,16 @@ int exynos_drm_device_register(struct drm_device *dev);
*/
int exynos_drm_device_unregister(struct drm_device *dev);
+int exynos_drm_initialize_managers(struct drm_device *dev);
+void exynos_drm_remove_managers(struct drm_device *dev);
+int exynos_drm_initialize_displays(struct drm_device *dev);
+void exynos_drm_remove_displays(struct drm_device *dev);
+
+int exynos_drm_manager_register(struct exynos_drm_manager *manager);
+int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
+int exynos_drm_display_register(struct exynos_drm_display *display);
+int exynos_drm_display_unregister(struct exynos_drm_display *display);
+
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
@@ -338,6 +359,16 @@ int exynos_platform_device_ipp_register(void);
*/
void exynos_platform_device_ipp_unregister(void);
+#ifdef CONFIG_DRM_EXYNOS_DPI
+int exynos_dpi_probe(struct device *dev);
+int exynos_dpi_remove(struct device *dev);
+#else
+static inline int exynos_dpi_probe(struct device *dev) { return 0; }
+static inline int exynos_dpi_remove(struct device *dev) { return 0; }
+#endif
+
+extern struct platform_driver dp_driver;
+extern struct platform_driver dsi_driver;
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
new file mode 100644
index 000000000000..eb73e3bf2a0c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -0,0 +1,1524 @@
+/*
+ * Samsung SoC MIPI DSI Master driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * Contacts: Tomasz Figa <t.figa@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+#include "exynos_drm_drv.h"
+
+/* returns true iff both arguments logically differs */
+#define NEQV(a, b) (!(a) ^ !(b))
+
+#define DSIM_STATUS_REG 0x0 /* Status register */
+#define DSIM_SWRST_REG 0x4 /* Software reset register */
+#define DSIM_CLKCTRL_REG 0x8 /* Clock control register */
+#define DSIM_TIMEOUT_REG 0xc /* Time out register */
+#define DSIM_CONFIG_REG 0x10 /* Configuration register */
+#define DSIM_ESCMODE_REG 0x14 /* Escape mode register */
+
+/* Main display image resolution register */
+#define DSIM_MDRESOL_REG 0x18
+#define DSIM_MVPORCH_REG 0x1c /* Main display Vporch register */
+#define DSIM_MHPORCH_REG 0x20 /* Main display Hporch register */
+#define DSIM_MSYNC_REG 0x24 /* Main display sync area register */
+
+/* Sub display image resolution register */
+#define DSIM_SDRESOL_REG 0x28
+#define DSIM_INTSRC_REG 0x2c /* Interrupt source register */
+#define DSIM_INTMSK_REG 0x30 /* Interrupt mask register */
+#define DSIM_PKTHDR_REG 0x34 /* Packet Header FIFO register */
+#define DSIM_PAYLOAD_REG 0x38 /* Payload FIFO register */
+#define DSIM_RXFIFO_REG 0x3c /* Read FIFO register */
+#define DSIM_FIFOTHLD_REG 0x40 /* FIFO threshold level register */
+#define DSIM_FIFOCTRL_REG 0x44 /* FIFO status and control register */
+
+/* FIFO memory AC characteristic register */
+#define DSIM_PLLCTRL_REG 0x4c /* PLL control register */
+#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */
+#define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */
+#define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */
+
+/* DSIM_STATUS */
+#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
+#define DSIM_STOP_STATE_CLK (1 << 8)
+#define DSIM_TX_READY_HS_CLK (1 << 10)
+#define DSIM_PLL_STABLE (1 << 31)
+
+/* DSIM_SWRST */
+#define DSIM_FUNCRST (1 << 16)
+#define DSIM_SWRST (1 << 0)
+
+/* DSIM_TIMEOUT */
+#define DSIM_LPDR_TIMEOUT(x) ((x) << 0)
+#define DSIM_BTA_TIMEOUT(x) ((x) << 16)
+
+/* DSIM_CLKCTRL */
+#define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0)
+#define DSIM_ESC_PRESCALER_MASK (0xffff << 0)
+#define DSIM_LANE_ESC_CLK_EN_CLK (1 << 19)
+#define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20)
+#define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20)
+#define DSIM_BYTE_CLKEN (1 << 24)
+#define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25)
+#define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25)
+#define DSIM_PLL_BYPASS (1 << 27)
+#define DSIM_ESC_CLKEN (1 << 28)
+#define DSIM_TX_REQUEST_HSCLK (1 << 31)
+
+/* DSIM_CONFIG */
+#define DSIM_LANE_EN_CLK (1 << 0)
+#define DSIM_LANE_EN(x) (((x) & 0xf) << 1)
+#define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5)
+#define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8)
+#define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12)
+#define DSIM_SUB_VC (((x) & 0x3) << 16)
+#define DSIM_MAIN_VC (((x) & 0x3) << 18)
+#define DSIM_HSA_MODE (1 << 20)
+#define DSIM_HBP_MODE (1 << 21)
+#define DSIM_HFP_MODE (1 << 22)
+#define DSIM_HSE_MODE (1 << 23)
+#define DSIM_AUTO_MODE (1 << 24)
+#define DSIM_VIDEO_MODE (1 << 25)
+#define DSIM_BURST_MODE (1 << 26)
+#define DSIM_SYNC_INFORM (1 << 27)
+#define DSIM_EOT_DISABLE (1 << 28)
+#define DSIM_MFLUSH_VS (1 << 29)
+
+/* DSIM_ESCMODE */
+#define DSIM_TX_TRIGGER_RST (1 << 4)
+#define DSIM_TX_LPDT_LP (1 << 6)
+#define DSIM_CMD_LPDT_LP (1 << 7)
+#define DSIM_FORCE_BTA (1 << 16)
+#define DSIM_FORCE_STOP_STATE (1 << 20)
+#define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21)
+#define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21)
+
+/* DSIM_MDRESOL */
+#define DSIM_MAIN_STAND_BY (1 << 31)
+#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16)
+#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0)
+
+/* DSIM_MVPORCH */
+#define DSIM_CMD_ALLOW(x) ((x) << 28)
+#define DSIM_STABLE_VFP(x) ((x) << 16)
+#define DSIM_MAIN_VBP(x) ((x) << 0)
+#define DSIM_CMD_ALLOW_MASK (0xf << 28)
+#define DSIM_STABLE_VFP_MASK (0x7ff << 16)
+#define DSIM_MAIN_VBP_MASK (0x7ff << 0)
+
+/* DSIM_MHPORCH */
+#define DSIM_MAIN_HFP(x) ((x) << 16)
+#define DSIM_MAIN_HBP(x) ((x) << 0)
+#define DSIM_MAIN_HFP_MASK ((0xffff) << 16)
+#define DSIM_MAIN_HBP_MASK ((0xffff) << 0)
+
+/* DSIM_MSYNC */
+#define DSIM_MAIN_VSA(x) ((x) << 22)
+#define DSIM_MAIN_HSA(x) ((x) << 0)
+#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22)
+#define DSIM_MAIN_HSA_MASK ((0xffff) << 0)
+
+/* DSIM_SDRESOL */
+#define DSIM_SUB_STANDY(x) ((x) << 31)
+#define DSIM_SUB_VRESOL(x) ((x) << 16)
+#define DSIM_SUB_HRESOL(x) ((x) << 0)
+#define DSIM_SUB_STANDY_MASK ((0x1) << 31)
+#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16)
+#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0)
+
+/* DSIM_INTSRC */
+#define DSIM_INT_PLL_STABLE (1 << 31)
+#define DSIM_INT_SW_RST_RELEASE (1 << 30)
+#define DSIM_INT_SFR_FIFO_EMPTY (1 << 29)
+#define DSIM_INT_BTA (1 << 25)
+#define DSIM_INT_FRAME_DONE (1 << 24)
+#define DSIM_INT_RX_TIMEOUT (1 << 21)
+#define DSIM_INT_BTA_TIMEOUT (1 << 20)
+#define DSIM_INT_RX_DONE (1 << 18)
+#define DSIM_INT_RX_TE (1 << 17)
+#define DSIM_INT_RX_ACK (1 << 16)
+#define DSIM_INT_RX_ECC_ERR (1 << 15)
+#define DSIM_INT_RX_CRC_ERR (1 << 14)
+
+/* DSIM_FIFOCTRL */
+#define DSIM_RX_DATA_FULL (1 << 25)
+#define DSIM_RX_DATA_EMPTY (1 << 24)
+#define DSIM_SFR_HEADER_FULL (1 << 23)
+#define DSIM_SFR_HEADER_EMPTY (1 << 22)
+#define DSIM_SFR_PAYLOAD_FULL (1 << 21)
+#define DSIM_SFR_PAYLOAD_EMPTY (1 << 20)
+#define DSIM_I80_HEADER_FULL (1 << 19)
+#define DSIM_I80_HEADER_EMPTY (1 << 18)
+#define DSIM_I80_PAYLOAD_FULL (1 << 17)
+#define DSIM_I80_PAYLOAD_EMPTY (1 << 16)
+#define DSIM_SD_HEADER_FULL (1 << 15)
+#define DSIM_SD_HEADER_EMPTY (1 << 14)
+#define DSIM_SD_PAYLOAD_FULL (1 << 13)
+#define DSIM_SD_PAYLOAD_EMPTY (1 << 12)
+#define DSIM_MD_HEADER_FULL (1 << 11)
+#define DSIM_MD_HEADER_EMPTY (1 << 10)
+#define DSIM_MD_PAYLOAD_FULL (1 << 9)
+#define DSIM_MD_PAYLOAD_EMPTY (1 << 8)
+#define DSIM_RX_FIFO (1 << 4)
+#define DSIM_SFR_FIFO (1 << 3)
+#define DSIM_I80_FIFO (1 << 2)
+#define DSIM_SD_FIFO (1 << 1)
+#define DSIM_MD_FIFO (1 << 0)
+
+/* DSIM_PHYACCHR */
+#define DSIM_AFC_EN (1 << 14)
+#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5)
+
+/* DSIM_PLLCTRL */
+#define DSIM_FREQ_BAND(x) ((x) << 24)
+#define DSIM_PLL_EN (1 << 23)
+#define DSIM_PLL_P(x) ((x) << 13)
+#define DSIM_PLL_M(x) ((x) << 4)
+#define DSIM_PLL_S(x) ((x) << 1)
+
+#define DSI_MAX_BUS_WIDTH 4
+#define DSI_NUM_VIRTUAL_CHANNELS 4
+#define DSI_TX_FIFO_SIZE 2048
+#define DSI_RX_FIFO_SIZE 256
+#define DSI_XFER_TIMEOUT_MS 100
+#define DSI_RX_FIFO_EMPTY 0x30800002
+
+enum exynos_dsi_transfer_type {
+ EXYNOS_DSI_TX,
+ EXYNOS_DSI_RX,
+};
+
+struct exynos_dsi_transfer {
+ struct list_head list;
+ struct completion completed;
+ int result;
+ u8 data_id;
+ u8 data[2];
+ u16 flags;
+
+ const u8 *tx_payload;
+ u16 tx_len;
+ u16 tx_done;
+
+ u8 *rx_payload;
+ u16 rx_len;
+ u16 rx_done;
+};
+
+#define DSIM_STATE_ENABLED BIT(0)
+#define DSIM_STATE_INITIALIZED BIT(1)
+#define DSIM_STATE_CMD_LPM BIT(2)
+
+struct exynos_dsi {
+ struct mipi_dsi_host dsi_host;
+ struct drm_connector connector;
+ struct drm_encoder *encoder;
+ struct device_node *panel_node;
+ struct drm_panel *panel;
+ struct device *dev;
+
+ void __iomem *reg_base;
+ struct phy *phy;
+ struct clk *pll_clk;
+ struct clk *bus_clk;
+ struct regulator_bulk_data supplies[2];
+ int irq;
+
+ u32 pll_clk_rate;
+ u32 burst_clk_rate;
+ u32 esc_clk_rate;
+ u32 lanes;
+ u32 mode_flags;
+ u32 format;
+ struct videomode vm;
+
+ int state;
+ struct drm_property *brightness;
+ struct completion completed;
+
+ spinlock_t transfer_lock; /* protects transfer_list */
+ struct list_head transfer_list;
+};
+
+#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
+#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+
+static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
+{
+ if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
+ return;
+
+ dev_err(dsi->dev, "timeout waiting for reset\n");
+}
+
+static void exynos_dsi_reset(struct exynos_dsi *dsi)
+{
+ reinit_completion(&dsi->completed);
+ writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
+}
+
+#ifndef MHZ
+#define MHZ (1000*1000)
+#endif
+
+static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
+ unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
+{
+ unsigned long best_freq = 0;
+ u32 min_delta = 0xffffffff;
+ u8 p_min, p_max;
+ u8 _p, uninitialized_var(best_p);
+ u16 _m, uninitialized_var(best_m);
+ u8 _s, uninitialized_var(best_s);
+
+ p_min = DIV_ROUND_UP(fin, (12 * MHZ));
+ p_max = fin / (6 * MHZ);
+
+ for (_p = p_min; _p <= p_max; ++_p) {
+ for (_s = 0; _s <= 5; ++_s) {
+ u64 tmp;
+ u32 delta;
+
+ tmp = (u64)fout * (_p << _s);
+ do_div(tmp, fin);
+ _m = tmp;
+ if (_m < 41 || _m > 125)
+ continue;
+
+ tmp = (u64)_m * fin;
+ do_div(tmp, _p);
+ if (tmp < 500 * MHZ || tmp > 1000 * MHZ)
+ continue;
+
+ tmp = (u64)_m * fin;
+ do_div(tmp, _p << _s);
+
+ delta = abs(fout - tmp);
+ if (delta < min_delta) {
+ best_p = _p;
+ best_m = _m;
+ best_s = _s;
+ min_delta = delta;
+ best_freq = tmp;
+ }
+ }
+ }
+
+ if (best_freq) {
+ *p = best_p;
+ *m = best_m;
+ *s = best_s;
+ }
+
+ return best_freq;
+}
+
+static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
+ unsigned long freq)
+{
+ static const unsigned long freq_bands[] = {
+ 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
+ 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
+ 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
+ 770 * MHZ, 870 * MHZ, 950 * MHZ,
+ };
+ unsigned long fin, fout;
+ int timeout, band;
+ u8 p, s;
+ u16 m;
+ u32 reg;
+
+ clk_set_rate(dsi->pll_clk, dsi->pll_clk_rate);
+
+ fin = clk_get_rate(dsi->pll_clk);
+ if (!fin) {
+ dev_err(dsi->dev, "failed to get PLL clock frequency\n");
+ return 0;
+ }
+
+ dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin);
+
+ fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
+ if (!fout) {
+ dev_err(dsi->dev,
+ "failed to find PLL PMS for requested frequency\n");
+ return -EFAULT;
+ }
+
+ for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
+ if (fout < freq_bands[band])
+ break;
+
+ dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout,
+ p, m, s, band);
+
+ writel(500, dsi->reg_base + DSIM_PLLTMR_REG);
+
+ reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
+ | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
+ writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+
+ timeout = 1000;
+ do {
+ if (timeout-- == 0) {
+ dev_err(dsi->dev, "PLL failed to stabilize\n");
+ return -EFAULT;
+ }
+ reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+ } while ((reg & DSIM_PLL_STABLE) == 0);
+
+ return fout;
+}
+
+static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
+{
+ unsigned long hs_clk, byte_clk, esc_clk;
+ unsigned long esc_div;
+ u32 reg;
+
+ hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate);
+ if (!hs_clk) {
+ dev_err(dsi->dev, "failed to configure DSI PLL\n");
+ return -EFAULT;
+ }
+
+ byte_clk = hs_clk / 8;
+ esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
+ esc_clk = byte_clk / esc_div;
+
+ if (esc_clk > 20 * MHZ) {
+ ++esc_div;
+ esc_clk = byte_clk / esc_div;
+ }
+
+ dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
+ hs_clk, byte_clk, esc_clk);
+
+ reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+ reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
+ | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
+ | DSIM_BYTE_CLK_SRC_MASK);
+ reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
+ | DSIM_ESC_PRESCALER(esc_div)
+ | DSIM_LANE_ESC_CLK_EN_CLK
+ | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
+ | DSIM_BYTE_CLK_SRC(0)
+ | DSIM_TX_REQUEST_HSCLK;
+ writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+
+ return 0;
+}
+
+static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
+{
+ u32 reg;
+
+ reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+ reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
+ | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
+ writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+
+ reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG);
+ reg &= ~DSIM_PLL_EN;
+ writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+}
+
+static int exynos_dsi_init_link(struct exynos_dsi *dsi)
+{
+ int timeout;
+ u32 reg;
+ u32 lanes_mask;
+
+ /* Initialize FIFO pointers */
+ reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+ reg &= ~0x1f;
+ writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+ usleep_range(9000, 11000);
+
+ reg |= 0x1f;
+ writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+ usleep_range(9000, 11000);
+
+ /* DSI configuration */
+ reg = 0;
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ reg |= DSIM_VIDEO_MODE;
+
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
+ reg |= DSIM_MFLUSH_VS;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
+ reg |= DSIM_EOT_DISABLE;
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+ reg |= DSIM_SYNC_INFORM;
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+ reg |= DSIM_BURST_MODE;
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
+ reg |= DSIM_AUTO_MODE;
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
+ reg |= DSIM_HSE_MODE;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
+ reg |= DSIM_HFP_MODE;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
+ reg |= DSIM_HBP_MODE;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
+ reg |= DSIM_HSA_MODE;
+ }
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
+ break;
+ default:
+ dev_err(dsi->dev, "invalid pixel format\n");
+ return -EINVAL;
+ }
+
+ reg |= DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1);
+
+ writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+ reg |= DSIM_LANE_EN_CLK;
+ writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+ lanes_mask = BIT(dsi->lanes) - 1;
+ reg |= DSIM_LANE_EN(lanes_mask);
+ writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+ /* Check clock and data lane state are stop state */
+ timeout = 100;
+ do {
+ if (timeout-- == 0) {
+ dev_err(dsi->dev, "waiting for bus lanes timed out\n");
+ return -EFAULT;
+ }
+
+ reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+ if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
+ != DSIM_STOP_STATE_DAT(lanes_mask))
+ continue;
+ } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
+
+ reg = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+ reg &= ~DSIM_STOP_STATE_CNT_MASK;
+ reg |= DSIM_STOP_STATE_CNT(0xf);
+ writel(reg, dsi->reg_base + DSIM_ESCMODE_REG);
+
+ reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
+ writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG);
+
+ return 0;
+}
+
+static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
+{
+ struct videomode *vm = &dsi->vm;
+ u32 reg;
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ reg = DSIM_CMD_ALLOW(0xf)
+ | DSIM_STABLE_VFP(vm->vfront_porch)
+ | DSIM_MAIN_VBP(vm->vback_porch);
+ writel(reg, dsi->reg_base + DSIM_MVPORCH_REG);
+
+ reg = DSIM_MAIN_HFP(vm->hfront_porch)
+ | DSIM_MAIN_HBP(vm->hback_porch);
+ writel(reg, dsi->reg_base + DSIM_MHPORCH_REG);
+
+ reg = DSIM_MAIN_VSA(vm->vsync_len)
+ | DSIM_MAIN_HSA(vm->hsync_len);
+ writel(reg, dsi->reg_base + DSIM_MSYNC_REG);
+ }
+
+ reg = DSIM_MAIN_HRESOL(vm->hactive) | DSIM_MAIN_VRESOL(vm->vactive);
+ writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+
+ dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive);
+}
+
+static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
+{
+ u32 reg;
+
+ reg = readl(dsi->reg_base + DSIM_MDRESOL_REG);
+ if (enable)
+ reg |= DSIM_MAIN_STAND_BY;
+ else
+ reg &= ~DSIM_MAIN_STAND_BY;
+ writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+}
+
+static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
+{
+ int timeout = 2000;
+
+ do {
+ u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+ if (!(reg & DSIM_SFR_HEADER_FULL))
+ return 0;
+
+ if (!cond_resched())
+ usleep_range(950, 1050);
+ } while (--timeout);
+
+ return -ETIMEDOUT;
+}
+
+static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
+{
+ u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+
+ if (lpm)
+ v |= DSIM_CMD_LPDT_LP;
+ else
+ v &= ~DSIM_CMD_LPDT_LP;
+
+ writel(v, dsi->reg_base + DSIM_ESCMODE_REG);
+}
+
+static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
+{
+ u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+
+ v |= DSIM_FORCE_BTA;
+ writel(v, dsi->reg_base + DSIM_ESCMODE_REG);
+}
+
+static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
+ struct exynos_dsi_transfer *xfer)
+{
+ struct device *dev = dsi->dev;
+ const u8 *payload = xfer->tx_payload + xfer->tx_done;
+ u16 length = xfer->tx_len - xfer->tx_done;
+ bool first = !xfer->tx_done;
+ u32 reg;
+
+ dev_dbg(dev, "< xfer %p: tx len %u, done %u, rx len %u, done %u\n",
+ xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+ if (length > DSI_TX_FIFO_SIZE)
+ length = DSI_TX_FIFO_SIZE;
+
+ xfer->tx_done += length;
+
+ /* Send payload */
+ while (length >= 4) {
+ reg = (payload[3] << 24) | (payload[2] << 16)
+ | (payload[1] << 8) | payload[0];
+ writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+ payload += 4;
+ length -= 4;
+ }
+
+ reg = 0;
+ switch (length) {
+ case 3:
+ reg |= payload[2] << 16;
+ /* Fall through */
+ case 2:
+ reg |= payload[1] << 8;
+ /* Fall through */
+ case 1:
+ reg |= payload[0];
+ writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+ break;
+ case 0:
+ /* Do nothing */
+ break;
+ }
+
+ /* Send packet header */
+ if (!first)
+ return;
+
+ reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->data_id;
+ if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
+ dev_err(dev, "waiting for header FIFO timed out\n");
+ return;
+ }
+
+ if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
+ dsi->state & DSIM_STATE_CMD_LPM)) {
+ exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
+ dsi->state ^= DSIM_STATE_CMD_LPM;
+ }
+
+ writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+
+ if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
+ exynos_dsi_force_bta(dsi);
+}
+
+static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
+ struct exynos_dsi_transfer *xfer)
+{
+ u8 *payload = xfer->rx_payload + xfer->rx_done;
+ bool first = !xfer->rx_done;
+ struct device *dev = dsi->dev;
+ u16 length;
+ u32 reg;
+
+ if (first) {
+ reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+
+ switch (reg & 0x3f) {
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+ if (xfer->rx_len >= 2) {
+ payload[1] = reg >> 16;
+ ++xfer->rx_done;
+ }
+ /* Fall through */
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+ payload[0] = reg >> 8;
+ ++xfer->rx_done;
+ xfer->rx_len = xfer->rx_done;
+ xfer->result = 0;
+ goto clear_fifo;
+ case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+ dev_err(dev, "DSI Error Report: 0x%04x\n",
+ (reg >> 8) & 0xffff);
+ xfer->result = 0;
+ goto clear_fifo;
+ }
+
+ length = (reg >> 8) & 0xffff;
+ if (length > xfer->rx_len) {
+ dev_err(dev,
+ "response too long (%u > %u bytes), stripping\n",
+ xfer->rx_len, length);
+ length = xfer->rx_len;
+ } else if (length < xfer->rx_len)
+ xfer->rx_len = length;
+ }
+
+ length = xfer->rx_len - xfer->rx_done;
+ xfer->rx_done += length;
+
+ /* Receive payload */
+ while (length >= 4) {
+ reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ payload[0] = (reg >> 0) & 0xff;
+ payload[1] = (reg >> 8) & 0xff;
+ payload[2] = (reg >> 16) & 0xff;
+ payload[3] = (reg >> 24) & 0xff;
+ payload += 4;
+ length -= 4;
+ }
+
+ if (length) {
+ reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ switch (length) {
+ case 3:
+ payload[2] = (reg >> 16) & 0xff;
+ /* Fall through */
+ case 2:
+ payload[1] = (reg >> 8) & 0xff;
+ /* Fall through */
+ case 1:
+ payload[0] = reg & 0xff;
+ }
+ }
+
+ if (xfer->rx_done == xfer->rx_len)
+ xfer->result = 0;
+
+clear_fifo:
+ length = DSI_RX_FIFO_SIZE / 4;
+ do {
+ reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ if (reg == DSI_RX_FIFO_EMPTY)
+ break;
+ } while (--length);
+}
+
+static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
+{
+ unsigned long flags;
+ struct exynos_dsi_transfer *xfer;
+ bool start = false;
+
+again:
+ spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+ if (list_empty(&dsi->transfer_list)) {
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+ return;
+ }
+
+ xfer = list_first_entry(&dsi->transfer_list,
+ struct exynos_dsi_transfer, list);
+
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+ if (xfer->tx_len && xfer->tx_done == xfer->tx_len)
+ /* waiting for RX */
+ return;
+
+ exynos_dsi_send_to_fifo(dsi, xfer);
+
+ if (xfer->tx_len || xfer->rx_len)
+ return;
+
+ xfer->result = 0;
+ complete(&xfer->completed);
+
+ spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+ list_del_init(&xfer->list);
+ start = !list_empty(&dsi->transfer_list);
+
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+ if (start)
+ goto again;
+}
+
+static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
+{
+ struct exynos_dsi_transfer *xfer;
+ unsigned long flags;
+ bool start = true;
+
+ spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+ if (list_empty(&dsi->transfer_list)) {
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+ return false;
+ }
+
+ xfer = list_first_entry(&dsi->transfer_list,
+ struct exynos_dsi_transfer, list);
+
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+ dev_dbg(dsi->dev,
+ "> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n",
+ xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+ if (xfer->tx_done != xfer->tx_len)
+ return true;
+
+ if (xfer->rx_done != xfer->rx_len)
+ exynos_dsi_read_from_fifo(dsi, xfer);
+
+ if (xfer->rx_done != xfer->rx_len)
+ return true;
+
+ spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+ list_del_init(&xfer->list);
+ start = !list_empty(&dsi->transfer_list);
+
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+ if (!xfer->rx_len)
+ xfer->result = 0;
+ complete(&xfer->completed);
+
+ return start;
+}
+
+static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
+ struct exynos_dsi_transfer *xfer)
+{
+ unsigned long flags;
+ bool start;
+
+ spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+ if (!list_empty(&dsi->transfer_list) &&
+ xfer == list_first_entry(&dsi->transfer_list,
+ struct exynos_dsi_transfer, list)) {
+ list_del_init(&xfer->list);
+ start = !list_empty(&dsi->transfer_list);
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+ if (start)
+ exynos_dsi_transfer_start(dsi);
+ return;
+ }
+
+ list_del_init(&xfer->list);
+
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+static int exynos_dsi_transfer(struct exynos_dsi *dsi,
+ struct exynos_dsi_transfer *xfer)
+{
+ unsigned long flags;
+ bool stopped;
+
+ xfer->tx_done = 0;
+ xfer->rx_done = 0;
+ xfer->result = -ETIMEDOUT;
+ init_completion(&xfer->completed);
+
+ spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+ stopped = list_empty(&dsi->transfer_list);
+ list_add_tail(&xfer->list, &dsi->transfer_list);
+
+ spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+ if (stopped)
+ exynos_dsi_transfer_start(dsi);
+
+ wait_for_completion_timeout(&xfer->completed,
+ msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+ if (xfer->result == -ETIMEDOUT) {
+ exynos_dsi_remove_transfer(dsi, xfer);
+ dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 2, xfer->data,
+ xfer->tx_len, xfer->tx_payload);
+ return -ETIMEDOUT;
+ }
+
+ /* Also covers hardware timeout condition */
+ return xfer->result;
+}
+
+static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
+{
+ struct exynos_dsi *dsi = dev_id;
+ u32 status;
+
+ status = readl(dsi->reg_base + DSIM_INTSRC_REG);
+ if (!status) {
+ static unsigned long int j;
+ if (printk_timed_ratelimit(&j, 500))
+ dev_warn(dsi->dev, "spurious interrupt\n");
+ return IRQ_HANDLED;
+ }
+ writel(status, dsi->reg_base + DSIM_INTSRC_REG);
+
+ if (status & DSIM_INT_SW_RST_RELEASE) {
+ u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY);
+ writel(mask, dsi->reg_base + DSIM_INTMSK_REG);
+ complete(&dsi->completed);
+ return IRQ_HANDLED;
+ }
+
+ if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY)))
+ return IRQ_HANDLED;
+
+ if (exynos_dsi_transfer_finish(dsi))
+ exynos_dsi_transfer_start(dsi);
+
+ return IRQ_HANDLED;
+}
+
+static int exynos_dsi_init(struct exynos_dsi *dsi)
+{
+ exynos_dsi_enable_clock(dsi);
+ exynos_dsi_reset(dsi);
+ enable_irq(dsi->irq);
+ exynos_dsi_wait_for_reset(dsi);
+ exynos_dsi_init_link(dsi);
+
+ return 0;
+}
+
+static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct exynos_dsi *dsi = host_to_dsi(host);
+
+ dsi->lanes = device->lanes;
+ dsi->format = device->format;
+ dsi->mode_flags = device->mode_flags;
+ dsi->panel_node = device->dev.of_node;
+
+ if (dsi->connector.dev)
+ drm_helper_hpd_irq_event(dsi->connector.dev);
+
+ return 0;
+}
+
+static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct exynos_dsi *dsi = host_to_dsi(host);
+
+ dsi->panel_node = NULL;
+
+ if (dsi->connector.dev)
+ drm_helper_hpd_irq_event(dsi->connector.dev);
+
+ return 0;
+}
+
+/* distinguish between short and long DSI packet types */
+static bool exynos_dsi_is_short_dsi_type(u8 type)
+{
+ return (type & 0x0f) <= 8;
+}
+
+static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
+ struct mipi_dsi_msg *msg)
+{
+ struct exynos_dsi *dsi = host_to_dsi(host);
+ struct exynos_dsi_transfer xfer;
+ int ret;
+
+ if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
+ ret = exynos_dsi_init(dsi);
+ if (ret)
+ return ret;
+ dsi->state |= DSIM_STATE_INITIALIZED;
+ }
+
+ if (msg->tx_len == 0)
+ return -EINVAL;
+
+ xfer.data_id = msg->type | (msg->channel << 6);
+
+ if (exynos_dsi_is_short_dsi_type(msg->type)) {
+ const char *tx_buf = msg->tx_buf;
+
+ if (msg->tx_len > 2)
+ return -EINVAL;
+ xfer.tx_len = 0;
+ xfer.data[0] = tx_buf[0];
+ xfer.data[1] = (msg->tx_len == 2) ? tx_buf[1] : 0;
+ } else {
+ xfer.tx_len = msg->tx_len;
+ xfer.data[0] = msg->tx_len & 0xff;
+ xfer.data[1] = msg->tx_len >> 8;
+ xfer.tx_payload = msg->tx_buf;
+ }
+
+ xfer.rx_len = msg->rx_len;
+ xfer.rx_payload = msg->rx_buf;
+ xfer.flags = msg->flags;
+
+ ret = exynos_dsi_transfer(dsi, &xfer);
+ return (ret < 0) ? ret : xfer.rx_done;
+}
+
+static const struct mipi_dsi_host_ops exynos_dsi_ops = {
+ .attach = exynos_dsi_host_attach,
+ .detach = exynos_dsi_host_detach,
+ .transfer = exynos_dsi_host_transfer,
+};
+
+static int exynos_dsi_poweron(struct exynos_dsi *dsi)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+ if (ret < 0) {
+ dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dsi->bus_clk);
+ if (ret < 0) {
+ dev_err(dsi->dev, "cannot enable bus clock %d\n", ret);
+ goto err_bus_clk;
+ }
+
+ ret = clk_prepare_enable(dsi->pll_clk);
+ if (ret < 0) {
+ dev_err(dsi->dev, "cannot enable pll clock %d\n", ret);
+ goto err_pll_clk;
+ }
+
+ ret = phy_power_on(dsi->phy);
+ if (ret < 0) {
+ dev_err(dsi->dev, "cannot enable phy %d\n", ret);
+ goto err_phy;
+ }
+
+ return 0;
+
+err_phy:
+ clk_disable_unprepare(dsi->pll_clk);
+err_pll_clk:
+ clk_disable_unprepare(dsi->bus_clk);
+err_bus_clk:
+ regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+ return ret;
+}
+
+static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
+{
+ int ret;
+
+ usleep_range(10000, 20000);
+
+ if (dsi->state & DSIM_STATE_INITIALIZED) {
+ dsi->state &= ~DSIM_STATE_INITIALIZED;
+
+ exynos_dsi_disable_clock(dsi);
+
+ disable_irq(dsi->irq);
+ }
+
+ dsi->state &= ~DSIM_STATE_CMD_LPM;
+
+ phy_power_off(dsi->phy);
+
+ clk_disable_unprepare(dsi->pll_clk);
+ clk_disable_unprepare(dsi->bus_clk);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+ if (ret < 0)
+ dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
+}
+
+static int exynos_dsi_enable(struct exynos_dsi *dsi)
+{
+ int ret;
+
+ if (dsi->state & DSIM_STATE_ENABLED)
+ return 0;
+
+ ret = exynos_dsi_poweron(dsi);
+ if (ret < 0)
+ return ret;
+
+ ret = drm_panel_enable(dsi->panel);
+ if (ret < 0) {
+ exynos_dsi_poweroff(dsi);
+ return ret;
+ }
+
+ exynos_dsi_set_display_mode(dsi);
+ exynos_dsi_set_display_enable(dsi, true);
+
+ dsi->state |= DSIM_STATE_ENABLED;
+
+ return 0;
+}
+
+static void exynos_dsi_disable(struct exynos_dsi *dsi)
+{
+ if (!(dsi->state & DSIM_STATE_ENABLED))
+ return;
+
+ exynos_dsi_set_display_enable(dsi, false);
+ drm_panel_disable(dsi->panel);
+ exynos_dsi_poweroff(dsi);
+
+ dsi->state &= ~DSIM_STATE_ENABLED;
+}
+
+static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
+{
+ struct exynos_dsi *dsi = display->ctx;
+
+ if (dsi->panel) {
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ exynos_dsi_enable(dsi);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ exynos_dsi_disable(dsi);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static enum drm_connector_status
+exynos_dsi_detect(struct drm_connector *connector, bool force)
+{
+ struct exynos_dsi *dsi = connector_to_dsi(connector);
+
+ if (!dsi->panel) {
+ dsi->panel = of_drm_find_panel(dsi->panel_node);
+ if (dsi->panel)
+ drm_panel_attach(dsi->panel, &dsi->connector);
+ } else if (!dsi->panel_node) {
+ struct exynos_drm_display *display;
+
+ display = platform_get_drvdata(to_platform_device(dsi->dev));
+ exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF);
+ drm_panel_detach(dsi->panel);
+ dsi->panel = NULL;
+ }
+
+ if (dsi->panel)
+ return connector_status_connected;
+
+ return connector_status_disconnected;
+}
+
+static void exynos_dsi_connector_destroy(struct drm_connector *connector)
+{
+}
+
+static struct drm_connector_funcs exynos_dsi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = exynos_dsi_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = exynos_dsi_connector_destroy,
+};
+
+static int exynos_dsi_get_modes(struct drm_connector *connector)
+{
+ struct exynos_dsi *dsi = connector_to_dsi(connector);
+
+ if (dsi->panel)
+ return dsi->panel->funcs->get_modes(dsi->panel);
+
+ return 0;
+}
+
+static int exynos_dsi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+exynos_dsi_best_encoder(struct drm_connector *connector)
+{
+ struct exynos_dsi *dsi = connector_to_dsi(connector);
+
+ return dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
+ .get_modes = exynos_dsi_get_modes,
+ .mode_valid = exynos_dsi_mode_valid,
+ .best_encoder = exynos_dsi_best_encoder,
+};
+
+static int exynos_dsi_create_connector(struct exynos_drm_display *display,
+ struct drm_encoder *encoder)
+{
+ struct exynos_dsi *dsi = display->ctx;
+ struct drm_connector *connector = &dsi->connector;
+ int ret;
+
+ dsi->encoder = encoder;
+
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(encoder->dev, connector,
+ &exynos_dsi_connector_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static void exynos_dsi_mode_set(struct exynos_drm_display *display,
+ struct drm_display_mode *mode)
+{
+ struct exynos_dsi *dsi = display->ctx;
+ struct videomode *vm = &dsi->vm;
+
+ vm->hactive = mode->hdisplay;
+ vm->vactive = mode->vdisplay;
+ vm->vfront_porch = mode->vsync_start - mode->vdisplay;
+ vm->vback_porch = mode->vtotal - mode->vsync_end;
+ vm->vsync_len = mode->vsync_end - mode->vsync_start;
+ vm->hfront_porch = mode->hsync_start - mode->hdisplay;
+ vm->hback_porch = mode->htotal - mode->hsync_end;
+ vm->hsync_len = mode->hsync_end - mode->hsync_start;
+}
+
+static struct exynos_drm_display_ops exynos_dsi_display_ops = {
+ .create_connector = exynos_dsi_create_connector,
+ .mode_set = exynos_dsi_mode_set,
+ .dpms = exynos_dsi_dpms
+};
+
+static struct exynos_drm_display exynos_dsi_display = {
+ .type = EXYNOS_DISPLAY_TYPE_LCD,
+ .ops = &exynos_dsi_display_ops,
+};
+
+/* of_* functions will be removed after merge of of_graph patches */
+static struct device_node *
+of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg)
+{
+ struct device_node *np;
+
+ for_each_child_of_node(parent, np) {
+ u32 r;
+
+ if (!np->name || of_node_cmp(np->name, name))
+ continue;
+
+ if (of_property_read_u32(np, "reg", &r) < 0)
+ r = 0;
+
+ if (reg == r)
+ break;
+ }
+
+ return np;
+}
+
+static struct device_node *of_graph_get_port_by_reg(struct device_node *parent,
+ u32 reg)
+{
+ struct device_node *ports, *port;
+
+ ports = of_get_child_by_name(parent, "ports");
+ if (ports)
+ parent = ports;
+
+ port = of_get_child_by_name_reg(parent, "port", reg);
+
+ of_node_put(ports);
+
+ return port;
+}
+
+static struct device_node *
+of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg)
+{
+ return of_get_child_by_name_reg(port, "endpoint", reg);
+}
+
+static int exynos_dsi_of_read_u32(const struct device_node *np,
+ const char *propname, u32 *out_value)
+{
+ int ret = of_property_read_u32(np, propname, out_value);
+
+ if (ret < 0)
+ pr_err("%s: failed to get '%s' property\n", np->full_name,
+ propname);
+
+ return ret;
+}
+
+enum {
+ DSI_PORT_IN,
+ DSI_PORT_OUT
+};
+
+static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
+{
+ struct device *dev = dsi->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *port, *ep;
+ int ret;
+
+ ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
+ &dsi->pll_clk_rate);
+ if (ret < 0)
+ return ret;
+
+ port = of_graph_get_port_by_reg(node, DSI_PORT_OUT);
+ if (!port) {
+ dev_err(dev, "no output port specified\n");
+ return -EINVAL;
+ }
+
+ ep = of_graph_get_endpoint_by_reg(port, 0);
+ of_node_put(port);
+ if (!ep) {
+ dev_err(dev, "no endpoint specified in output port\n");
+ return -EINVAL;
+ }
+
+ ret = exynos_dsi_of_read_u32(ep, "samsung,burst-clock-frequency",
+ &dsi->burst_clk_rate);
+ if (ret < 0)
+ goto end;
+
+ ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency",
+ &dsi->esc_clk_rate);
+
+end:
+ of_node_put(ep);
+
+ return ret;
+}
+
+static int exynos_dsi_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct exynos_dsi *dsi;
+ int ret;
+
+ dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi) {
+ dev_err(&pdev->dev, "failed to allocate dsi object.\n");
+ return -ENOMEM;
+ }
+
+ init_completion(&dsi->completed);
+ spin_lock_init(&dsi->transfer_lock);
+ INIT_LIST_HEAD(&dsi->transfer_list);
+
+ dsi->dsi_host.ops = &exynos_dsi_ops;
+ dsi->dsi_host.dev = &pdev->dev;
+
+ dsi->dev = &pdev->dev;
+
+ ret = exynos_dsi_parse_dt(dsi);
+ if (ret)
+ return ret;
+
+ dsi->supplies[0].supply = "vddcore";
+ dsi->supplies[1].supply = "vddio";
+ ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(dsi->supplies),
+ dsi->supplies);
+ if (ret) {
+ dev_info(&pdev->dev, "failed to get regulators: %d\n", ret);
+ return -EPROBE_DEFER;
+ }
+
+ dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
+ if (IS_ERR(dsi->pll_clk)) {
+ dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
+ return -EPROBE_DEFER;
+ }
+
+ dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(dsi->bus_clk)) {
+ dev_info(&pdev->dev, "failed to get dsi bus clock\n");
+ return -EPROBE_DEFER;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (!dsi->reg_base) {
+ dev_err(&pdev->dev, "failed to remap io region\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ dsi->phy = devm_phy_get(&pdev->dev, "dsim");
+ if (IS_ERR(dsi->phy)) {
+ dev_info(&pdev->dev, "failed to get dsim phy\n");
+ return -EPROBE_DEFER;
+ }
+
+ dsi->irq = platform_get_irq(pdev, 0);
+ if (dsi->irq < 0) {
+ dev_err(&pdev->dev, "failed to request dsi irq resource\n");
+ return dsi->irq;
+ }
+
+ irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL,
+ exynos_dsi_irq, IRQF_ONESHOT,
+ dev_name(&pdev->dev), dsi);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request dsi irq\n");
+ return ret;
+ }
+
+ exynos_dsi_display.ctx = dsi;
+
+ platform_set_drvdata(pdev, &exynos_dsi_display);
+ exynos_drm_display_register(&exynos_dsi_display);
+
+ return mipi_dsi_host_register(&dsi->dsi_host);
+}
+
+static int exynos_dsi_remove(struct platform_device *pdev)
+{
+ struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+
+ exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
+
+ exynos_drm_display_unregister(&exynos_dsi_display);
+ mipi_dsi_host_unregister(&dsi->dsi_host);
+
+ return 0;
+}
+
+#if CONFIG_PM_SLEEP
+static int exynos_dsi_resume(struct device *dev)
+{
+ struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+
+ if (dsi->state & DSIM_STATE_ENABLED) {
+ dsi->state &= ~DSIM_STATE_ENABLED;
+ exynos_dsi_enable(dsi);
+ }
+
+ return 0;
+}
+
+static int exynos_dsi_suspend(struct device *dev)
+{
+ struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+
+ if (dsi->state & DSIM_STATE_ENABLED) {
+ exynos_dsi_disable(dsi);
+ dsi->state |= DSIM_STATE_ENABLED;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
+};
+
+static struct of_device_id exynos_dsi_of_match[] = {
+ { .compatible = "samsung,exynos4210-mipi-dsi" },
+ { }
+};
+
+struct platform_driver dsi_driver = {
+ .probe = exynos_dsi_probe,
+ .remove = exynos_dsi_remove,
+ .driver = {
+ .name = "exynos-dsi",
+ .owner = THIS_MODULE,
+ .pm = &exynos_dsi_pm_ops,
+ .of_match_table = exynos_dsi_of_match,
+ },
+};
+
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 06f1b2a09da7..7e282e3d6038 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -17,7 +17,6 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
-#include "exynos_drm_connector.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
@@ -26,72 +25,22 @@
* exynos specific encoder structure.
*
* @drm_encoder: encoder object.
- * @manager: specific encoder has its own manager to control a hardware
- * appropriately and we can access a hardware drawing on this manager.
- * @dpms: store the encoder dpms value.
- * @updated: indicate whether overlay data updating is needed or not.
+ * @display: the display structure that maps to this encoder
*/
struct exynos_drm_encoder {
- struct drm_crtc *old_crtc;
struct drm_encoder drm_encoder;
- struct exynos_drm_manager *manager;
- int dpms;
- bool updated;
+ struct exynos_drm_display *display;
};
-static void exynos_drm_connector_power(struct drm_encoder *encoder, int mode)
-{
- struct drm_device *dev = encoder->dev;
- struct drm_connector *connector;
-
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (exynos_drm_best_encoder(connector) == encoder) {
- DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
- connector->base.id, mode);
-
- exynos_drm_display_power(connector, mode);
- }
- }
-}
-
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
{
- struct drm_device *dev = encoder->dev;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display *display = exynos_encoder->display;
DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
- if (exynos_encoder->dpms == mode) {
- DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
- return;
- }
-
- mutex_lock(&dev->struct_mutex);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- if (manager_ops && manager_ops->apply)
- if (!exynos_encoder->updated)
- manager_ops->apply(manager->dev);
-
- exynos_drm_connector_power(encoder, mode);
- exynos_encoder->dpms = mode;
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- exynos_drm_connector_power(encoder, mode);
- exynos_encoder->dpms = mode;
- exynos_encoder->updated = false;
- break;
- default:
- DRM_ERROR("unspecified mode %d\n", mode);
- break;
- }
-
- mutex_unlock(&dev->struct_mutex);
+ if (display->ops->dpms)
+ display->ops->dpms(display, mode);
}
static bool
@@ -100,87 +49,31 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display *display = exynos_encoder->display;
struct drm_connector *connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder)
- if (manager_ops && manager_ops->mode_fixup)
- manager_ops->mode_fixup(manager->dev, connector,
- mode, adjusted_mode);
+ if (connector->encoder != encoder)
+ continue;
+
+ if (display->ops->mode_fixup)
+ display->ops->mode_fixup(display, connector, mode,
+ adjusted_mode);
}
return true;
}
-static void disable_plane_to_crtc(struct drm_device *dev,
- struct drm_crtc *old_crtc,
- struct drm_crtc *new_crtc)
-{
- struct drm_plane *plane;
-
- /*
- * if old_crtc isn't same as encoder->crtc then it means that
- * user changed crtc id to another one so the plane to old_crtc
- * should be disabled and plane->crtc should be set to new_crtc
- * (encoder->crtc)
- */
- list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
- if (plane->crtc == old_crtc) {
- /*
- * do not change below call order.
- *
- * plane->funcs->disable_plane call checks
- * if encoder->crtc is same as plane->crtc and if same
- * then overlay_ops->disable callback will be called
- * to diasble current hw overlay so plane->crtc should
- * have new_crtc because new_crtc was set to
- * encoder->crtc in advance.
- */
- plane->crtc = new_crtc;
- plane->funcs->disable_plane(plane);
- }
- }
-}
-
static void exynos_drm_encoder_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_connector *connector;
- struct exynos_drm_manager *manager;
- struct exynos_drm_manager_ops *manager_ops;
-
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
- struct exynos_drm_encoder *exynos_encoder;
-
- exynos_encoder = to_exynos_encoder(encoder);
-
- if (exynos_encoder->old_crtc != encoder->crtc &&
- exynos_encoder->old_crtc) {
-
- /*
- * disable a plane to old crtc and change
- * crtc of the plane to new one.
- */
- disable_plane_to_crtc(dev,
- exynos_encoder->old_crtc,
- encoder->crtc);
- }
-
- manager = exynos_drm_get_manager(encoder);
- manager_ops = manager->ops;
-
- if (manager_ops && manager_ops->mode_set)
- manager_ops->mode_set(manager->dev,
- adjusted_mode);
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display *display = exynos_encoder->display;
- exynos_encoder->old_crtc = encoder->crtc;
- }
- }
+ if (display->ops->mode_set)
+ display->ops->mode_set(display, adjusted_mode);
}
static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
@@ -191,53 +84,15 @@ static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_manager *manager = exynos_encoder->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
-
- if (manager_ops && manager_ops->commit)
- manager_ops->commit(manager->dev);
-
- /*
- * this will avoid one issue that overlay data is updated to
- * real hardware two times.
- * And this variable will be used to check if the data was
- * already updated or not by exynos_drm_encoder_dpms function.
- */
- exynos_encoder->updated = true;
-
- /*
- * In case of setcrtc, there is no way to update encoder's dpms
- * so update it here.
- */
- exynos_encoder->dpms = DRM_MODE_DPMS_ON;
-}
+ struct exynos_drm_display *display = exynos_encoder->display;
-void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb)
-{
- struct exynos_drm_encoder *exynos_encoder;
- struct exynos_drm_manager_ops *ops;
- struct drm_device *dev = fb->dev;
- struct drm_encoder *encoder;
+ if (display->ops->dpms)
+ display->ops->dpms(display, DRM_MODE_DPMS_ON);
- /*
- * make sure that overlay data are updated to real hardware
- * for all encoders.
- */
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- exynos_encoder = to_exynos_encoder(encoder);
- ops = exynos_encoder->manager->ops;
-
- /*
- * wait for vblank interrupt
- * - this makes sure that overlay data are updated to
- * real hardware.
- */
- if (ops->wait_for_vblank)
- ops->wait_for_vblank(exynos_encoder->manager->dev);
- }
+ if (display->ops->commit)
+ display->ops->commit(display);
}
-
static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
{
struct drm_plane *plane;
@@ -246,7 +101,7 @@ static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
/* all planes connected to this encoder should be also disabled. */
- list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
if (plane->crtc == encoder->crtc)
plane->funcs->disable_plane(plane);
}
@@ -263,10 +118,7 @@ static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
{
- struct exynos_drm_encoder *exynos_encoder =
- to_exynos_encoder(encoder);
-
- exynos_encoder->manager->pipe = -1;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
drm_encoder_cleanup(encoder);
kfree(exynos_encoder);
@@ -281,13 +133,12 @@ static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
struct drm_encoder *clone;
struct drm_device *dev = encoder->dev;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display_ops *display_ops =
- exynos_encoder->manager->display_ops;
+ struct exynos_drm_display *display = exynos_encoder->display;
unsigned int clone_mask = 0;
int cnt = 0;
list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
- switch (display_ops->type) {
+ switch (display->type) {
case EXYNOS_DISPLAY_TYPE_LCD:
case EXYNOS_DISPLAY_TYPE_HDMI:
case EXYNOS_DISPLAY_TYPE_VIDI:
@@ -311,24 +162,20 @@ void exynos_drm_encoder_setup(struct drm_device *dev)
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
- struct exynos_drm_manager *manager,
- unsigned int possible_crtcs)
+ struct exynos_drm_display *display,
+ unsigned long possible_crtcs)
{
struct drm_encoder *encoder;
struct exynos_drm_encoder *exynos_encoder;
- if (!manager || !possible_crtcs)
- return NULL;
-
- if (!manager->dev)
+ if (!possible_crtcs)
return NULL;
exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
if (!exynos_encoder)
return NULL;
- exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
- exynos_encoder->manager = manager;
+ exynos_encoder->display = display;
encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
@@ -344,149 +191,7 @@ exynos_drm_encoder_create(struct drm_device *dev,
return encoder;
}
-struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder)
-{
- return to_exynos_encoder(encoder)->manager;
-}
-
-void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
- void (*fn)(struct drm_encoder *, void *))
-{
- struct drm_device *dev = crtc->dev;
- struct drm_encoder *encoder;
- struct exynos_drm_private *private = dev->dev_private;
- struct exynos_drm_manager *manager;
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- /*
- * if crtc is detached from encoder, check pipe,
- * otherwise check crtc attached to encoder
- */
- if (!encoder->crtc) {
- manager = to_exynos_encoder(encoder)->manager;
- if (manager->pipe < 0 ||
- private->crtc[manager->pipe] != crtc)
- continue;
- } else {
- if (encoder->crtc != crtc)
- continue;
- }
-
- fn(encoder, data);
- }
-}
-
-void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
- int crtc = *(int *)data;
-
- if (manager->pipe != crtc)
- return;
-
- if (manager_ops->enable_vblank)
- manager_ops->enable_vblank(manager->dev);
-}
-
-void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
- int crtc = *(int *)data;
-
- if (manager->pipe != crtc)
- return;
-
- if (manager_ops->disable_vblank)
- manager_ops->disable_vblank(manager->dev);
-}
-
-void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_manager *manager = exynos_encoder->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
- int mode = *(int *)data;
-
- if (manager_ops && manager_ops->dpms)
- manager_ops->dpms(manager->dev, mode);
-
- /*
- * if this condition is ok then it means that the crtc is already
- * detached from encoder and last function for detaching is properly
- * done, so clear pipe from manager to prevent repeated call.
- */
- if (mode > DRM_MODE_DPMS_ON) {
- if (!encoder->crtc)
- manager->pipe = -1;
- }
-}
-
-void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- int pipe = *(int *)data;
-
- /*
- * when crtc is detached from encoder, this pipe is used
- * to select manager operation
- */
- manager->pipe = pipe;
-}
-
-void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- struct exynos_drm_overlay *overlay = data;
-
- if (overlay_ops && overlay_ops->mode_set)
- overlay_ops->mode_set(manager->dev, overlay);
-}
-
-void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
+struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- int zpos = DEFAULT_ZPOS;
-
- if (data)
- zpos = *(int *)data;
-
- if (overlay_ops && overlay_ops->commit)
- overlay_ops->commit(manager->dev, zpos);
-}
-
-void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- int zpos = DEFAULT_ZPOS;
-
- if (data)
- zpos = *(int *)data;
-
- if (overlay_ops && overlay_ops->enable)
- overlay_ops->enable(manager->dev, zpos);
-}
-
-void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
-{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- int zpos = DEFAULT_ZPOS;
-
- if (data)
- zpos = *(int *)data;
-
- if (overlay_ops && overlay_ops->disable)
- overlay_ops->disable(manager->dev, zpos);
+ return to_exynos_encoder(encoder)->display;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index 89e2fb0770af..b7a1620a7e79 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -18,20 +18,8 @@ struct exynos_drm_manager;
void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
- struct exynos_drm_manager *mgr,
- unsigned int possible_crtcs);
-struct exynos_drm_manager *
-exynos_drm_get_manager(struct drm_encoder *encoder);
-void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
- void (*fn)(struct drm_encoder *, void *));
-void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
-void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb);
+ struct exynos_drm_display *mgr,
+ unsigned long possible_crtcs);
+struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder);
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index ea39e0ef2ae4..65a22cad7b36 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -20,9 +20,10 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
+#include "exynos_drm_fbdev.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_iommu.h"
-#include "exynos_drm_encoder.h"
+#include "exynos_drm_crtc.h"
#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
@@ -71,7 +72,7 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
unsigned int i;
/* make sure that overlay data are updated before relesing fb. */
- exynos_drm_encoder_complete_scanout(fb);
+ exynos_drm_crtc_complete_scanout(fb);
drm_framebuffer_cleanup(fb);
@@ -300,6 +301,8 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
if (fb_helper)
drm_fb_helper_hotplug_event(fb_helper);
+ else
+ exynos_drm_fbdev_init(dev);
}
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index e7c2f2d07f19..addbf7536da4 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -90,7 +90,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
/* RGB formats use only one buffer */
buffer = exynos_drm_fb_buffer(fb, 0);
if (!buffer) {
- DRM_LOG_KMS("buffer is null.\n");
+ DRM_DEBUG_KMS("buffer is null.\n");
return -EFAULT;
}
@@ -237,6 +237,24 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create,
};
+bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ bool ret = false;
+
+ mutex_lock(&dev->mode_config.mutex);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->status != connector_status_connected)
+ continue;
+
+ ret = true;
+ break;
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
+}
+
int exynos_drm_fbdev_init(struct drm_device *dev)
{
struct exynos_drm_fbdev *fbdev;
@@ -248,6 +266,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return 0;
+ if (!exynos_drm_fbdev_is_anything_connected(dev))
+ return 0;
+
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev)
return -ENOMEM;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index a20440ce32e6..40fd6ccfcd6f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -62,7 +62,7 @@
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
-#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev))
struct fimd_driver_data {
unsigned int timing_base;
@@ -105,20 +105,18 @@ struct fimd_win_data {
};
struct fimd_context {
- struct exynos_drm_subdrv subdrv;
- int irq;
- struct drm_crtc *crtc;
+ struct device *dev;
+ struct drm_device *drm_dev;
struct clk *bus_clk;
struct clk *lcd_clk;
void __iomem *regs;
+ struct drm_display_mode mode;
struct fimd_win_data win_data[WINDOWS_NR];
- unsigned int clkdiv;
unsigned int default_win;
unsigned long irq_flags;
- u32 vidcon0;
u32 vidcon1;
bool suspended;
- struct mutex lock;
+ int pipe;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
@@ -145,153 +143,147 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->data;
}
-static bool fimd_display_is_connected(struct device *dev)
+static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
+ struct drm_device *drm_dev, int pipe)
{
- /* TODO. */
+ struct fimd_context *ctx = mgr->ctx;
- return true;
-}
+ ctx->drm_dev = drm_dev;
+ ctx->pipe = pipe;
-static void *fimd_get_panel(struct device *dev)
-{
- struct fimd_context *ctx = get_fimd_context(dev);
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = true, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ drm_dev->irq_enabled = true;
- return &ctx->panel;
-}
+ /*
+ * with vblank_disable_allowed = true, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = true;
-static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode)
-{
- /* TODO. */
+ /* attach this sub driver to iommu mapping if supported. */
+ if (is_drm_iommu_supported(ctx->drm_dev))
+ drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
return 0;
}
-static int fimd_display_power_on(struct device *dev, int mode)
+static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
{
- /* TODO */
+ struct fimd_context *ctx = mgr->ctx;
- return 0;
+ /* detach this sub driver from iommu mapping if supported. */
+ if (is_drm_iommu_supported(ctx->drm_dev))
+ drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
}
-static struct exynos_drm_display_ops fimd_display_ops = {
- .type = EXYNOS_DISPLAY_TYPE_LCD,
- .is_connected = fimd_display_is_connected,
- .get_panel = fimd_get_panel,
- .check_mode = fimd_check_mode,
- .power_on = fimd_display_power_on,
-};
-
-static void fimd_dpms(struct device *subdrv_dev, int mode)
+static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
+ const struct drm_display_mode *mode)
{
- struct fimd_context *ctx = get_fimd_context(subdrv_dev);
+ unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
+ u32 clkdiv;
- DRM_DEBUG_KMS("%d\n", mode);
+ /* Find the clock divider value that gets us closest to ideal_clk */
+ clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
- mutex_lock(&ctx->lock);
+ return (clkdiv < 0x100) ? clkdiv : 0xff;
+}
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- /*
- * enable fimd hardware only if suspended status.
- *
- * P.S. fimd_dpms function would be called at booting time so
- * clk_enable could be called double time.
- */
- if (ctx->suspended)
- pm_runtime_get_sync(subdrv_dev);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- if (!ctx->suspended)
- pm_runtime_put_sync(subdrv_dev);
- break;
- default:
- DRM_DEBUG_KMS("unspecified mode %d\n", mode);
- break;
- }
+static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ if (adjusted_mode->vrefresh == 0)
+ adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
- mutex_unlock(&ctx->lock);
+ return true;
}
-static void fimd_apply(struct device *subdrv_dev)
+static void fimd_mode_set(struct exynos_drm_manager *mgr,
+ const struct drm_display_mode *in_mode)
{
- struct fimd_context *ctx = get_fimd_context(subdrv_dev);
- struct exynos_drm_manager *mgr = ctx->subdrv.manager;
- struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
- struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
- struct fimd_win_data *win_data;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- win_data = &ctx->win_data[i];
- if (win_data->enabled && (ovl_ops && ovl_ops->commit))
- ovl_ops->commit(subdrv_dev, i);
- }
+ struct fimd_context *ctx = mgr->ctx;
- if (mgr_ops && mgr_ops->commit)
- mgr_ops->commit(subdrv_dev);
+ drm_mode_copy(&ctx->mode, in_mode);
}
-static void fimd_commit(struct device *dev)
+static void fimd_commit(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = get_fimd_context(dev);
- struct exynos_drm_panel_info *panel = &ctx->panel;
- struct videomode *vm = &panel->vm;
+ struct fimd_context *ctx = mgr->ctx;
+ struct drm_display_mode *mode = &ctx->mode;
struct fimd_driver_data *driver_data;
- u32 val;
+ u32 val, clkdiv, vidcon1;
+ int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
driver_data = ctx->driver_data;
if (ctx->suspended)
return;
- /* setup polarity values from machine code. */
- writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
+ /* nothing to do if we haven't set the mode yet */
+ if (mode->htotal == 0 || mode->vtotal == 0)
+ return;
+
+ /* setup polarity values */
+ vidcon1 = ctx->vidcon1;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ vidcon1 |= VIDCON1_INV_VSYNC;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ vidcon1 |= VIDCON1_INV_HSYNC;
+ writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
/* setup vertical timing values. */
- val = VIDTCON0_VBPD(vm->vback_porch - 1) |
- VIDTCON0_VFPD(vm->vfront_porch - 1) |
- VIDTCON0_VSPW(vm->vsync_len - 1);
+ vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
+ vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
+
+ val = VIDTCON0_VBPD(vbpd - 1) |
+ VIDTCON0_VFPD(vfpd - 1) |
+ VIDTCON0_VSPW(vsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
/* setup horizontal timing values. */
- val = VIDTCON1_HBPD(vm->hback_porch - 1) |
- VIDTCON1_HFPD(vm->hfront_porch - 1) |
- VIDTCON1_HSPW(vm->hsync_len - 1);
+ hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
+ hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
+
+ val = VIDTCON1_HBPD(hbpd - 1) |
+ VIDTCON1_HFPD(hfpd - 1) |
+ VIDTCON1_HSPW(hsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
/* setup horizontal and vertical display size. */
- val = VIDTCON2_LINEVAL(vm->vactive - 1) |
- VIDTCON2_HOZVAL(vm->hactive - 1) |
- VIDTCON2_LINEVAL_E(vm->vactive - 1) |
- VIDTCON2_HOZVAL_E(vm->hactive - 1);
+ val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
+ VIDTCON2_HOZVAL(mode->hdisplay - 1) |
+ VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
+ VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
- /* setup clock source, clock divider, enable dma. */
- val = ctx->vidcon0;
- val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
-
- if (ctx->driver_data->has_clksel) {
- val &= ~VIDCON0_CLKSEL_MASK;
- val |= VIDCON0_CLKSEL_LCD;
- }
-
- if (ctx->clkdiv > 1)
- val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
- else
- val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
-
/*
* fields of register with prefix '_F' would be updated
* at vsync(same as dma start)
*/
- val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
+ val = VIDCON0_ENVID | VIDCON0_ENVID_F;
+
+ if (ctx->driver_data->has_clksel)
+ val |= VIDCON0_CLKSEL_LCD;
+
+ clkdiv = fimd_calc_clkdiv(ctx, mode);
+ if (clkdiv > 1)
+ val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
+
writel(val, ctx->regs + VIDCON0);
}
-static int fimd_enable_vblank(struct device *dev)
+static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
u32 val;
if (ctx->suspended)
@@ -314,9 +306,9 @@ static int fimd_enable_vblank(struct device *dev)
return 0;
}
-static void fimd_disable_vblank(struct device *dev)
+static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
u32 val;
if (ctx->suspended)
@@ -332,9 +324,9 @@ static void fimd_disable_vblank(struct device *dev)
}
}
-static void fimd_wait_for_vblank(struct device *dev)
+static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
@@ -351,25 +343,16 @@ static void fimd_wait_for_vblank(struct device *dev)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
-static struct exynos_drm_manager_ops fimd_manager_ops = {
- .dpms = fimd_dpms,
- .apply = fimd_apply,
- .commit = fimd_commit,
- .enable_vblank = fimd_enable_vblank,
- .disable_vblank = fimd_disable_vblank,
- .wait_for_vblank = fimd_wait_for_vblank,
-};
-
-static void fimd_win_mode_set(struct device *dev,
- struct exynos_drm_overlay *overlay)
+static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
+ struct exynos_drm_overlay *overlay)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int win;
unsigned long offset;
if (!overlay) {
- dev_err(dev, "overlay is NULL\n");
+ DRM_ERROR("overlay is NULL\n");
return;
}
@@ -409,9 +392,8 @@ static void fimd_win_mode_set(struct device *dev,
overlay->fb_width, overlay->crtc_width);
}
-static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
+static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
{
- struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data = &ctx->win_data[win];
unsigned long val;
@@ -467,9 +449,8 @@ static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
writel(val, ctx->regs + WINCON(win));
}
-static void fimd_win_set_colkey(struct device *dev, unsigned int win)
+static void fimd_win_set_colkey(struct fimd_context *ctx, unsigned int win)
{
- struct fimd_context *ctx = get_fimd_context(dev);
unsigned int keycon0 = 0, keycon1 = 0;
keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
@@ -508,9 +489,9 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
writel(val, ctx->regs + reg);
}
-static void fimd_win_commit(struct device *dev, int zpos)
+static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int win = zpos;
unsigned long val, alpha, size;
@@ -528,6 +509,12 @@ static void fimd_win_commit(struct device *dev, int zpos)
win_data = &ctx->win_data[win];
+ /* If suspended, enable this on resume */
+ if (ctx->suspended) {
+ win_data->resume = true;
+ return;
+ }
+
/*
* SHADOWCON/PRTCON register is used for enabling timing.
*
@@ -605,11 +592,11 @@ static void fimd_win_commit(struct device *dev, int zpos)
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
}
- fimd_win_set_pixfmt(dev, win);
+ fimd_win_set_pixfmt(ctx, win);
/* hardware window 0 doesn't support color key. */
if (win != 0)
- fimd_win_set_colkey(dev, win);
+ fimd_win_set_colkey(ctx, win);
/* wincon */
val = readl(ctx->regs + WINCON(win));
@@ -628,9 +615,9 @@ static void fimd_win_commit(struct device *dev, int zpos)
win_data->enabled = true;
}
-static void fimd_win_disable(struct device *dev, int zpos)
+static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int win = zpos;
u32 val;
@@ -669,132 +656,6 @@ static void fimd_win_disable(struct device *dev, int zpos)
win_data->enabled = false;
}
-static struct exynos_drm_overlay_ops fimd_overlay_ops = {
- .mode_set = fimd_win_mode_set,
- .commit = fimd_win_commit,
- .disable = fimd_win_disable,
-};
-
-static struct exynos_drm_manager fimd_manager = {
- .pipe = -1,
- .ops = &fimd_manager_ops,
- .overlay_ops = &fimd_overlay_ops,
- .display_ops = &fimd_display_ops,
-};
-
-static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
-{
- struct fimd_context *ctx = (struct fimd_context *)dev_id;
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct drm_device *drm_dev = subdrv->drm_dev;
- struct exynos_drm_manager *manager = subdrv->manager;
- u32 val;
-
- val = readl(ctx->regs + VIDINTCON1);
-
- if (val & VIDINTCON1_INT_FRAME)
- /* VSYNC interrupt */
- writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
-
- /* check the crtc is detached already from encoder */
- if (manager->pipe < 0)
- goto out;
-
- drm_handle_vblank(drm_dev, manager->pipe);
- exynos_drm_crtc_finish_pageflip(drm_dev, manager->pipe);
-
- /* set wait vsync event to zero and wake up queue. */
- if (atomic_read(&ctx->wait_vsync_event)) {
- atomic_set(&ctx->wait_vsync_event, 0);
- wake_up(&ctx->wait_vsync_queue);
- }
-out:
- return IRQ_HANDLED;
-}
-
-static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
-{
- /*
- * enable drm irq mode.
- * - with irq_enabled = true, we can use the vblank feature.
- *
- * P.S. note that we wouldn't use drm irq handler but
- * just specific driver own one instead because
- * drm framework supports only one irq handler.
- */
- drm_dev->irq_enabled = true;
-
- /*
- * with vblank_disable_allowed = true, vblank interrupt will be disabled
- * by drm timer once a current process gives up ownership of
- * vblank event.(after drm_vblank_put function is called)
- */
- drm_dev->vblank_disable_allowed = true;
-
- /* attach this sub driver to iommu mapping if supported. */
- if (is_drm_iommu_supported(drm_dev))
- drm_iommu_attach_device(drm_dev, dev);
-
- return 0;
-}
-
-static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
-{
- /* detach this sub driver from iommu mapping if supported. */
- if (is_drm_iommu_supported(drm_dev))
- drm_iommu_detach_device(drm_dev, dev);
-}
-
-static int fimd_configure_clocks(struct fimd_context *ctx, struct device *dev)
-{
- struct videomode *vm = &ctx->panel.vm;
- unsigned long clk;
-
- ctx->bus_clk = devm_clk_get(dev, "fimd");
- if (IS_ERR(ctx->bus_clk)) {
- dev_err(dev, "failed to get bus clock\n");
- return PTR_ERR(ctx->bus_clk);
- }
-
- ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
- if (IS_ERR(ctx->lcd_clk)) {
- dev_err(dev, "failed to get lcd clock\n");
- return PTR_ERR(ctx->lcd_clk);
- }
-
- clk = clk_get_rate(ctx->lcd_clk);
- if (clk == 0) {
- dev_err(dev, "error getting sclk_fimd clock rate\n");
- return -EINVAL;
- }
-
- if (vm->pixelclock == 0) {
- unsigned long c;
- c = vm->hactive + vm->hback_porch + vm->hfront_porch +
- vm->hsync_len;
- c *= vm->vactive + vm->vback_porch + vm->vfront_porch +
- vm->vsync_len;
- vm->pixelclock = c * FIMD_DEFAULT_FRAMERATE;
- if (vm->pixelclock == 0) {
- dev_err(dev, "incorrect display timings\n");
- return -EINVAL;
- }
- dev_warn(dev, "pixel clock recalculated to %luHz (%dHz frame rate)\n",
- vm->pixelclock, FIMD_DEFAULT_FRAMERATE);
- }
- ctx->clkdiv = DIV_ROUND_UP(clk, vm->pixelclock);
- if (ctx->clkdiv > 256) {
- dev_warn(dev, "calculated pixel clock divider too high (%u), lowered to 256\n",
- ctx->clkdiv);
- ctx->clkdiv = 256;
- }
- vm->pixelclock = clk / ctx->clkdiv;
- DRM_DEBUG_KMS("pixel clock = %lu, clkdiv = %d\n", vm->pixelclock,
- ctx->clkdiv);
-
- return 0;
-}
-
static void fimd_clear_win(struct fimd_context *ctx, int win)
{
writel(0, ctx->regs + WINCON(win));
@@ -808,111 +669,190 @@ static void fimd_clear_win(struct fimd_context *ctx, int win)
fimd_shadow_protect_win(ctx, win, false);
}
-static int fimd_clock(struct fimd_context *ctx, bool enable)
+static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{
- if (enable) {
- int ret;
-
- ret = clk_prepare_enable(ctx->bus_clk);
- if (ret < 0)
- return ret;
+ struct fimd_context *ctx = mgr->ctx;
+ struct fimd_win_data *win_data;
+ int i;
- ret = clk_prepare_enable(ctx->lcd_clk);
- if (ret < 0) {
- clk_disable_unprepare(ctx->bus_clk);
- return ret;
- }
- } else {
- clk_disable_unprepare(ctx->lcd_clk);
- clk_disable_unprepare(ctx->bus_clk);
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ win_data->resume = win_data->enabled;
+ if (win_data->enabled)
+ fimd_win_disable(mgr, i);
}
-
- return 0;
+ fimd_wait_for_vblank(mgr);
}
-static void fimd_window_suspend(struct device *dev)
+static void fimd_window_resume(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
- win_data->resume = win_data->enabled;
- fimd_win_disable(dev, i);
+ win_data->enabled = win_data->resume;
+ win_data->resume = false;
}
- fimd_wait_for_vblank(dev);
}
-static void fimd_window_resume(struct device *dev)
+static void fimd_apply(struct exynos_drm_manager *mgr)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *ctx = mgr->ctx;
struct fimd_win_data *win_data;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
- win_data->enabled = win_data->resume;
- win_data->resume = false;
+ if (win_data->enabled)
+ fimd_win_commit(mgr, i);
}
+
+ fimd_commit(mgr);
}
-static int fimd_activate(struct fimd_context *ctx, bool enable)
+static int fimd_poweron(struct exynos_drm_manager *mgr)
{
- struct device *dev = ctx->subdrv.dev;
- if (enable) {
- int ret;
+ struct fimd_context *ctx = mgr->ctx;
+ int ret;
- ret = fimd_clock(ctx, true);
- if (ret < 0)
- return ret;
+ if (!ctx->suspended)
+ return 0;
- ctx->suspended = false;
+ ctx->suspended = false;
- /* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags))
- fimd_enable_vblank(dev);
+ pm_runtime_get_sync(ctx->dev);
- fimd_window_resume(dev);
- } else {
- fimd_window_suspend(dev);
+ ret = clk_prepare_enable(ctx->bus_clk);
+ if (ret < 0) {
+ DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
+ goto bus_clk_err;
+ }
+
+ ret = clk_prepare_enable(ctx->lcd_clk);
+ if (ret < 0) {
+ DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
+ goto lcd_clk_err;
+ }
- fimd_clock(ctx, false);
- ctx->suspended = true;
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags)) {
+ ret = fimd_enable_vblank(mgr);
+ if (ret) {
+ DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
+ goto enable_vblank_err;
+ }
}
+ fimd_window_resume(mgr);
+
+ fimd_apply(mgr);
+
return 0;
+
+enable_vblank_err:
+ clk_disable_unprepare(ctx->lcd_clk);
+lcd_clk_err:
+ clk_disable_unprepare(ctx->bus_clk);
+bus_clk_err:
+ ctx->suspended = true;
+ return ret;
}
-static int fimd_get_platform_data(struct fimd_context *ctx, struct device *dev)
+static int fimd_poweroff(struct exynos_drm_manager *mgr)
{
- struct videomode *vm;
- int ret;
+ struct fimd_context *ctx = mgr->ctx;
- vm = &ctx->panel.vm;
- ret = of_get_videomode(dev->of_node, vm, OF_USE_NATIVE_MODE);
- if (ret) {
- DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
- return ret;
- }
+ if (ctx->suspended)
+ return 0;
- if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW)
- ctx->vidcon1 |= VIDCON1_INV_VSYNC;
- if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW)
- ctx->vidcon1 |= VIDCON1_INV_HSYNC;
- if (vm->flags & DISPLAY_FLAGS_DE_LOW)
- ctx->vidcon1 |= VIDCON1_INV_VDEN;
- if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
- ctx->vidcon1 |= VIDCON1_INV_VCLK;
+ /*
+ * We need to make sure that all windows are disabled before we
+ * suspend that connector. Otherwise we might try to scan from
+ * a destroyed buffer later.
+ */
+ fimd_window_suspend(mgr);
+ clk_disable_unprepare(ctx->lcd_clk);
+ clk_disable_unprepare(ctx->bus_clk);
+
+ pm_runtime_put_sync(ctx->dev);
+
+ ctx->suspended = true;
return 0;
}
+static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
+{
+ DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ fimd_poweron(mgr);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ fimd_poweroff(mgr);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+}
+
+static struct exynos_drm_manager_ops fimd_manager_ops = {
+ .initialize = fimd_mgr_initialize,
+ .remove = fimd_mgr_remove,
+ .dpms = fimd_dpms,
+ .mode_fixup = fimd_mode_fixup,
+ .mode_set = fimd_mode_set,
+ .commit = fimd_commit,
+ .enable_vblank = fimd_enable_vblank,
+ .disable_vblank = fimd_disable_vblank,
+ .wait_for_vblank = fimd_wait_for_vblank,
+ .win_mode_set = fimd_win_mode_set,
+ .win_commit = fimd_win_commit,
+ .win_disable = fimd_win_disable,
+};
+
+static struct exynos_drm_manager fimd_manager = {
+ .type = EXYNOS_DISPLAY_TYPE_LCD,
+ .ops = &fimd_manager_ops,
+};
+
+static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
+{
+ struct fimd_context *ctx = (struct fimd_context *)dev_id;
+ u32 val;
+
+ val = readl(ctx->regs + VIDINTCON1);
+
+ if (val & VIDINTCON1_INT_FRAME)
+ /* VSYNC interrupt */
+ writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
+
+ /* check the crtc is detached already from encoder */
+ if (ctx->pipe < 0 || !ctx->drm_dev)
+ goto out;
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+
+ /* set wait vsync event to zero and wake up queue. */
+ if (atomic_read(&ctx->wait_vsync_event)) {
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
static int fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx;
- struct exynos_drm_subdrv *subdrv;
struct resource *res;
int win;
int ret = -EINVAL;
@@ -924,13 +864,25 @@ static int fimd_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
- ret = fimd_get_platform_data(ctx, dev);
- if (ret)
- return ret;
+ ctx->dev = dev;
+ ctx->suspended = true;
- ret = fimd_configure_clocks(ctx, dev);
- if (ret)
- return ret;
+ if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
+ ctx->vidcon1 |= VIDCON1_INV_VDEN;
+ if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
+ ctx->vidcon1 |= VIDCON1_INV_VCLK;
+
+ ctx->bus_clk = devm_clk_get(dev, "fimd");
+ if (IS_ERR(ctx->bus_clk)) {
+ dev_err(dev, "failed to get bus clock\n");
+ return PTR_ERR(ctx->bus_clk);
+ }
+
+ ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
+ if (IS_ERR(ctx->lcd_clk)) {
+ dev_err(dev, "failed to get lcd clock\n");
+ return PTR_ERR(ctx->lcd_clk);
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -944,9 +896,7 @@ static int fimd_probe(struct platform_device *pdev)
return -ENXIO;
}
- ctx->irq = res->start;
-
- ret = devm_request_irq(dev, ctx->irq, fimd_irq_handler,
+ ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx);
if (ret) {
dev_err(dev, "irq request failed.\n");
@@ -957,112 +907,35 @@ static int fimd_probe(struct platform_device *pdev)
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
- subdrv = &ctx->subdrv;
+ platform_set_drvdata(pdev, &fimd_manager);
- subdrv->dev = dev;
- subdrv->manager = &fimd_manager;
- subdrv->probe = fimd_subdrv_probe;
- subdrv->remove = fimd_subdrv_remove;
+ fimd_manager.ctx = ctx;
+ exynos_drm_manager_register(&fimd_manager);
- mutex_init(&ctx->lock);
-
- platform_set_drvdata(pdev, ctx);
+ exynos_dpi_probe(ctx->dev);
pm_runtime_enable(dev);
- pm_runtime_get_sync(dev);
for (win = 0; win < WINDOWS_NR; win++)
fimd_clear_win(ctx, win);
- exynos_drm_subdrv_register(subdrv);
-
return 0;
}
static int fimd_remove(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- struct fimd_context *ctx = platform_get_drvdata(pdev);
-
- exynos_drm_subdrv_unregister(&ctx->subdrv);
-
- if (ctx->suspended)
- goto out;
-
- pm_runtime_set_suspended(dev);
- pm_runtime_put_sync(dev);
-
-out:
- pm_runtime_disable(dev);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int fimd_suspend(struct device *dev)
-{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
- /*
- * 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.
- */
- if (!pm_runtime_suspended(dev))
- return fimd_activate(ctx, false);
+ exynos_dpi_remove(&pdev->dev);
- return 0;
-}
+ exynos_drm_manager_unregister(&fimd_manager);
-static int fimd_resume(struct device *dev)
-{
- struct fimd_context *ctx = get_fimd_context(dev);
+ fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
- /*
- * 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)) {
- int ret;
-
- ret = fimd_activate(ctx, true);
- if (ret < 0)
- return ret;
-
- /*
- * in case of dpms on(standby), fimd_apply function will
- * be called by encoder's dpms callback to update fimd's
- * registers but in case of sleep wakeup, it's not.
- * so fimd_apply function should be called at here.
- */
- fimd_apply(dev);
- }
+ pm_runtime_disable(&pdev->dev);
return 0;
}
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
-static int fimd_runtime_suspend(struct device *dev)
-{
- struct fimd_context *ctx = get_fimd_context(dev);
-
- return fimd_activate(ctx, false);
-}
-
-static int fimd_runtime_resume(struct device *dev)
-{
- struct fimd_context *ctx = get_fimd_context(dev);
-
- return fimd_activate(ctx, true);
-}
-#endif
-
-static const struct dev_pm_ops fimd_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
- SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
-};
struct platform_driver fimd_driver = {
.probe = fimd_probe,
@@ -1070,7 +943,6 @@ struct platform_driver fimd_driver = {
.driver = {
.name = "exynos4-fb",
.owner = THIS_MODULE,
- .pm = &fimd_pm_ops,
.of_match_table = fimd_driver_dt_match,
},
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
deleted file mode 100644
index 8548b974bd59..000000000000
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Authors:
- * Inki Dae <inki.dae@samsung.com>
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <drm/drmP.h>
-
-#include <linux/kernel.h>
-#include <linux/wait.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-
-#include <drm/exynos_drm.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_drm_hdmi.h"
-
-#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
-#define to_subdrv(dev) to_context(dev)
-#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
- struct drm_hdmi_context, subdrv);
-
-/* platform device pointer for common drm hdmi device. */
-static struct platform_device *exynos_drm_hdmi_pdev;
-
-/* Common hdmi subdrv needs to access the hdmi and mixer though context.
-* These should be initialied by the repective drivers */
-static struct exynos_drm_hdmi_context *hdmi_ctx;
-static struct exynos_drm_hdmi_context *mixer_ctx;
-
-/* these callback points shoud be set by specific drivers. */
-static struct exynos_hdmi_ops *hdmi_ops;
-static struct exynos_mixer_ops *mixer_ops;
-
-struct drm_hdmi_context {
- struct exynos_drm_subdrv subdrv;
- struct exynos_drm_hdmi_context *hdmi_ctx;
- struct exynos_drm_hdmi_context *mixer_ctx;
-
- bool enabled[MIXER_WIN_NR];
-};
-
-int exynos_platform_device_hdmi_register(void)
-{
- struct platform_device *pdev;
-
- if (exynos_drm_hdmi_pdev)
- return -EEXIST;
-
- pdev = platform_device_register_simple(
- "exynos-drm-hdmi", -1, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- exynos_drm_hdmi_pdev = pdev;
-
- return 0;
-}
-
-void exynos_platform_device_hdmi_unregister(void)
-{
- if (exynos_drm_hdmi_pdev) {
- platform_device_unregister(exynos_drm_hdmi_pdev);
- exynos_drm_hdmi_pdev = NULL;
- }
-}
-
-void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
-{
- if (ctx)
- hdmi_ctx = ctx;
-}
-
-void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
-{
- if (ctx)
- mixer_ctx = ctx;
-}
-
-void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
-{
- if (ops)
- hdmi_ops = ops;
-}
-
-void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
-{
- if (ops)
- mixer_ops = ops;
-}
-
-static bool drm_hdmi_is_connected(struct device *dev)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- if (hdmi_ops && hdmi_ops->is_connected)
- return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
-
- return false;
-}
-
-static struct edid *drm_hdmi_get_edid(struct device *dev,
- struct drm_connector *connector)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- if (hdmi_ops && hdmi_ops->get_edid)
- return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
-
- return NULL;
-}
-
-static int drm_hdmi_check_mode(struct device *dev,
- struct drm_display_mode *mode)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
- int ret = 0;
-
- /*
- * Both, mixer and hdmi should be able to handle the requested mode.
- * If any of the two fails, return mode as BAD.
- */
-
- if (mixer_ops && mixer_ops->check_mode)
- ret = mixer_ops->check_mode(ctx->mixer_ctx->ctx, mode);
-
- if (ret)
- return ret;
-
- if (hdmi_ops && hdmi_ops->check_mode)
- return hdmi_ops->check_mode(ctx->hdmi_ctx->ctx, mode);
-
- return 0;
-}
-
-static int drm_hdmi_power_on(struct device *dev, int mode)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- if (hdmi_ops && hdmi_ops->power_on)
- return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
-
- return 0;
-}
-
-static struct exynos_drm_display_ops drm_hdmi_display_ops = {
- .type = EXYNOS_DISPLAY_TYPE_HDMI,
- .is_connected = drm_hdmi_is_connected,
- .get_edid = drm_hdmi_get_edid,
- .check_mode = drm_hdmi_check_mode,
- .power_on = drm_hdmi_power_on,
-};
-
-static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct exynos_drm_manager *manager = subdrv->manager;
-
- if (mixer_ops && mixer_ops->enable_vblank)
- return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
- manager->pipe);
-
- return 0;
-}
-
-static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (mixer_ops && mixer_ops->disable_vblank)
- return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
-}
-
-static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (mixer_ops && mixer_ops->wait_for_vblank)
- mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
-}
-
-static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
- struct drm_connector *connector,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct drm_display_mode *m;
- int mode_ok;
-
- drm_mode_set_crtcinfo(adjusted_mode, 0);
-
- mode_ok = drm_hdmi_check_mode(subdrv_dev, adjusted_mode);
-
- /* just return if user desired mode exists. */
- if (mode_ok == 0)
- return;
-
- /*
- * otherwise, find the most suitable mode among modes and change it
- * to adjusted_mode.
- */
- list_for_each_entry(m, &connector->modes, head) {
- mode_ok = drm_hdmi_check_mode(subdrv_dev, m);
-
- if (mode_ok == 0) {
- struct drm_mode_object base;
- struct list_head head;
-
- DRM_INFO("desired mode doesn't exist so\n");
- DRM_INFO("use the most suitable mode among modes.\n");
-
- DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
- m->hdisplay, m->vdisplay, m->vrefresh);
-
- /* preserve display mode header while copying. */
- head = adjusted_mode->head;
- base = adjusted_mode->base;
- memcpy(adjusted_mode, m, sizeof(*m));
- adjusted_mode->head = head;
- adjusted_mode->base = base;
- break;
- }
- }
-}
-
-static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (hdmi_ops && hdmi_ops->mode_set)
- hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
-}
-
-static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
- unsigned int *width, unsigned int *height)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (hdmi_ops && hdmi_ops->get_max_resol)
- hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
-}
-
-static void drm_hdmi_commit(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (hdmi_ops && hdmi_ops->commit)
- hdmi_ops->commit(ctx->hdmi_ctx->ctx);
-}
-
-static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (mixer_ops && mixer_ops->dpms)
- mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
-
- if (hdmi_ops && hdmi_ops->dpms)
- hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
-}
-
-static void drm_hdmi_apply(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- int i;
-
- for (i = 0; i < MIXER_WIN_NR; i++) {
- if (!ctx->enabled[i])
- continue;
- if (mixer_ops && mixer_ops->win_commit)
- mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
- }
-
- if (hdmi_ops && hdmi_ops->commit)
- hdmi_ops->commit(ctx->hdmi_ctx->ctx);
-}
-
-static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
- .dpms = drm_hdmi_dpms,
- .apply = drm_hdmi_apply,
- .enable_vblank = drm_hdmi_enable_vblank,
- .disable_vblank = drm_hdmi_disable_vblank,
- .wait_for_vblank = drm_hdmi_wait_for_vblank,
- .mode_fixup = drm_hdmi_mode_fixup,
- .mode_set = drm_hdmi_mode_set,
- .get_max_resol = drm_hdmi_get_max_resol,
- .commit = drm_hdmi_commit,
-};
-
-static void drm_mixer_mode_set(struct device *subdrv_dev,
- struct exynos_drm_overlay *overlay)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- if (mixer_ops && mixer_ops->win_mode_set)
- mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
-}
-
-static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
-
- if (win < 0 || win >= MIXER_WIN_NR) {
- DRM_ERROR("mixer window[%d] is wrong\n", win);
- return;
- }
-
- if (mixer_ops && mixer_ops->win_commit)
- mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
-
- ctx->enabled[win] = true;
-}
-
-static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
-
- if (win < 0 || win >= MIXER_WIN_NR) {
- DRM_ERROR("mixer window[%d] is wrong\n", win);
- return;
- }
-
- if (mixer_ops && mixer_ops->win_disable)
- mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
-
- ctx->enabled[win] = false;
-}
-
-static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
- .mode_set = drm_mixer_mode_set,
- .commit = drm_mixer_commit,
- .disable = drm_mixer_disable,
-};
-
-static struct exynos_drm_manager hdmi_manager = {
- .pipe = -1,
- .ops = &drm_hdmi_manager_ops,
- .overlay_ops = &drm_hdmi_overlay_ops,
- .display_ops = &drm_hdmi_display_ops,
-};
-
-static int hdmi_subdrv_probe(struct drm_device *drm_dev,
- struct device *dev)
-{
- struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
- struct drm_hdmi_context *ctx;
-
- if (!hdmi_ctx) {
- DRM_ERROR("hdmi context not initialized.\n");
- return -EFAULT;
- }
-
- if (!mixer_ctx) {
- DRM_ERROR("mixer context not initialized.\n");
- return -EFAULT;
- }
-
- ctx = get_ctx_from_subdrv(subdrv);
-
- if (!ctx) {
- DRM_ERROR("no drm hdmi context.\n");
- return -EFAULT;
- }
-
- ctx->hdmi_ctx = hdmi_ctx;
- ctx->mixer_ctx = mixer_ctx;
-
- ctx->hdmi_ctx->drm_dev = drm_dev;
- ctx->mixer_ctx->drm_dev = drm_dev;
-
- if (mixer_ops->iommu_on)
- mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true);
-
- return 0;
-}
-
-static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
-{
- struct drm_hdmi_context *ctx;
- struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
-
- ctx = get_ctx_from_subdrv(subdrv);
-
- if (mixer_ops->iommu_on)
- mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false);
-}
-
-static int exynos_drm_hdmi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct exynos_drm_subdrv *subdrv;
- struct drm_hdmi_context *ctx;
-
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- subdrv = &ctx->subdrv;
-
- subdrv->dev = dev;
- subdrv->manager = &hdmi_manager;
- subdrv->probe = hdmi_subdrv_probe;
- subdrv->remove = hdmi_subdrv_remove;
-
- platform_set_drvdata(pdev, subdrv);
-
- exynos_drm_subdrv_register(subdrv);
-
- return 0;
-}
-
-static int exynos_drm_hdmi_remove(struct platform_device *pdev)
-{
- struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
-
- exynos_drm_subdrv_unregister(&ctx->subdrv);
-
- return 0;
-}
-
-struct platform_driver exynos_drm_common_hdmi_driver = {
- .probe = exynos_drm_hdmi_probe,
- .remove = exynos_drm_hdmi_remove,
- .driver = {
- .name = "exynos-drm-hdmi",
- .owner = THIS_MODULE,
- },
-};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
deleted file mode 100644
index 724cab181976..000000000000
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* exynos_drm_hdmi.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authoer: Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_HDMI_H_
-#define _EXYNOS_DRM_HDMI_H_
-
-#define MIXER_WIN_NR 3
-#define MIXER_DEFAULT_WIN 0
-
-/*
- * exynos hdmi common context structure.
- *
- * @drm_dev: pointer to drm_device.
- * @ctx: pointer to the context of specific device driver.
- * this context should be hdmi_context or mixer_context.
- */
-struct exynos_drm_hdmi_context {
- struct drm_device *drm_dev;
- void *ctx;
-};
-
-struct exynos_hdmi_ops {
- /* display */
- bool (*is_connected)(void *ctx);
- struct edid *(*get_edid)(void *ctx,
- struct drm_connector *connector);
- int (*check_mode)(void *ctx, struct drm_display_mode *mode);
- int (*power_on)(void *ctx, int mode);
-
- /* manager */
- void (*mode_set)(void *ctx, struct drm_display_mode *mode);
- void (*get_max_resol)(void *ctx, unsigned int *width,
- unsigned int *height);
- void (*commit)(void *ctx);
- void (*dpms)(void *ctx, int mode);
-};
-
-struct exynos_mixer_ops {
- /* manager */
- int (*iommu_on)(void *ctx, bool enable);
- int (*enable_vblank)(void *ctx, int pipe);
- void (*disable_vblank)(void *ctx);
- void (*wait_for_vblank)(void *ctx);
- void (*dpms)(void *ctx, int mode);
-
- /* overlay */
- void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
- void (*win_commit)(void *ctx, int zpos);
- void (*win_disable)(void *ctx, int zpos);
-
- /* display */
- int (*check_mode)(void *ctx, struct drm_display_mode *mode);
-};
-
-void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
-void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
-void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
-void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
-#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index fcb0652e77d0..8371cbd7631d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -13,7 +13,7 @@
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
-#include "exynos_drm_encoder.h"
+#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
@@ -87,7 +87,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
if (!buffer) {
- DRM_LOG_KMS("buffer is null\n");
+ DRM_DEBUG_KMS("buffer is null\n");
return -EFAULT;
}
@@ -139,7 +139,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
- exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set);
+ exynos_drm_crtc_plane_mode_set(crtc, overlay);
return 0;
}
@@ -149,8 +149,7 @@ void exynos_plane_commit(struct drm_plane *plane)
struct exynos_plane *exynos_plane = to_exynos_plane(plane);
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
- exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
- exynos_drm_encoder_plane_commit);
+ exynos_drm_crtc_plane_commit(plane->crtc, overlay->zpos);
}
void exynos_plane_dpms(struct drm_plane *plane, int mode)
@@ -162,17 +161,13 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
if (exynos_plane->enabled)
return;
- exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
- exynos_drm_encoder_plane_enable);
-
+ exynos_drm_crtc_plane_enable(plane->crtc, overlay->zpos);
exynos_plane->enabled = true;
} else {
if (!exynos_plane->enabled)
return;
- exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
- exynos_drm_encoder_plane_disable);
-
+ exynos_drm_crtc_plane_disable(plane->crtc, overlay->zpos);
exynos_plane->enabled = false;
}
}
@@ -259,7 +254,7 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
}
struct drm_plane *exynos_plane_init(struct drm_device *dev,
- unsigned int possible_crtcs, bool priv)
+ unsigned long possible_crtcs, bool priv)
{
struct exynos_plane *exynos_plane;
int err;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h
index 88312458580d..84d464c90d3d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h
@@ -17,4 +17,4 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
void exynos_plane_commit(struct drm_plane *plane);
void exynos_plane_dpms(struct drm_plane *plane, int mode);
struct drm_plane *exynos_plane_init(struct drm_device *dev,
- unsigned int possible_crtcs, bool priv);
+ unsigned long possible_crtcs, bool priv);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index ddaaedde173d..7afead9c3f30 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -28,7 +28,9 @@
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
-#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define get_vidi_mgr(dev) platform_get_drvdata(to_platform_device(dev))
+#define ctx_from_connector(c) container_of(c, struct vidi_context, \
+ connector)
struct vidi_win_data {
unsigned int offset_x;
@@ -45,8 +47,10 @@ struct vidi_win_data {
};
struct vidi_context {
- struct exynos_drm_subdrv subdrv;
+ struct drm_device *drm_dev;
struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ struct drm_connector connector;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
@@ -58,6 +62,7 @@ struct vidi_context {
bool direct_vblank;
struct work_struct work;
struct mutex lock;
+ int pipe;
};
static const char fake_edid_info[] = {
@@ -85,126 +90,34 @@ static const char fake_edid_info[] = {
0x00, 0x00, 0x00, 0x06
};
-static bool vidi_display_is_connected(struct device *dev)
+static void vidi_apply(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = get_vidi_context(dev);
-
- /*
- * connection request would come from user side
- * to do hotplug through specific ioctl.
- */
- return ctx->connected ? true : false;
-}
-
-static struct edid *vidi_get_edid(struct device *dev,
- struct drm_connector *connector)
-{
- struct vidi_context *ctx = get_vidi_context(dev);
- struct edid *edid;
-
- /*
- * the edid data comes from user side and it would be set
- * to ctx->raw_edid through specific ioctl.
- */
- if (!ctx->raw_edid) {
- DRM_DEBUG_KMS("raw_edid is null.\n");
- return ERR_PTR(-EFAULT);
- }
-
- edid = drm_edid_duplicate(ctx->raw_edid);
- if (!edid) {
- DRM_DEBUG_KMS("failed to allocate edid\n");
- return ERR_PTR(-ENOMEM);
- }
-
- return edid;
-}
-
-static void *vidi_get_panel(struct device *dev)
-{
- /* TODO. */
-
- return NULL;
-}
-
-static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode)
-{
- /* TODO. */
-
- return 0;
-}
-
-static int vidi_display_power_on(struct device *dev, int mode)
-{
- /* TODO */
-
- return 0;
-}
-
-static struct exynos_drm_display_ops vidi_display_ops = {
- .type = EXYNOS_DISPLAY_TYPE_VIDI,
- .is_connected = vidi_display_is_connected,
- .get_edid = vidi_get_edid,
- .get_panel = vidi_get_panel,
- .check_mode = vidi_check_mode,
- .power_on = vidi_display_power_on,
-};
-
-static void vidi_dpms(struct device *subdrv_dev, int mode)
-{
- struct vidi_context *ctx = get_vidi_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%d\n", mode);
-
- mutex_lock(&ctx->lock);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- /* TODO. */
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- /* TODO. */
- break;
- default:
- DRM_DEBUG_KMS("unspecified mode %d\n", mode);
- break;
- }
-
- mutex_unlock(&ctx->lock);
-}
-
-static void vidi_apply(struct device *subdrv_dev)
-{
- struct vidi_context *ctx = get_vidi_context(subdrv_dev);
- struct exynos_drm_manager *mgr = ctx->subdrv.manager;
+ struct vidi_context *ctx = mgr->ctx;
struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
- struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
struct vidi_win_data *win_data;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
- if (win_data->enabled && (ovl_ops && ovl_ops->commit))
- ovl_ops->commit(subdrv_dev, i);
+ if (win_data->enabled && (mgr_ops && mgr_ops->win_commit))
+ mgr_ops->win_commit(mgr, i);
}
if (mgr_ops && mgr_ops->commit)
- mgr_ops->commit(subdrv_dev);
+ mgr_ops->commit(mgr);
}
-static void vidi_commit(struct device *dev)
+static void vidi_commit(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
}
-static int vidi_enable_vblank(struct device *dev)
+static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *ctx = mgr->ctx;
if (ctx->suspended)
return -EPERM;
@@ -217,16 +130,16 @@ static int vidi_enable_vblank(struct device *dev)
/*
* in case of page flip request, vidi_finish_pageflip function
* will not be called because direct_vblank is true and then
- * that function will be called by overlay_ops->commit callback
+ * that function will be called by manager_ops->win_commit callback
*/
schedule_work(&ctx->work);
return 0;
}
-static void vidi_disable_vblank(struct device *dev)
+static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
@@ -235,24 +148,16 @@ static void vidi_disable_vblank(struct device *dev)
ctx->vblank_on = false;
}
-static struct exynos_drm_manager_ops vidi_manager_ops = {
- .dpms = vidi_dpms,
- .apply = vidi_apply,
- .commit = vidi_commit,
- .enable_vblank = vidi_enable_vblank,
- .disable_vblank = vidi_disable_vblank,
-};
-
-static void vidi_win_mode_set(struct device *dev,
- struct exynos_drm_overlay *overlay)
+static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
+ struct exynos_drm_overlay *overlay)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *ctx = mgr->ctx;
struct vidi_win_data *win_data;
int win;
unsigned long offset;
if (!overlay) {
- dev_err(dev, "overlay is NULL\n");
+ DRM_ERROR("overlay is NULL\n");
return;
}
@@ -296,9 +201,9 @@ static void vidi_win_mode_set(struct device *dev,
overlay->fb_width, overlay->crtc_width);
}
-static void vidi_win_commit(struct device *dev, int zpos)
+static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *ctx = mgr->ctx;
struct vidi_win_data *win_data;
int win = zpos;
@@ -321,9 +226,9 @@ static void vidi_win_commit(struct device *dev, int zpos)
schedule_work(&ctx->work);
}
-static void vidi_win_disable(struct device *dev, int zpos)
+static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *ctx = mgr->ctx;
struct vidi_win_data *win_data;
int win = zpos;
@@ -339,98 +244,132 @@ static void vidi_win_disable(struct device *dev, int zpos)
/* TODO. */
}
-static struct exynos_drm_overlay_ops vidi_overlay_ops = {
- .mode_set = vidi_win_mode_set,
- .commit = vidi_win_commit,
- .disable = vidi_win_disable,
-};
+static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
+{
+ struct vidi_context *ctx = mgr->ctx;
-static struct exynos_drm_manager vidi_manager = {
- .pipe = -1,
- .ops = &vidi_manager_ops,
- .overlay_ops = &vidi_overlay_ops,
- .display_ops = &vidi_display_ops,
-};
+ DRM_DEBUG_KMS("%s\n", __FILE__);
-static void vidi_fake_vblank_handler(struct work_struct *work)
-{
- struct vidi_context *ctx = container_of(work, struct vidi_context,
- work);
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct exynos_drm_manager *manager = subdrv->manager;
+ if (enable != false && enable != true)
+ return -EINVAL;
- if (manager->pipe < 0)
- return;
+ if (enable) {
+ ctx->suspended = false;
- /* refresh rate is about 50Hz. */
- usleep_range(16000, 20000);
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ vidi_enable_vblank(mgr);
+
+ vidi_apply(mgr);
+ } else {
+ ctx->suspended = true;
+ }
+
+ return 0;
+}
+
+static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
+{
+ struct vidi_context *ctx = mgr->ctx;
+
+ DRM_DEBUG_KMS("%d\n", mode);
mutex_lock(&ctx->lock);
- if (ctx->direct_vblank) {
- drm_handle_vblank(subdrv->drm_dev, manager->pipe);
- ctx->direct_vblank = false;
- mutex_unlock(&ctx->lock);
- return;
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ vidi_power_on(mgr, true);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ vidi_power_on(mgr, false);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
}
mutex_unlock(&ctx->lock);
-
- exynos_drm_crtc_finish_pageflip(subdrv->drm_dev, manager->pipe);
}
-static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
+ struct drm_device *drm_dev, int pipe)
{
+ struct vidi_context *ctx = mgr->ctx;
+
+ DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
+
+ ctx->drm_dev = drm_dev;
+ ctx->pipe = pipe;
+
/*
* enable drm irq mode.
- * - with irq_enabled = true, we can use the vblank feature.
+ * - with irq_enabled = 1, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
- drm_dev->irq_enabled = true;
+ drm_dev->irq_enabled = 1;
/*
- * with vblank_disable_allowed = true, vblank interrupt will be disabled
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
- drm_dev->vblank_disable_allowed = true;
+ drm_dev->vblank_disable_allowed = 1;
return 0;
}
-static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
-{
- /* TODO. */
-}
+static struct exynos_drm_manager_ops vidi_manager_ops = {
+ .initialize = vidi_mgr_initialize,
+ .dpms = vidi_dpms,
+ .commit = vidi_commit,
+ .enable_vblank = vidi_enable_vblank,
+ .disable_vblank = vidi_disable_vblank,
+ .win_mode_set = vidi_win_mode_set,
+ .win_commit = vidi_win_commit,
+ .win_disable = vidi_win_disable,
+};
-static int vidi_power_on(struct vidi_context *ctx, bool enable)
+static struct exynos_drm_manager vidi_manager = {
+ .type = EXYNOS_DISPLAY_TYPE_VIDI,
+ .ops = &vidi_manager_ops,
+};
+
+static void vidi_fake_vblank_handler(struct work_struct *work)
{
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct device *dev = subdrv->dev;
+ struct vidi_context *ctx = container_of(work, struct vidi_context,
+ work);
- if (enable) {
- ctx->suspended = false;
+ if (ctx->pipe < 0)
+ return;
- /* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags))
- vidi_enable_vblank(dev);
+ /* refresh rate is about 50Hz. */
+ usleep_range(16000, 20000);
- vidi_apply(dev);
- } else {
- ctx->suspended = true;
+ mutex_lock(&ctx->lock);
+
+ if (ctx->direct_vblank) {
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ ctx->direct_vblank = false;
+ mutex_unlock(&ctx->lock);
+ return;
}
- return 0;
+ mutex_unlock(&ctx->lock);
+
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
}
static int vidi_show_connection(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc;
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
+ struct vidi_context *ctx = mgr->ctx;
mutex_lock(&ctx->lock);
@@ -445,7 +384,8 @@ static int vidi_store_connection(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
+ struct vidi_context *ctx = mgr->ctx;
int ret;
ret = kstrtoint(buf, 0, &ctx->connected);
@@ -467,7 +407,7 @@ static int vidi_store_connection(struct device *dev,
DRM_DEBUG_KMS("requested connection.\n");
- drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+ drm_helper_hpd_irq_event(ctx->drm_dev);
return len;
}
@@ -480,8 +420,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
{
struct vidi_context *ctx = NULL;
struct drm_encoder *encoder;
- struct exynos_drm_manager *manager;
- struct exynos_drm_display_ops *display_ops;
+ struct exynos_drm_display *display;
struct drm_exynos_vidi_connection *vidi = data;
if (!vidi) {
@@ -496,11 +435,10 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
head) {
- manager = exynos_drm_get_manager(encoder);
- display_ops = manager->display_ops;
+ display = exynos_drm_get_display(encoder);
- if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
- ctx = get_vidi_context(manager->dev);
+ if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
+ ctx = display->ctx;
break;
}
}
@@ -539,16 +477,119 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
}
ctx->connected = vidi->connection;
- drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+ drm_helper_hpd_irq_event(ctx->drm_dev);
+
+ return 0;
+}
+
+static enum drm_connector_status vidi_detect(struct drm_connector *connector,
+ bool force)
+{
+ struct vidi_context *ctx = ctx_from_connector(connector);
+
+ /*
+ * connection request would come from user side
+ * to do hotplug through specific ioctl.
+ */
+ return ctx->connected ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static void vidi_connector_destroy(struct drm_connector *connector)
+{
+}
+
+static struct drm_connector_funcs vidi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = vidi_detect,
+ .destroy = vidi_connector_destroy,
+};
+
+static int vidi_get_modes(struct drm_connector *connector)
+{
+ struct vidi_context *ctx = ctx_from_connector(connector);
+ struct edid *edid;
+ int edid_len;
+
+ /*
+ * the edid data comes from user side and it would be set
+ * to ctx->raw_edid through specific ioctl.
+ */
+ if (!ctx->raw_edid) {
+ DRM_DEBUG_KMS("raw_edid is null.\n");
+ return -EFAULT;
+ }
+
+ edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH;
+ edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);
+ if (!edid) {
+ DRM_DEBUG_KMS("failed to allocate edid\n");
+ return -ENOMEM;
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+
+ return drm_add_edid_modes(connector, edid);
+}
+
+static int vidi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
+{
+ struct vidi_context *ctx = ctx_from_connector(connector);
+
+ return ctx->encoder;
+}
+
+static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
+ .get_modes = vidi_get_modes,
+ .mode_valid = vidi_mode_valid,
+ .best_encoder = vidi_best_encoder,
+};
+
+static int vidi_create_connector(struct exynos_drm_display *display,
+ struct drm_encoder *encoder)
+{
+ struct vidi_context *ctx = display->ctx;
+ struct drm_connector *connector = &ctx->connector;
+ int ret;
+
+ ctx->encoder = encoder;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(ctx->drm_dev, connector,
+ &vidi_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
+
+static struct exynos_drm_display_ops vidi_display_ops = {
+ .create_connector = vidi_create_connector,
+};
+
+static struct exynos_drm_display vidi_display = {
+ .type = EXYNOS_DISPLAY_TYPE_VIDI,
+ .ops = &vidi_display_ops,
+};
+
static int vidi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct vidi_context *ctx;
- struct exynos_drm_subdrv *subdrv;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
@@ -559,21 +600,19 @@ static int vidi_probe(struct platform_device *pdev)
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
- subdrv = &ctx->subdrv;
- subdrv->dev = dev;
- subdrv->manager = &vidi_manager;
- subdrv->probe = vidi_subdrv_probe;
- subdrv->remove = vidi_subdrv_remove;
+ vidi_manager.ctx = ctx;
+ vidi_display.ctx = ctx;
mutex_init(&ctx->lock);
- platform_set_drvdata(pdev, ctx);
+ platform_set_drvdata(pdev, &vidi_manager);
ret = device_create_file(dev, &dev_attr_connection);
if (ret < 0)
DRM_INFO("failed to create connection sysfs.\n");
- exynos_drm_subdrv_register(subdrv);
+ exynos_drm_manager_register(&vidi_manager);
+ exynos_drm_display_register(&vidi_display);
return 0;
}
@@ -582,7 +621,8 @@ static int vidi_remove(struct platform_device *pdev)
{
struct vidi_context *ctx = platform_get_drvdata(pdev);
- exynos_drm_subdrv_unregister(&ctx->subdrv);
+ exynos_drm_display_unregister(&vidi_display);
+ exynos_drm_manager_unregister(&vidi_manager);
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid);
@@ -592,32 +632,11 @@ static int vidi_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int vidi_suspend(struct device *dev)
-{
- struct vidi_context *ctx = get_vidi_context(dev);
-
- return vidi_power_on(ctx, false);
-}
-
-static int vidi_resume(struct device *dev)
-{
- struct vidi_context *ctx = get_vidi_context(dev);
-
- return vidi_power_on(ctx, true);
-}
-#endif
-
-static const struct dev_pm_ops vidi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
-};
-
struct platform_driver vidi_driver = {
.probe = vidi_probe,
.remove = vidi_remove,
.driver = {
.name = "exynos-drm-vidi",
.owner = THIS_MODULE,
- .pm = &vidi_pm_ops,
},
};
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index c021ddc1ffb4..9a6d652a3ef2 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -33,38 +33,42 @@
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/hdmi.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
-#include "exynos_drm_hdmi.h"
-
-#include "exynos_hdmi.h"
+#include "exynos_mixer.h"
#include <linux/gpio.h>
#include <media/s5p_hdmi.h>
-#define MAX_WIDTH 1920
-#define MAX_HEIGHT 1080
-#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
+#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
/* AVI header and aspect ratio */
#define HDMI_AVI_VERSION 0x02
#define HDMI_AVI_LENGTH 0x0D
-#define AVI_PIC_ASPECT_RATIO_16_9 (2 << 4)
-#define AVI_SAME_AS_PIC_ASPECT_RATIO 8
/* AUI header info */
#define HDMI_AUI_VERSION 0x01
#define HDMI_AUI_LENGTH 0x0A
+#define AVI_SAME_AS_PIC_ASPECT_RATIO 0x8
+#define AVI_4_3_CENTER_RATIO 0x9
+#define AVI_16_9_CENTER_RATIO 0xa
enum hdmi_type {
HDMI_TYPE13,
HDMI_TYPE14,
};
+struct hdmi_driver_data {
+ unsigned int type;
+ unsigned int is_apb_phy:1;
+};
+
struct hdmi_resources {
struct clk *hdmi;
struct clk *sclk_hdmi;
@@ -162,6 +166,7 @@ struct hdmi_v14_conf {
struct hdmi_conf_regs {
int pixel_clock;
int cea_video_id;
+ enum hdmi_picture_aspect aspect_ratio;
union {
struct hdmi_v13_conf v13_conf;
struct hdmi_v14_conf v14_conf;
@@ -171,16 +176,17 @@ struct hdmi_conf_regs {
struct hdmi_context {
struct device *dev;
struct drm_device *drm_dev;
+ struct drm_connector connector;
+ struct drm_encoder *encoder;
bool hpd;
bool powered;
bool dvi_mode;
struct mutex hdmi_mutex;
void __iomem *regs;
- void *parent_ctx;
int irq;
- struct i2c_client *ddc_port;
+ struct i2c_adapter *ddc_adpt;
struct i2c_client *hdmiphy_port;
/* current hdmiphy conf regs */
@@ -198,6 +204,14 @@ struct hdmiphy_config {
u8 conf[32];
};
+struct hdmi_driver_data exynos4212_hdmi_driver_data = {
+ .type = HDMI_TYPE14,
+};
+
+struct hdmi_driver_data exynos5_hdmi_driver_data = {
+ .type = HDMI_TYPE14,
+};
+
/* list of phy config settings */
static const struct hdmiphy_config hdmiphy_v13_configs[] = {
{
@@ -303,6 +317,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
},
},
{
+ .pixel_clock = 71000000,
+ .conf = {
+ 0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08,
+ 0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 73250000,
+ .conf = {
+ 0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08,
+ 0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
.pixel_clock = 74176000,
.conf = {
0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
@@ -330,6 +362,15 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
},
},
{
+ .pixel_clock = 88750000,
+ .conf = {
+ 0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08,
+ 0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
.pixel_clock = 106500000,
.conf = {
0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
@@ -348,6 +389,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
},
},
{
+ .pixel_clock = 115500000,
+ .conf = {
+ 0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04,
+ 0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 119000000,
+ .conf = {
+ 0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08,
+ 0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
.pixel_clock = 146250000,
.conf = {
0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
@@ -668,7 +727,6 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
{
u32 hdr_sum;
u8 chksum;
- u32 aspect_ratio;
u32 mod;
u32 vic;
@@ -697,10 +755,28 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
AVI_ACTIVE_FORMAT_VALID |
AVI_UNDERSCANNED_DISPLAY_VALID);
- aspect_ratio = AVI_PIC_ASPECT_RATIO_16_9;
-
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), aspect_ratio |
- AVI_SAME_AS_PIC_ASPECT_RATIO);
+ /*
+ * Set the aspect ratio as per the mode, mentioned in
+ * Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard
+ */
+ switch (hdata->mode_conf.aspect_ratio) {
+ case HDMI_PICTURE_ASPECT_4_3:
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
+ hdata->mode_conf.aspect_ratio |
+ AVI_4_3_CENTER_RATIO);
+ break;
+ case HDMI_PICTURE_ASPECT_16_9:
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
+ hdata->mode_conf.aspect_ratio |
+ AVI_16_9_CENTER_RATIO);
+ break;
+ case HDMI_PICTURE_ASPECT_NONE:
+ default:
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
+ hdata->mode_conf.aspect_ratio |
+ AVI_SAME_AS_PIC_ASPECT_RATIO);
+ break;
+ }
vic = hdata->mode_conf.cea_video_id;
hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic);
@@ -728,31 +804,46 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
}
}
-static bool hdmi_is_connected(void *ctx)
+static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
+ bool force)
{
- struct hdmi_context *hdata = ctx;
+ struct hdmi_context *hdata = ctx_from_connector(connector);
+
+ return hdata->hpd ? connector_status_connected :
+ connector_status_disconnected;
+}
- return hdata->hpd;
+static void hdmi_connector_destroy(struct drm_connector *connector)
+{
}
-static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
+static struct drm_connector_funcs hdmi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = hdmi_detect,
+ .destroy = hdmi_connector_destroy,
+};
+
+static int hdmi_get_modes(struct drm_connector *connector)
{
- struct edid *raw_edid;
- struct hdmi_context *hdata = ctx;
+ struct hdmi_context *hdata = ctx_from_connector(connector);
+ struct edid *edid;
- if (!hdata->ddc_port)
- return ERR_PTR(-ENODEV);
+ if (!hdata->ddc_adpt)
+ return -ENODEV;
- raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
- if (!raw_edid)
- return ERR_PTR(-ENODEV);
+ edid = drm_get_edid(connector, hdata->ddc_adpt);
+ if (!edid)
+ return -ENODEV;
- hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid);
+ hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
(hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
- raw_edid->width_cm, raw_edid->height_cm);
+ edid->width_cm, edid->height_cm);
- return raw_edid;
+ drm_mode_connector_update_edid_property(connector, edid);
+
+ return drm_add_edid_modes(connector, edid);
}
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
@@ -777,9 +868,10 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
return -EINVAL;
}
-static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
+static int hdmi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
{
- struct hdmi_context *hdata = ctx;
+ struct hdmi_context *hdata = ctx_from_connector(connector);
int ret;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
@@ -787,12 +879,103 @@ static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
false, mode->clock * 1000);
+ ret = mixer_check_mode(mode);
+ if (ret)
+ return MODE_BAD;
+
ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
if (ret < 0)
+ return MODE_BAD;
+
+ return MODE_OK;
+}
+
+static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
+{
+ struct hdmi_context *hdata = ctx_from_connector(connector);
+
+ return hdata->encoder;
+}
+
+static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
+ .get_modes = hdmi_get_modes,
+ .mode_valid = hdmi_mode_valid,
+ .best_encoder = hdmi_best_encoder,
+};
+
+static int hdmi_create_connector(struct exynos_drm_display *display,
+ struct drm_encoder *encoder)
+{
+ struct hdmi_context *hdata = display->ctx;
+ struct drm_connector *connector = &hdata->connector;
+ int ret;
+
+ hdata->encoder = encoder;
+ connector->interlace_allowed = true;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(hdata->drm_dev, connector,
+ &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
return ret;
+ }
+
+ drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static int hdmi_initialize(struct exynos_drm_display *display,
+ struct drm_device *drm_dev)
+{
+ struct hdmi_context *hdata = display->ctx;
+
+ hdata->drm_dev = drm_dev;
+
return 0;
}
+static void hdmi_mode_fixup(struct exynos_drm_display *display,
+ struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_display_mode *m;
+ int mode_ok;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ mode_ok = hdmi_mode_valid(connector, adjusted_mode);
+
+ /* just return if user desired mode exists. */
+ if (mode_ok == MODE_OK)
+ return;
+
+ /*
+ * otherwise, find the most suitable mode among modes and change it
+ * to adjusted_mode.
+ */
+ list_for_each_entry(m, &connector->modes, head) {
+ mode_ok = hdmi_mode_valid(connector, m);
+
+ if (mode_ok == MODE_OK) {
+ DRM_INFO("desired mode doesn't exist so\n");
+ DRM_INFO("use the most suitable mode among modes.\n");
+
+ DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
+ m->hdisplay, m->vdisplay, m->vrefresh);
+
+ drm_mode_copy(adjusted_mode, m);
+ break;
+ }
+ }
+}
+
static void hdmi_set_acr(u32 freq, u8 *acr)
{
u32 n, cts;
@@ -1421,6 +1604,7 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
hdata->mode_conf.cea_video_id =
drm_match_cea_mode((struct drm_display_mode *)m);
hdata->mode_conf.pixel_clock = m->clock * 1000;
+ hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;
hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
@@ -1517,6 +1701,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
hdata->mode_conf.cea_video_id =
drm_match_cea_mode((struct drm_display_mode *)m);
hdata->mode_conf.pixel_clock = m->clock * 1000;
+ hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;
hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
hdmi_set_reg(core->v_line, 2, m->vtotal);
@@ -1618,9 +1803,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
hdmi_set_reg(tg->tg_3d, 1, 0x0);
}
-static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
+static void hdmi_mode_set(struct exynos_drm_display *display,
+ struct drm_display_mode *mode)
{
- struct hdmi_context *hdata = ctx;
+ struct hdmi_context *hdata = display->ctx;
struct drm_display_mode *m = mode;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
@@ -1634,16 +1820,9 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
hdmi_v14_mode_set(hdata, mode);
}
-static void hdmi_get_max_resol(void *ctx, unsigned int *width,
- unsigned int *height)
-{
- *width = MAX_WIDTH;
- *height = MAX_HEIGHT;
-}
-
-static void hdmi_commit(void *ctx)
+static void hdmi_commit(struct exynos_drm_display *display)
{
- struct hdmi_context *hdata = ctx;
+ struct hdmi_context *hdata = display->ctx;
mutex_lock(&hdata->hdmi_mutex);
if (!hdata->powered) {
@@ -1655,8 +1834,9 @@ static void hdmi_commit(void *ctx)
hdmi_conf_apply(hdata);
}
-static void hdmi_poweron(struct hdmi_context *hdata)
+static void hdmi_poweron(struct exynos_drm_display *display)
{
+ struct hdmi_context *hdata = display->ctx;
struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex);
@@ -1669,6 +1849,8 @@ static void hdmi_poweron(struct hdmi_context *hdata)
mutex_unlock(&hdata->hdmi_mutex);
+ pm_runtime_get_sync(hdata->dev);
+
if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
DRM_DEBUG_KMS("failed to enable regulator bulk\n");
@@ -1677,10 +1859,12 @@ static void hdmi_poweron(struct hdmi_context *hdata)
clk_prepare_enable(res->sclk_hdmi);
hdmiphy_poweron(hdata);
+ hdmi_commit(display);
}
-static void hdmi_poweroff(struct hdmi_context *hdata)
+static void hdmi_poweroff(struct exynos_drm_display *display)
{
+ struct hdmi_context *hdata = display->ctx;
struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex);
@@ -1700,30 +1884,27 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
clk_disable_unprepare(res->hdmiphy);
regulator_bulk_disable(res->regul_count, res->regul_bulk);
- mutex_lock(&hdata->hdmi_mutex);
+ pm_runtime_put_sync(hdata->dev);
+ mutex_lock(&hdata->hdmi_mutex);
hdata->powered = false;
out:
mutex_unlock(&hdata->hdmi_mutex);
}
-static void hdmi_dpms(void *ctx, int mode)
+static void hdmi_dpms(struct exynos_drm_display *display, int mode)
{
- struct hdmi_context *hdata = ctx;
-
DRM_DEBUG_KMS("mode %d\n", mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
- if (pm_runtime_suspended(hdata->dev))
- pm_runtime_get_sync(hdata->dev);
+ hdmi_poweron(display);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- if (!pm_runtime_suspended(hdata->dev))
- pm_runtime_put_sync(hdata->dev);
+ hdmi_poweroff(display);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
@@ -1731,30 +1912,30 @@ static void hdmi_dpms(void *ctx, int mode)
}
}
-static struct exynos_hdmi_ops hdmi_ops = {
- /* display */
- .is_connected = hdmi_is_connected,
- .get_edid = hdmi_get_edid,
- .check_mode = hdmi_check_mode,
-
- /* manager */
+static struct exynos_drm_display_ops hdmi_display_ops = {
+ .initialize = hdmi_initialize,
+ .create_connector = hdmi_create_connector,
+ .mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
- .get_max_resol = hdmi_get_max_resol,
- .commit = hdmi_commit,
.dpms = hdmi_dpms,
+ .commit = hdmi_commit,
+};
+
+static struct exynos_drm_display hdmi_display = {
+ .type = EXYNOS_DISPLAY_TYPE_HDMI,
+ .ops = &hdmi_display_ops,
};
static irqreturn_t hdmi_irq_thread(int irq, void *arg)
{
- struct exynos_drm_hdmi_context *ctx = arg;
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = arg;
mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
mutex_unlock(&hdata->hdmi_mutex);
- if (ctx->drm_dev)
- drm_helper_hpd_irq_event(ctx->drm_dev);
+ if (hdata->drm_dev)
+ drm_helper_hpd_irq_event(hdata->drm_dev);
return IRQ_HANDLED;
}
@@ -1830,20 +2011,6 @@ fail:
return -ENODEV;
}
-static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
-
-void hdmi_attach_ddc_client(struct i2c_client *ddc)
-{
- if (ddc)
- hdmi_ddc = ddc;
-}
-
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
-{
- if (hdmiphy)
- hdmi_hdmiphy = hdmiphy;
-}
-
static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
(struct device *dev)
{
@@ -1871,10 +2038,10 @@ err_data:
static struct of_device_id hdmi_match_types[] = {
{
.compatible = "samsung,exynos5-hdmi",
- .data = (void *)HDMI_TYPE14,
+ .data = &exynos5_hdmi_driver_data,
}, {
.compatible = "samsung,exynos4212-hdmi",
- .data = (void *)HDMI_TYPE14,
+ .data = &exynos4212_hdmi_driver_data,
}, {
/* end node */
}
@@ -1883,11 +2050,12 @@ static struct of_device_id hdmi_match_types[] = {
static int hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct hdmi_context *hdata;
struct s5p_hdmi_platform_data *pdata;
struct resource *res;
const struct of_device_id *match;
+ struct device_node *ddc_node, *phy_node;
+ struct hdmi_driver_data *drv_data;
int ret;
if (!dev->of_node)
@@ -1897,25 +2065,20 @@ static int hdmi_probe(struct platform_device *pdev)
if (!pdata)
return -EINVAL;
- drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx), GFP_KERNEL);
- if (!drm_hdmi_ctx)
- return -ENOMEM;
-
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata)
return -ENOMEM;
mutex_init(&hdata->hdmi_mutex);
- drm_hdmi_ctx->ctx = (void *)hdata;
- hdata->parent_ctx = (void *)drm_hdmi_ctx;
-
- platform_set_drvdata(pdev, drm_hdmi_ctx);
+ platform_set_drvdata(pdev, &hdmi_display);
match = of_match_node(hdmi_match_types, dev->of_node);
if (!match)
return -ENODEV;
- hdata->type = (enum hdmi_type)match->data;
+
+ drv_data = (struct hdmi_driver_data *)match->data;
+ hdata->type = drv_data->type;
hdata->hpd_gpio = pdata->hpd_gpio;
hdata->dev = dev;
@@ -1938,21 +2101,34 @@ static int hdmi_probe(struct platform_device *pdev)
}
/* DDC i2c driver */
- if (i2c_add_driver(&ddc_driver)) {
- DRM_ERROR("failed to register ddc i2c driver\n");
- return -ENOENT;
+ ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
+ if (!ddc_node) {
+ DRM_ERROR("Failed to find ddc node in device tree\n");
+ return -ENODEV;
+ }
+ hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
+ if (!hdata->ddc_adpt) {
+ DRM_ERROR("Failed to get ddc i2c adapter by node\n");
+ return -ENODEV;
}
- hdata->ddc_port = hdmi_ddc;
+ /* Not support APB PHY yet. */
+ if (drv_data->is_apb_phy)
+ return -EPERM;
/* hdmiphy i2c driver */
- if (i2c_add_driver(&hdmiphy_driver)) {
- DRM_ERROR("failed to register hdmiphy i2c driver\n");
- ret = -ENOENT;
+ phy_node = of_parse_phandle(dev->of_node, "phy", 0);
+ if (!phy_node) {
+ DRM_ERROR("Failed to find hdmiphy node in device tree\n");
+ ret = -ENODEV;
+ goto err_ddc;
+ }
+ hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
+ if (!hdata->hdmiphy_port) {
+ DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
+ ret = -ENODEV;
goto err_ddc;
}
-
- hdata->hdmiphy_port = hdmi_hdmiphy;
hdata->irq = gpio_to_irq(hdata->hpd_gpio);
if (hdata->irq < 0) {
@@ -1966,119 +2142,45 @@ static int hdmi_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
hdmi_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "hdmi", drm_hdmi_ctx);
+ "hdmi", hdata);
if (ret) {
DRM_ERROR("failed to register hdmi interrupt\n");
goto err_hdmiphy;
}
- /* Attach HDMI Driver to common hdmi. */
- exynos_hdmi_drv_attach(drm_hdmi_ctx);
-
- /* register specific callbacks to common hdmi. */
- exynos_hdmi_ops_register(&hdmi_ops);
-
pm_runtime_enable(dev);
+ hdmi_display.ctx = hdata;
+ exynos_drm_display_register(&hdmi_display);
+
return 0;
err_hdmiphy:
- i2c_del_driver(&hdmiphy_driver);
+ put_device(&hdata->hdmiphy_port->dev);
err_ddc:
- i2c_del_driver(&ddc_driver);
+ put_device(&hdata->ddc_adpt->dev);
return ret;
}
static int hdmi_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct exynos_drm_display *display = get_hdmi_display(dev);
+ struct hdmi_context *hdata = display->ctx;
- pm_runtime_disable(dev);
-
- /* hdmiphy i2c driver */
- i2c_del_driver(&hdmiphy_driver);
- /* DDC i2c driver */
- i2c_del_driver(&ddc_driver);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int hdmi_suspend(struct device *dev)
-{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
-
- disable_irq(hdata->irq);
-
- hdata->hpd = false;
- if (ctx->drm_dev)
- drm_helper_hpd_irq_event(ctx->drm_dev);
-
- if (pm_runtime_suspended(dev)) {
- DRM_DEBUG_KMS("Already suspended\n");
- return 0;
- }
-
- hdmi_poweroff(hdata);
+ put_device(&hdata->hdmiphy_port->dev);
+ put_device(&hdata->ddc_adpt->dev);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
-static int hdmi_resume(struct device *dev)
-{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
-
- hdata->hpd = gpio_get_value(hdata->hpd_gpio);
-
- enable_irq(hdata->irq);
-
- if (!pm_runtime_suspended(dev)) {
- DRM_DEBUG_KMS("Already resumed\n");
- return 0;
- }
-
- hdmi_poweron(hdata);
-
- return 0;
-}
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
-static int hdmi_runtime_suspend(struct device *dev)
-{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
-
- hdmi_poweroff(hdata);
-
- return 0;
-}
-
-static int hdmi_runtime_resume(struct device *dev)
-{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
-
- hdmi_poweron(hdata);
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops hdmi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(hdmi_suspend, hdmi_resume)
- SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
-};
-
struct platform_driver hdmi_driver = {
.probe = hdmi_probe,
.remove = hdmi_remove,
.driver = {
.name = "exynos-hdmi",
.owner = THIS_MODULE,
- .pm = &hdmi_pm_ops,
.of_match_table = hdmi_match_types,
},
};
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 2dfa48c76f54..ce288818d2c0 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -36,10 +36,13 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
-#include "exynos_drm_hdmi.h"
#include "exynos_drm_iommu.h"
+#include "exynos_mixer.h"
-#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
+
+#define MIXER_WIN_NR 3
+#define MIXER_DEFAULT_WIN 0
struct hdmi_win_data {
dma_addr_t dma_addr;
@@ -82,6 +85,7 @@ enum mixer_version_id {
};
struct mixer_context {
+ struct platform_device *pdev;
struct device *dev;
struct drm_device *drm_dev;
int pipe;
@@ -94,7 +98,6 @@ struct mixer_context {
struct mixer_resources mixer_res;
struct hdmi_win_data win_data[MIXER_WIN_NR];
enum mixer_version_id mxr_ver;
- void *parent_ctx;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
};
@@ -685,31 +688,196 @@ static void mixer_win_reset(struct mixer_context *ctx)
spin_unlock_irqrestore(&res->reg_slock, flags);
}
-static int mixer_iommu_on(void *ctx, bool enable)
+static irqreturn_t mixer_irq_handler(int irq, void *arg)
+{
+ struct mixer_context *ctx = arg;
+ struct mixer_resources *res = &ctx->mixer_res;
+ u32 val, base, shadow;
+
+ spin_lock(&res->reg_slock);
+
+ /* read interrupt status for handling and clearing flags for VSYNC */
+ val = mixer_reg_read(res, MXR_INT_STATUS);
+
+ /* handling VSYNC */
+ if (val & MXR_INT_STATUS_VSYNC) {
+ /* interlace scan need to check shadow register */
+ if (ctx->interlace) {
+ base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
+ shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
+ if (base != shadow)
+ goto out;
+
+ base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
+ shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
+ if (base != shadow)
+ goto out;
+ }
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+
+ /* set wait vsync event to zero and wake up queue. */
+ if (atomic_read(&ctx->wait_vsync_event)) {
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+ }
+ }
+
+out:
+ /* clear interrupts */
+ if (~val & MXR_INT_EN_VSYNC) {
+ /* vsync interrupt use different bit for read and clear */
+ val &= ~MXR_INT_EN_VSYNC;
+ val |= MXR_INT_CLEAR_VSYNC;
+ }
+ mixer_reg_write(res, MXR_INT_STATUS, val);
+
+ spin_unlock(&res->reg_slock);
+
+ return IRQ_HANDLED;
+}
+
+static int mixer_resources_init(struct mixer_context *mixer_ctx)
{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
- struct mixer_context *mdata = ctx;
- struct drm_device *drm_dev;
+ struct device *dev = &mixer_ctx->pdev->dev;
+ struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
+ struct resource *res;
+ int ret;
- drm_hdmi_ctx = mdata->parent_ctx;
- drm_dev = drm_hdmi_ctx->drm_dev;
+ spin_lock_init(&mixer_res->reg_slock);
- if (is_drm_iommu_supported(drm_dev)) {
- if (enable)
- return drm_iommu_attach_device(drm_dev, mdata->dev);
+ mixer_res->mixer = devm_clk_get(dev, "mixer");
+ if (IS_ERR(mixer_res->mixer)) {
+ dev_err(dev, "failed to get clock 'mixer'\n");
+ return -ENODEV;
+ }
- drm_iommu_detach_device(drm_dev, mdata->dev);
+ mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
+ if (IS_ERR(mixer_res->sclk_hdmi)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
+ return -ENODEV;
+ }
+ res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ return -ENXIO;
}
+
+ mixer_res->mixer_regs = devm_ioremap(dev, res->start,
+ resource_size(res));
+ if (mixer_res->mixer_regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ return -ENXIO;
+ }
+
+ res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ return -ENXIO;
+ }
+
+ ret = devm_request_irq(dev, res->start, mixer_irq_handler,
+ 0, "drm_mixer", mixer_ctx);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ return ret;
+ }
+ mixer_res->irq = res->start;
+
return 0;
}
-static int mixer_enable_vblank(void *ctx, int pipe)
+static int vp_resources_init(struct mixer_context *mixer_ctx)
{
- struct mixer_context *mixer_ctx = ctx;
- struct mixer_resources *res = &mixer_ctx->mixer_res;
+ struct device *dev = &mixer_ctx->pdev->dev;
+ struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
+ struct resource *res;
+
+ mixer_res->vp = devm_clk_get(dev, "vp");
+ if (IS_ERR(mixer_res->vp)) {
+ dev_err(dev, "failed to get clock 'vp'\n");
+ return -ENODEV;
+ }
+ mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
+ if (IS_ERR(mixer_res->sclk_mixer)) {
+ dev_err(dev, "failed to get clock 'sclk_mixer'\n");
+ return -ENODEV;
+ }
+ mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
+ if (IS_ERR(mixer_res->sclk_dac)) {
+ dev_err(dev, "failed to get clock 'sclk_dac'\n");
+ return -ENODEV;
+ }
+
+ if (mixer_res->sclk_hdmi)
+ clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
+
+ res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ return -ENXIO;
+ }
+ mixer_res->vp_regs = devm_ioremap(dev, res->start,
+ resource_size(res));
+ if (mixer_res->vp_regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int mixer_initialize(struct exynos_drm_manager *mgr,
+ struct drm_device *drm_dev, int pipe)
+{
+ int ret;
+ struct mixer_context *mixer_ctx = mgr->ctx;
+
+ mixer_ctx->drm_dev = drm_dev;
mixer_ctx->pipe = pipe;
+ /* acquire resources: regs, irqs, clocks */
+ ret = mixer_resources_init(mixer_ctx);
+ if (ret) {
+ DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
+ return ret;
+ }
+
+ if (mixer_ctx->vp_enabled) {
+ /* acquire vp resources: regs, irqs, clocks */
+ ret = vp_resources_init(mixer_ctx);
+ if (ret) {
+ DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
+ return 0;
+
+ return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
+}
+
+static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
+{
+ struct mixer_context *mixer_ctx = mgr->ctx;
+
+ if (is_drm_iommu_supported(mixer_ctx->drm_dev))
+ drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
+}
+
+static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
+{
+ struct mixer_context *mixer_ctx = mgr->ctx;
+ struct mixer_resources *res = &mixer_ctx->mixer_res;
+
+ if (!mixer_ctx->powered) {
+ mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
+ return 0;
+ }
+
/* enable vsync interrupt */
mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
MXR_INT_EN_VSYNC);
@@ -717,19 +885,19 @@ static int mixer_enable_vblank(void *ctx, int pipe)
return 0;
}
-static void mixer_disable_vblank(void *ctx)
+static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
{
- struct mixer_context *mixer_ctx = ctx;
+ struct mixer_context *mixer_ctx = mgr->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
/* disable vsync interrupt */
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
}
-static void mixer_win_mode_set(void *ctx,
- struct exynos_drm_overlay *overlay)
+static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
+ struct exynos_drm_overlay *overlay)
{
- struct mixer_context *mixer_ctx = ctx;
+ struct mixer_context *mixer_ctx = mgr->ctx;
struct hdmi_win_data *win_data;
int win;
@@ -778,9 +946,10 @@ static void mixer_win_mode_set(void *ctx,
win_data->scan_flags = overlay->scan_flag;
}
-static void mixer_win_commit(void *ctx, int win)
+static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
{
- struct mixer_context *mixer_ctx = ctx;
+ struct mixer_context *mixer_ctx = mgr->ctx;
+ int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("win: %d\n", win);
@@ -799,10 +968,11 @@ static void mixer_win_commit(void *ctx, int win)
mixer_ctx->win_data[win].enabled = true;
}
-static void mixer_win_disable(void *ctx, int win)
+static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
{
- struct mixer_context *mixer_ctx = ctx;
+ struct mixer_context *mixer_ctx = mgr->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
+ int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
unsigned long flags;
DRM_DEBUG_KMS("win: %d\n", win);
@@ -826,32 +996,9 @@ static void mixer_win_disable(void *ctx, int win)
mixer_ctx->win_data[win].enabled = false;
}
-static int mixer_check_mode(void *ctx, struct drm_display_mode *mode)
+static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
{
- struct mixer_context *mixer_ctx = ctx;
- u32 w, h;
-
- w = mode->hdisplay;
- h = mode->vdisplay;
-
- DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
- mode->hdisplay, mode->vdisplay, mode->vrefresh,
- (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
-
- if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16 ||
- mixer_ctx->mxr_ver == MXR_VER_128_0_0_184)
- return 0;
-
- if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
- (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
- (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
- return 0;
-
- return -EINVAL;
-}
-static void mixer_wait_for_vblank(void *ctx)
-{
- struct mixer_context *mixer_ctx = ctx;
+ struct mixer_context *mixer_ctx = mgr->ctx;
mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) {
@@ -872,21 +1019,23 @@ static void mixer_wait_for_vblank(void *ctx)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
-static void mixer_window_suspend(struct mixer_context *ctx)
+static void mixer_window_suspend(struct exynos_drm_manager *mgr)
{
+ struct mixer_context *ctx = mgr->ctx;
struct hdmi_win_data *win_data;
int i;
for (i = 0; i < MIXER_WIN_NR; i++) {
win_data = &ctx->win_data[i];
win_data->resume = win_data->enabled;
- mixer_win_disable(ctx, i);
+ mixer_win_disable(mgr, i);
}
- mixer_wait_for_vblank(ctx);
+ mixer_wait_for_vblank(mgr);
}
-static void mixer_window_resume(struct mixer_context *ctx)
+static void mixer_window_resume(struct exynos_drm_manager *mgr)
{
+ struct mixer_context *ctx = mgr->ctx;
struct hdmi_win_data *win_data;
int i;
@@ -894,11 +1043,14 @@ static void mixer_window_resume(struct mixer_context *ctx)
win_data = &ctx->win_data[i];
win_data->enabled = win_data->resume;
win_data->resume = false;
+ if (win_data->enabled)
+ mixer_win_commit(mgr, i);
}
}
-static void mixer_poweron(struct mixer_context *ctx)
+static void mixer_poweron(struct exynos_drm_manager *mgr)
{
+ struct mixer_context *ctx = mgr->ctx;
struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex);
@@ -909,6 +1061,8 @@ static void mixer_poweron(struct mixer_context *ctx)
ctx->powered = true;
mutex_unlock(&ctx->mixer_mutex);
+ pm_runtime_get_sync(ctx->dev);
+
clk_prepare_enable(res->mixer);
if (ctx->vp_enabled) {
clk_prepare_enable(res->vp);
@@ -918,11 +1072,12 @@ static void mixer_poweron(struct mixer_context *ctx)
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
mixer_win_reset(ctx);
- mixer_window_resume(ctx);
+ mixer_window_resume(mgr);
}
-static void mixer_poweroff(struct mixer_context *ctx)
+static void mixer_poweroff(struct exynos_drm_manager *mgr)
{
+ struct mixer_context *ctx = mgr->ctx;
struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex);
@@ -930,7 +1085,7 @@ static void mixer_poweroff(struct mixer_context *ctx)
goto out;
mutex_unlock(&ctx->mixer_mutex);
- mixer_window_suspend(ctx);
+ mixer_window_suspend(mgr);
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
@@ -940,6 +1095,8 @@ static void mixer_poweroff(struct mixer_context *ctx)
clk_disable_unprepare(res->sclk_mixer);
}
+ pm_runtime_put_sync(ctx->dev);
+
mutex_lock(&ctx->mixer_mutex);
ctx->powered = false;
@@ -947,20 +1104,16 @@ out:
mutex_unlock(&ctx->mixer_mutex);
}
-static void mixer_dpms(void *ctx, int mode)
+static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
{
- struct mixer_context *mixer_ctx = ctx;
-
switch (mode) {
case DRM_MODE_DPMS_ON:
- if (pm_runtime_suspended(mixer_ctx->dev))
- pm_runtime_get_sync(mixer_ctx->dev);
+ mixer_poweron(mgr);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- if (!pm_runtime_suspended(mixer_ctx->dev))
- pm_runtime_put_sync(mixer_ctx->dev);
+ mixer_poweroff(mgr);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
@@ -968,169 +1121,42 @@ static void mixer_dpms(void *ctx, int mode)
}
}
-static struct exynos_mixer_ops mixer_ops = {
- /* manager */
- .iommu_on = mixer_iommu_on,
- .enable_vblank = mixer_enable_vblank,
- .disable_vblank = mixer_disable_vblank,
- .wait_for_vblank = mixer_wait_for_vblank,
- .dpms = mixer_dpms,
-
- /* overlay */
- .win_mode_set = mixer_win_mode_set,
- .win_commit = mixer_win_commit,
- .win_disable = mixer_win_disable,
-
- /* display */
- .check_mode = mixer_check_mode,
-};
-
-static irqreturn_t mixer_irq_handler(int irq, void *arg)
-{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
- struct mixer_context *ctx = drm_hdmi_ctx->ctx;
- struct mixer_resources *res = &ctx->mixer_res;
- u32 val, base, shadow;
-
- spin_lock(&res->reg_slock);
-
- /* read interrupt status for handling and clearing flags for VSYNC */
- val = mixer_reg_read(res, MXR_INT_STATUS);
-
- /* handling VSYNC */
- if (val & MXR_INT_STATUS_VSYNC) {
- /* interlace scan need to check shadow register */
- if (ctx->interlace) {
- base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
- shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
- if (base != shadow)
- goto out;
-
- base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
- shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
- if (base != shadow)
- goto out;
- }
-
- drm_handle_vblank(drm_hdmi_ctx->drm_dev, ctx->pipe);
- exynos_drm_crtc_finish_pageflip(drm_hdmi_ctx->drm_dev,
- ctx->pipe);
-
- /* set wait vsync event to zero and wake up queue. */
- if (atomic_read(&ctx->wait_vsync_event)) {
- atomic_set(&ctx->wait_vsync_event, 0);
- wake_up(&ctx->wait_vsync_queue);
- }
- }
-
-out:
- /* clear interrupts */
- if (~val & MXR_INT_EN_VSYNC) {
- /* vsync interrupt use different bit for read and clear */
- val &= ~MXR_INT_EN_VSYNC;
- val |= MXR_INT_CLEAR_VSYNC;
- }
- mixer_reg_write(res, MXR_INT_STATUS, val);
-
- spin_unlock(&res->reg_slock);
-
- return IRQ_HANDLED;
-}
-
-static int mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
- struct platform_device *pdev)
+/* Only valid for Mixer version 16.0.33.0 */
+int mixer_check_mode(struct drm_display_mode *mode)
{
- struct mixer_context *mixer_ctx = ctx->ctx;
- struct device *dev = &pdev->dev;
- struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
- struct resource *res;
- int ret;
-
- spin_lock_init(&mixer_res->reg_slock);
-
- mixer_res->mixer = devm_clk_get(dev, "mixer");
- if (IS_ERR(mixer_res->mixer)) {
- dev_err(dev, "failed to get clock 'mixer'\n");
- return -ENODEV;
- }
-
- mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
- if (IS_ERR(mixer_res->sclk_hdmi)) {
- dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
- return -ENODEV;
- }
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "get memory resource failed.\n");
- return -ENXIO;
- }
+ u32 w, h;
- mixer_res->mixer_regs = devm_ioremap(dev, res->start,
- resource_size(res));
- if (mixer_res->mixer_regs == NULL) {
- dev_err(dev, "register mapping failed.\n");
- return -ENXIO;
- }
+ w = mode->hdisplay;
+ h = mode->vdisplay;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(dev, "get interrupt resource failed.\n");
- return -ENXIO;
- }
+ DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
+ mode->hdisplay, mode->vdisplay, mode->vrefresh,
+ (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
- ret = devm_request_irq(dev, res->start, mixer_irq_handler,
- 0, "drm_mixer", ctx);
- if (ret) {
- dev_err(dev, "request interrupt failed.\n");
- return ret;
- }
- mixer_res->irq = res->start;
+ if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
+ (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
+ (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
+ return 0;
- return 0;
+ return -EINVAL;
}
-static int vp_resources_init(struct exynos_drm_hdmi_context *ctx,
- struct platform_device *pdev)
-{
- struct mixer_context *mixer_ctx = ctx->ctx;
- struct device *dev = &pdev->dev;
- struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
- struct resource *res;
-
- mixer_res->vp = devm_clk_get(dev, "vp");
- if (IS_ERR(mixer_res->vp)) {
- dev_err(dev, "failed to get clock 'vp'\n");
- return -ENODEV;
- }
- mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
- if (IS_ERR(mixer_res->sclk_mixer)) {
- dev_err(dev, "failed to get clock 'sclk_mixer'\n");
- return -ENODEV;
- }
- mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
- if (IS_ERR(mixer_res->sclk_dac)) {
- dev_err(dev, "failed to get clock 'sclk_dac'\n");
- return -ENODEV;
- }
-
- if (mixer_res->sclk_hdmi)
- clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res == NULL) {
- dev_err(dev, "get memory resource failed.\n");
- return -ENXIO;
- }
-
- mixer_res->vp_regs = devm_ioremap(dev, res->start,
- resource_size(res));
- if (mixer_res->vp_regs == NULL) {
- dev_err(dev, "register mapping failed.\n");
- return -ENXIO;
- }
+static struct exynos_drm_manager_ops mixer_manager_ops = {
+ .initialize = mixer_initialize,
+ .remove = mixer_mgr_remove,
+ .dpms = mixer_dpms,
+ .enable_vblank = mixer_enable_vblank,
+ .disable_vblank = mixer_disable_vblank,
+ .wait_for_vblank = mixer_wait_for_vblank,
+ .win_mode_set = mixer_win_mode_set,
+ .win_commit = mixer_win_commit,
+ .win_disable = mixer_win_disable,
+};
- return 0;
-}
+static struct exynos_drm_manager mixer_manager = {
+ .type = EXYNOS_DISPLAY_TYPE_HDMI,
+ .ops = &mixer_manager_ops,
+};
static struct mixer_drv_data exynos5420_mxr_drv_data = {
.version = MXR_VER_128_0_0_184,
@@ -1177,21 +1203,16 @@ static struct of_device_id mixer_match_types[] = {
static int mixer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct mixer_context *ctx;
struct mixer_drv_data *drv;
- int ret;
dev_info(dev, "probe start\n");
- drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
- GFP_KERNEL);
- if (!drm_hdmi_ctx)
- return -ENOMEM;
-
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ DRM_ERROR("failed to alloc mixer context.\n");
return -ENOMEM;
+ }
mutex_init(&ctx->mixer_mutex);
@@ -1204,46 +1225,20 @@ static int mixer_probe(struct platform_device *pdev)
platform_get_device_id(pdev)->driver_data;
}
+ ctx->pdev = pdev;
ctx->dev = dev;
- ctx->parent_ctx = (void *)drm_hdmi_ctx;
- drm_hdmi_ctx->ctx = (void *)ctx;
ctx->vp_enabled = drv->is_vp_enabled;
ctx->mxr_ver = drv->version;
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
- platform_set_drvdata(pdev, drm_hdmi_ctx);
-
- /* acquire resources: regs, irqs, clocks */
- ret = mixer_resources_init(drm_hdmi_ctx, pdev);
- if (ret) {
- DRM_ERROR("mixer_resources_init failed\n");
- goto fail;
- }
-
- if (ctx->vp_enabled) {
- /* acquire vp resources: regs, irqs, clocks */
- ret = vp_resources_init(drm_hdmi_ctx, pdev);
- if (ret) {
- DRM_ERROR("vp_resources_init failed\n");
- goto fail;
- }
- }
-
- /* attach mixer driver to common hdmi. */
- exynos_mixer_drv_attach(drm_hdmi_ctx);
-
- /* register specific callback point to common hdmi. */
- exynos_mixer_ops_register(&mixer_ops);
+ mixer_manager.ctx = ctx;
+ platform_set_drvdata(pdev, &mixer_manager);
+ exynos_drm_manager_register(&mixer_manager);
pm_runtime_enable(dev);
return 0;
-
-
-fail:
- dev_info(dev, "probe failed\n");
- return ret;
}
static int mixer_remove(struct platform_device *pdev)
@@ -1255,70 +1250,10 @@ static int mixer_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int mixer_suspend(struct device *dev)
-{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
- struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-
- if (pm_runtime_suspended(dev)) {
- DRM_DEBUG_KMS("Already suspended\n");
- return 0;
- }
-
- mixer_poweroff(ctx);
-
- return 0;
-}
-
-static int mixer_resume(struct device *dev)
-{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
- struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-
- if (!pm_runtime_suspended(dev)) {
- DRM_DEBUG_KMS("Already resumed\n");
- return 0;
- }
-
- mixer_poweron(ctx);
-
- return 0;
-}
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
-static int mixer_runtime_suspend(struct device *dev)
-{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
- struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-
- mixer_poweroff(ctx);
-
- return 0;
-}
-
-static int mixer_runtime_resume(struct device *dev)
-{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
- struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-
- mixer_poweron(ctx);
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops mixer_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mixer_suspend, mixer_resume)
- SET_RUNTIME_PM_OPS(mixer_runtime_suspend, mixer_runtime_resume, NULL)
-};
-
struct platform_driver mixer_driver = {
.driver = {
.name = "exynos-mixer",
.owner = THIS_MODULE,
- .pm = &mixer_pm_ops,
.of_match_table = mixer_match_types,
},
.probe = mixer_probe,
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.h b/drivers/gpu/drm/exynos/exynos_mixer.h
new file mode 100644
index 000000000000..3811e417f0e9
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_mixer.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013 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 _EXYNOS_MIXER_H_
+#define _EXYNOS_MIXER_H_
+
+/* This function returns 0 if the given timing is valid for the mixer */
+int mixer_check_mode(struct drm_display_mode *mode);
+
+#endif
diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile
index e9064dd9045d..b15315576376 100644
--- a/drivers/gpu/drm/gma500/Makefile
+++ b/drivers/gpu/drm/gma500/Makefile
@@ -13,9 +13,11 @@ gma500_gfx-y += \
intel_i2c.o \
intel_gmbus.o \
mmu.o \
+ blitter.o \
power.o \
psb_drv.o \
gma_display.o \
+ gma_device.o \
psb_intel_display.o \
psb_intel_lvds.o \
psb_intel_modes.o \
diff --git a/drivers/gpu/drm/gma500/blitter.c b/drivers/gpu/drm/gma500/blitter.c
new file mode 100644
index 000000000000..9cd54a6fb899
--- /dev/null
+++ b/drivers/gpu/drm/gma500/blitter.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, Patrik Jakobsson
+ * 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.
+ *
+ * Authors: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+ */
+
+#include "psb_drv.h"
+
+#include "blitter.h"
+#include "psb_reg.h"
+
+/* Wait for the blitter to be completely idle */
+int gma_blt_wait_idle(struct drm_psb_private *dev_priv)
+{
+ unsigned long stop = jiffies + HZ;
+ int busy = 1;
+
+ /* NOP for Cedarview */
+ if (IS_CDV(dev_priv->dev))
+ return 0;
+
+ /* First do a quick check */
+ if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) &&
+ ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0))
+ return 0;
+
+ do {
+ busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY);
+ } while (busy && !time_after_eq(jiffies, stop));
+
+ if (busy)
+ return -EBUSY;
+
+ do {
+ busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
+ _PSB_C2B_STATUS_BUSY) != 0);
+ } while (busy && !time_after_eq(jiffies, stop));
+
+ /* If still busy, we probably have a hang */
+ return (busy) ? -EBUSY : 0;
+}
diff --git a/drivers/gpu/drm/gma500/blitter.h b/drivers/gpu/drm/gma500/blitter.h
new file mode 100644
index 000000000000..b83648df590d
--- /dev/null
+++ b/drivers/gpu/drm/gma500/blitter.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014, Patrik Jakobsson
+ * 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.
+ *
+ * Authors: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+ */
+
+#ifndef __BLITTER_H
+#define __BLITTER_H
+
+extern int gma_blt_wait_idle(struct drm_psb_private *dev_priv);
+
+#endif
diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c
index 5a9a6a3063a8..3531f90e53d0 100644
--- a/drivers/gpu/drm/gma500/cdv_device.c
+++ b/drivers/gpu/drm/gma500/cdv_device.c
@@ -26,6 +26,7 @@
#include "psb_intel_reg.h"
#include "intel_bios.h"
#include "cdv_device.h"
+#include "gma_device.h"
#define VGA_SR_INDEX 0x3c4
#define VGA_SR_DATA 0x3c5
@@ -426,43 +427,6 @@ 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;
- break;
- default:
- dev_priv->core_freq = 0;
- }
-}
-
static void cdv_hotplug_work_func(struct work_struct *work)
{
struct drm_psb_private *dev_priv = container_of(work, struct drm_psb_private,
@@ -618,7 +582,7 @@ static int cdv_chip_setup(struct drm_device *dev)
if (pci_enable_msi(dev->pdev))
dev_warn(dev->dev, "Enabling MSI failed!\n");
dev_priv->regmap = cdv_regmap;
- cdv_get_core_freq(dev);
+ gma_get_core_freq(dev);
psb_intel_opregion_init(dev);
psb_intel_init_bios(dev);
cdv_hotplug_enable(dev, false);
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
index 661af492173d..c18268cd516e 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -81,13 +81,6 @@ static int cdv_intel_crt_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
-static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder,
- const 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)
@@ -224,7 +217,7 @@ static int cdv_intel_crt_set_property(struct drm_connector *connector,
static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = {
.dpms = cdv_intel_crt_dpms,
- .mode_fixup = cdv_intel_crt_mode_fixup,
+ .mode_fixup = gma_encoder_mode_fixup,
.prepare = gma_encoder_prepare,
.commit = gma_encoder_commit,
.mode_set = cdv_intel_crt_mode_set,
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
index 8fbfa06da62d..66727328832d 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -412,8 +412,11 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
int refclk,
struct gma_clock_t *best_clock)
{
+ struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
struct gma_clock_t clock;
- if (refclk == 27000) {
+
+ switch (refclk) {
+ case 27000:
if (target < 200000) {
clock.p1 = 2;
clock.p2 = 10;
@@ -427,7 +430,9 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
clock.m1 = 0;
clock.m2 = 98;
}
- } else if (refclk == 100000) {
+ break;
+
+ case 100000:
if (target < 200000) {
clock.p1 = 2;
clock.p2 = 10;
@@ -441,12 +446,13 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
clock.m1 = 0;
clock.m2 = 133;
}
- } else
+ break;
+
+ default:
return false;
- clock.m = clock.m2 + 2;
- clock.p = clock.p1 * clock.p2;
- clock.vco = (refclk * clock.m) / clock.n;
- clock.dot = clock.vco / clock.p;
+ }
+
+ gma_crtc->clock_funcs->clock(refclk, &clock);
memcpy(best_clock, &clock, sizeof(struct gma_clock_t));
return true;
}
@@ -463,54 +469,11 @@ static bool cdv_intel_pipe_enabled(struct drm_device *dev, int pipe)
crtc = dev_priv->pipe_to_crtc_mapping[pipe];
gma_crtc = to_gma_crtc(crtc);
- if (crtc->fb == NULL || !gma_crtc->active)
+ if (crtc->primary->fb == NULL || !gma_crtc->active)
return false;
return true;
}
-static bool cdv_intel_single_pipe_active (struct drm_device *dev)
-{
- uint32_t pipe_enabled = 0;
-
- if (cdv_intel_pipe_enabled(dev, 0))
- pipe_enabled |= FIFO_PIPEA;
-
- if (cdv_intel_pipe_enabled(dev, 1))
- pipe_enabled |= FIFO_PIPEB;
-
-
- DRM_DEBUG_KMS("pipe enabled %x\n", pipe_enabled);
-
- if (pipe_enabled == FIFO_PIPEA || pipe_enabled == FIFO_PIPEB)
- return true;
- else
- return false;
-}
-
-static bool is_pipeb_lvds(struct drm_device *dev, struct drm_crtc *crtc)
-{
- struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
- struct drm_mode_config *mode_config = &dev->mode_config;
- struct drm_connector *connector;
-
- if (gma_crtc->pipe != 1)
- return false;
-
- list_for_each_entry(connector, &mode_config->connector_list, head) {
- struct gma_encoder *gma_encoder =
- gma_attached_encoder(connector);
-
- if (!connector->encoder
- || connector->encoder->crtc != crtc)
- continue;
-
- if (gma_encoder->type == INTEL_OUTPUT_LVDS)
- return true;
- }
-
- return false;
-}
-
void cdv_disable_sr(struct drm_device *dev)
{
if (REG_READ(FW_BLC_SELF) & FW_BLC_SELF_EN) {
@@ -535,8 +498,10 @@ void cdv_disable_sr(struct drm_device *dev)
void cdv_update_wm(struct drm_device *dev, struct drm_crtc *crtc)
{
struct drm_psb_private *dev_priv = dev->dev_private;
+ struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
- if (cdv_intel_single_pipe_active(dev)) {
+ /* Is only one pipe enabled? */
+ if (cdv_intel_pipe_enabled(dev, 0) ^ cdv_intel_pipe_enabled(dev, 1)) {
u32 fw;
fw = REG_READ(DSPFW1);
@@ -557,7 +522,9 @@ void cdv_update_wm(struct drm_device *dev, struct drm_crtc *crtc)
/* ignore FW4 */
- if (is_pipeb_lvds(dev, crtc)) {
+ /* Is pipe b lvds ? */
+ if (gma_crtc->pipe == 1 &&
+ gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
REG_WRITE(DSPFW5, 0x00040330);
} else {
fw = (3 << DSP_PLANE_B_FIFO_WM1_SHIFT) |
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index 0490ce36b53f..9ff30c2efadb 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1693,7 +1693,7 @@ done:
struct drm_crtc *crtc = encoder->base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y,
- crtc->fb);
+ crtc->primary->fb);
}
return 0;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index 1c0d723b8d24..b99084b3f706 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -89,13 +89,6 @@ static void cdv_hdmi_mode_set(struct drm_encoder *encoder,
REG_READ(hdmi_priv->hdmi_reg);
}
-static bool cdv_hdmi_mode_fixup(struct drm_encoder *encoder,
- const 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;
@@ -199,7 +192,7 @@ static int cdv_hdmi_set_property(struct drm_connector *connector,
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))
+ encoder->crtc->x, encoder->crtc->y, encoder->crtc->primary->fb))
return -1;
} else {
struct drm_encoder_helper_funcs *helpers
@@ -262,7 +255,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector)
static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = {
.dpms = cdv_hdmi_dpms,
- .mode_fixup = cdv_hdmi_mode_fixup,
+ .mode_fixup = gma_encoder_mode_fixup,
.prepare = gma_encoder_prepare,
.mode_set = cdv_hdmi_mode_set,
.commit = gma_encoder_commit,
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 20e08e65d46c..8ecc920fc26d 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -494,7 +494,7 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
&crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
- encoder->crtc->fb))
+ encoder->crtc->primary->fb))
return -1;
}
} else if (!strcmp(property->name, "backlight") && encoder) {
@@ -712,6 +712,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
+ mutex_lock(&dev->mode_config.mutex);
psb_intel_ddc_get_modes(connector,
&gma_encoder->ddc_bus->adapter);
list_for_each_entry(scan, &connector->probed_modes, head) {
@@ -772,10 +773,12 @@ void cdv_intel_lvds_init(struct drm_device *dev,
}
out:
+ mutex_unlock(&dev->mode_config.mutex);
drm_sysfs_connector_add(connector);
return;
failed_find:
+ mutex_unlock(&dev->mode_config.mutex);
printk(KERN_ERR "Failed find\n");
if (gma_encoder->ddc_bus)
psb_intel_i2c_destroy(gma_encoder->ddc_bus);
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index 94b3fec22c28..e7fcc148f333 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -319,7 +319,7 @@ static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size)
{
struct gtt_range *backing;
/* Begin by trying to use stolen memory backing */
- backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1);
+ backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1, PAGE_SIZE);
if (backing) {
drm_gem_private_object_init(dev, &backing->gem, aligned_size);
return backing;
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
index e2db48a81ed0..c707fa6fca85 100644
--- a/drivers/gpu/drm/gma500/gem.c
+++ b/drivers/gpu/drm/gma500/gem.c
@@ -62,9 +62,6 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
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 */
@@ -98,8 +95,8 @@ unlock:
* 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)
+int psb_gem_create(struct drm_file *file, struct drm_device *dev, u64 size,
+ u32 *handlep, int stolen, u32 align)
{
struct gtt_range *r;
int ret;
@@ -109,7 +106,7 @@ static int psb_gem_create(struct drm_file *file,
/* Allocate our object - for now a direct gtt range which is not
stolen memory backed */
- r = psb_gtt_alloc_range(dev, size, "gem", 0);
+ r = psb_gtt_alloc_range(dev, size, "gem", 0, PAGE_SIZE);
if (r == NULL) {
dev_err(dev->dev, "no memory for %lld byte GEM object\n", size);
return -ENOSPC;
@@ -153,7 +150,8 @@ int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
{
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);
+ return psb_gem_create(file, dev, args->size, &args->handle, 0,
+ PAGE_SIZE);
}
/**
@@ -229,47 +227,3 @@ fail:
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;
-
- drm_gem_private_object_init(dev, &gtt->gem, size);
- if (drm_gem_handle_create(file, &gtt->gem, handle) == 0)
- return 0;
-
- drm_gem_object_release(&gtt->gem);
- 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 & GMA_GEM_CREATE_STOLEN) {
- ret = psb_gem_create_stolen(file, dev, args->size,
- &args->handle);
- if (ret == 0)
- return 0;
- /* Fall throguh */
- args->flags &= ~GMA_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/drivers/gpu/drm/gma500/gem.h b/drivers/gpu/drm/gma500/gem.h
new file mode 100644
index 000000000000..1381c5190f46
--- /dev/null
+++ b/drivers/gpu/drm/gma500/gem.h
@@ -0,0 +1,21 @@
+/**************************************************************************
+ * Copyright (c) 2014 Patrik Jakobsson
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef _GEM_H
+#define _GEM_H
+
+extern int psb_gem_create(struct drm_file *file, struct drm_device *dev,
+ u64 size, u32 *handlep, int stolen, u32 align);
+#endif
diff --git a/drivers/gpu/drm/gma500/gma_device.c b/drivers/gpu/drm/gma500/gma_device.c
new file mode 100644
index 000000000000..4a295f9ba067
--- /dev/null
+++ b/drivers/gpu/drm/gma500/gma_device.c
@@ -0,0 +1,56 @@
+/**************************************************************************
+ * 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.
+ *
+ **************************************************************************/
+
+#include <drm/drmP.h>
+#include "psb_drv.h"
+
+void gma_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;
+ break;
+ default:
+ dev_priv->core_freq = 0;
+ }
+}
diff --git a/drivers/gpu/drm/gma500/gma_device.h b/drivers/gpu/drm/gma500/gma_device.h
new file mode 100644
index 000000000000..e1dbb007b820
--- /dev/null
+++ b/drivers/gpu/drm/gma500/gma_device.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.
+ *
+ **************************************************************************/
+
+#ifndef _GMA_DEVICE_H
+#define _GMA_DEVICE_H
+
+extern void gma_get_core_freq(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c
index 386de2c9dc86..9bb9bddd881a 100644
--- a/drivers/gpu/drm/gma500/gma_display.c
+++ b/drivers/gpu/drm/gma500/gma_display.c
@@ -59,7 +59,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
- struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
+ struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
@@ -70,7 +70,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
/* no fb bound */
- if (!crtc->fb) {
+ if (!crtc->primary->fb) {
dev_err(dev->dev, "No FB bound\n");
goto gma_pipe_cleaner;
}
@@ -81,19 +81,19 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y,
if (ret < 0)
goto gma_pipe_set_base_exit;
start = psbfb->gtt->offset;
- offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
+ offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
- REG_WRITE(map->stride, crtc->fb->pitches[0]);
+ REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
- if (crtc->fb->depth == 15)
+ if (crtc->primary->fb->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;
@@ -485,6 +485,13 @@ int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
return 0;
}
+bool gma_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
bool gma_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -511,8 +518,8 @@ void gma_crtc_disable(struct drm_crtc *crtc)
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
- if (crtc->fb) {
- gt = to_psb_fb(crtc->fb)->gtt;
+ if (crtc->primary->fb) {
+ gt = to_psb_fb(crtc->primary->fb)->gtt;
psb_gtt_unpin(gt);
}
}
diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h
index 78b9f986a6e5..ed569d8a6af3 100644
--- a/drivers/gpu/drm/gma500/gma_display.h
+++ b/drivers/gpu/drm/gma500/gma_display.h
@@ -90,6 +90,9 @@ extern void gma_crtc_restore(struct drm_crtc *crtc);
extern void gma_encoder_prepare(struct drm_encoder *encoder);
extern void gma_encoder_commit(struct drm_encoder *encoder);
extern void gma_encoder_destroy(struct drm_encoder *encoder);
+extern bool gma_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
/* Common clock related functions */
extern const struct gma_limit_t *gma_limit(struct drm_crtc *crtc, int refclk);
diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c
index 2db731f00930..592d205a0089 100644
--- a/drivers/gpu/drm/gma500/gtt.c
+++ b/drivers/gpu/drm/gma500/gtt.c
@@ -22,6 +22,7 @@
#include <drm/drmP.h>
#include <linux/shmem_fs.h>
#include "psb_drv.h"
+#include "blitter.h"
/*
@@ -105,11 +106,13 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r,
/* 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);
+ pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
+ PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
for (i = 0; i < r->roll; i++) {
- pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
+ pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
+ PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
/* Make sure all the entries are set before we return */
@@ -127,7 +130,7 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r,
* 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)
+void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
{
struct drm_psb_private *dev_priv = dev->dev_private;
u32 __iomem *gtt_slot;
@@ -137,7 +140,8 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
WARN_ON(r->stolen);
gtt_slot = psb_gtt_entry(dev, r);
- pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0);
+ pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page),
+ PSB_MMU_CACHED_MEMORY);
for (i = 0; i < r->npage; i++)
iowrite32(pte, gtt_slot++);
@@ -176,11 +180,13 @@ void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll)
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);
+ pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
+ PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
for (i = 0; i < r->roll; i++) {
- pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
+ pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]),
+ PSB_MMU_CACHED_MEMORY);
iowrite32(pte, gtt_slot++);
}
ioread32(gtt_slot - 1);
@@ -240,6 +246,7 @@ 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;
+ u32 gpu_base = dev_priv->gtt.gatt_start;
mutex_lock(&dev_priv->gtt_mutex);
@@ -252,6 +259,9 @@ int psb_gtt_pin(struct gtt_range *gt)
psb_gtt_detach_pages(gt);
goto out;
}
+ psb_mmu_insert_pages(psb_mmu_get_default_pd(dev_priv->mmu),
+ gt->pages, (gpu_base + gt->offset),
+ gt->npage, 0, 0, PSB_MMU_CACHED_MEMORY);
}
gt->in_gart++;
out:
@@ -274,16 +284,30 @@ void psb_gtt_unpin(struct gtt_range *gt)
{
struct drm_device *dev = gt->gem.dev;
struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 gpu_base = dev_priv->gtt.gatt_start;
+ int ret;
+ /* While holding the gtt_mutex no new blits can be initiated */
mutex_lock(&dev_priv->gtt_mutex);
+ /* Wait for any possible usage of the memory to be finished */
+ ret = gma_blt_wait_idle(dev_priv);
+ if (ret) {
+ DRM_ERROR("Failed to idle the blitter, unpin failed!");
+ goto out;
+ }
+
WARN_ON(!gt->in_gart);
gt->in_gart--;
if (gt->in_gart == 0 && gt->stolen == 0) {
+ psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu),
+ (gpu_base + gt->offset), gt->npage, 0, 0);
psb_gtt_remove(dev, gt);
psb_gtt_detach_pages(gt);
}
+
+out:
mutex_unlock(&dev_priv->gtt_mutex);
}
@@ -306,7 +330,7 @@ void psb_gtt_unpin(struct gtt_range *gt)
* as in use.
*/
struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
- const char *name, int backed)
+ const char *name, int backed, u32 align)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct gtt_range *gt;
@@ -334,7 +358,7 @@ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
/* Ensure this is set for non GEM objects */
gt->gem.dev = dev;
ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
- len, start, end, PAGE_SIZE, NULL, NULL);
+ len, start, end, align, NULL, NULL);
if (ret == 0) {
gt->offset = gt->resource.start - r->start;
return gt;
@@ -497,6 +521,7 @@ int psb_gtt_init(struct drm_device *dev, int resume)
if (!resume)
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;
@@ -512,7 +537,7 @@ int psb_gtt_init(struct drm_device *dev, int resume)
dev_dbg(dev->dev, "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);
+ pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY);
iowrite32(pte, dev_priv->gtt_map + i);
}
@@ -521,7 +546,7 @@ int psb_gtt_init(struct drm_device *dev, int resume)
*/
pfn_base = page_to_pfn(dev_priv->scratch_page);
- pte = psb_gtt_mask_pte(pfn_base, 0);
+ pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY);
for (; i < gtt_pages; ++i)
iowrite32(pte, dev_priv->gtt_map + i);
diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h
index 6191d10acf33..f5860a739bd8 100644
--- a/drivers/gpu/drm/gma500/gtt.h
+++ b/drivers/gpu/drm/gma500/gtt.h
@@ -53,7 +53,8 @@ struct gtt_range {
};
extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
- const char *name, int backed);
+ const char *name, int backed,
+ u32 align);
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);
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
index 860a4ee9baaf..6e91b20ce2e5 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
@@ -287,7 +287,7 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
&gma_crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
- encoder->crtc->fb))
+ encoder->crtc->primary->fb))
goto set_prop_error;
} else {
struct drm_encoder_helper_funcs *funcs =
diff --git a/drivers/gpu/drm/gma500/mdfld_intel_display.c b/drivers/gpu/drm/gma500/mdfld_intel_display.c
index 321c00a944e9..8cc8a5abbc7b 100644
--- a/drivers/gpu/drm/gma500/mdfld_intel_display.c
+++ b/drivers/gpu/drm/gma500/mdfld_intel_display.c
@@ -166,7 +166,7 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
- struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
+ struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
@@ -178,12 +178,12 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
dev_dbg(dev->dev, "pipe = 0x%x.\n", pipe);
/* no fb bound */
- if (!crtc->fb) {
+ if (!crtc->primary->fb) {
dev_dbg(dev->dev, "No FB bound\n");
return 0;
}
- ret = check_fb(crtc->fb);
+ ret = check_fb(crtc->primary->fb);
if (ret)
return ret;
@@ -196,18 +196,18 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
start = psbfb->gtt->offset;
- offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
+ offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
- REG_WRITE(map->stride, crtc->fb->pitches[0]);
+ REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
- if (crtc->fb->depth == 15)
+ if (crtc->primary->fb->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;
@@ -700,7 +700,7 @@ static int mdfld_crtc_mode_set(struct drm_crtc *crtc,
}
#endif
- ret = check_fb(crtc->fb);
+ ret = check_fb(crtc->primary->fb);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c
index c3e67ba94446..0eaf11c19939 100644
--- a/drivers/gpu/drm/gma500/mmu.c
+++ b/drivers/gpu/drm/gma500/mmu.c
@@ -18,6 +18,7 @@
#include <drm/drmP.h>
#include "psb_drv.h"
#include "psb_reg.h"
+#include "mmu.h"
/*
* Code for the SGX MMU:
@@ -47,51 +48,6 @@
* 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;
@@ -102,13 +58,13 @@ static inline uint32_t psb_mmu_pd_index(uint32_t offset)
return offset >> PSB_PDE_SHIFT;
}
+#if defined(CONFIG_X86)
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)
+static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
{
if (!driver->has_clflush)
return;
@@ -117,62 +73,77 @@ static inline void psb_mmu_clflush(struct psb_mmu_driver *driver,
psb_clflush(addr);
mb();
}
+#else
-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);
- mb();
- for (i = 0; i < clflush_count; ++i) {
- psb_clflush(clf);
- clf += clflush_add;
- }
- mb();
- kunmap_atomic(clf);
+static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr)
+{;
}
-static void psb_pages_clflush(struct psb_mmu_driver *driver,
- struct page *page[], unsigned long num_pages)
-{
- int i;
-
- if (!driver->has_clflush)
- return ;
+#endif
- 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)
+static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, int force)
{
+ struct drm_device *dev = driver->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (atomic_read(&driver->needs_tlbflush) || force) {
+ uint32_t val = PSB_RSGX32(PSB_CR_BIF_CTRL);
+ PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
+
+ /* Make sure data cache is turned off before enabling it */
+ wmb();
+ PSB_WSGX32(val & ~_PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
+ (void)PSB_RSGX32(PSB_CR_BIF_CTRL);
+ if (driver->msvdx_mmu_invaldc)
+ atomic_set(driver->msvdx_mmu_invaldc, 1);
+ }
atomic_set(&driver->needs_tlbflush, 0);
}
+#if 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);
}
+#endif
-void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot)
+void psb_mmu_flush(struct psb_mmu_driver *driver)
{
- if (rc_prot)
- down_write(&driver->sem);
- if (rc_prot)
- up_write(&driver->sem);
+ struct drm_device *dev = driver->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ uint32_t val;
+
+ down_write(&driver->sem);
+ val = PSB_RSGX32(PSB_CR_BIF_CTRL);
+ if (atomic_read(&driver->needs_tlbflush))
+ PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL);
+ else
+ PSB_WSGX32(val | _PSB_CB_CTRL_FLUSH, PSB_CR_BIF_CTRL);
+
+ /* Make sure data cache is turned off and MMU is flushed before
+ restoring bank interface control register */
+ wmb();
+ PSB_WSGX32(val & ~(_PSB_CB_CTRL_FLUSH | _PSB_CB_CTRL_INVALDC),
+ PSB_CR_BIF_CTRL);
+ (void)PSB_RSGX32(PSB_CR_BIF_CTRL);
+
+ atomic_set(&driver->needs_tlbflush, 0);
+ if (driver->msvdx_mmu_invaldc)
+ atomic_set(driver->msvdx_mmu_invaldc, 1);
+ 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);
+ struct drm_device *dev = pd->driver->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ uint32_t offset = (hw_context == 0) ? PSB_CR_BIF_DIR_LIST_BASE0 :
+ PSB_CR_BIF_DIR_LIST_BASE1 + hw_context * 4;
+
down_write(&pd->driver->sem);
+ PSB_WSGX32(page_to_pfn(pd->p) << PAGE_SHIFT, offset);
wmb();
psb_mmu_flush_pd_locked(pd->driver, 1);
pd->hw_context = hw_context;
@@ -183,7 +154,6 @@ void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context)
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;
}
@@ -223,12 +193,10 @@ struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
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);
+ 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;
@@ -279,12 +247,16 @@ static void psb_mmu_free_pt(struct psb_mmu_pt *pt)
void psb_mmu_free_pagedir(struct psb_mmu_pd *pd)
{
struct psb_mmu_driver *driver = pd->driver;
+ struct drm_device *dev = driver->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
struct psb_mmu_pt *pt;
int i;
down_write(&driver->sem);
- if (pd->hw_context != -1)
+ if (pd->hw_context != -1) {
+ PSB_WSGX32(0, PSB_CR_BIF_DIR_LIST_BASE0 + pd->hw_context * 4);
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. */
@@ -331,7 +303,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
*ptes++ = pd->invalid_pte;
-
+#if defined(CONFIG_X86)
if (pd->driver->has_clflush && pd->hw_context != -1) {
mb();
for (i = 0; i < clflush_count; ++i) {
@@ -340,7 +312,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
}
mb();
}
-
+#endif
kunmap_atomic(v);
spin_unlock(lock);
@@ -351,7 +323,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
return pt;
}
-static struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
+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);
@@ -383,7 +355,7 @@ static struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
kunmap_atomic((void *) v);
if (pd->hw_context != -1) {
- psb_mmu_clflush(pd->driver, (void *) &v[index]);
+ psb_mmu_clflush(pd->driver, (void *)&v[index]);
atomic_set(&pd->driver->needs_tlbflush, 1);
}
}
@@ -420,8 +392,7 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt)
pd->tables[pt->index] = NULL;
if (pd->hw_context != -1) {
- psb_mmu_clflush(pd->driver,
- (void *) &v[pt->index]);
+ psb_mmu_clflush(pd->driver, (void *)&v[pt->index]);
atomic_set(&pd->driver->needs_tlbflush, 1);
}
kunmap_atomic(pt->v);
@@ -432,8 +403,8 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt)
spin_unlock(&pd->driver->lock);
}
-static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt,
- unsigned long addr, uint32_t pte)
+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;
}
@@ -444,69 +415,50 @@ static inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt,
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)
+struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver)
{
- uint32_t *v;
- uint32_t start = psb_mmu_pd_index(mmu_offset);
- struct psb_mmu_driver *driver = pd->driver;
- int num_pages = gtt_pages;
+ struct psb_mmu_pd *pd;
down_read(&driver->sem);
- spin_lock(&driver->lock);
-
- v = kmap_atomic(pd->p);
- 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);
- spin_unlock(&driver->lock);
-
- if (pd->hw_context != -1)
- atomic_set(&pd->driver->needs_tlbflush, 1);
+ pd = driver->default_pd;
+ up_read(&driver->sem);
- up_read(&pd->driver->sem);
- psb_mmu_flush_pd(pd->driver, 0);
+ return pd;
}
-struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver)
+/* 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;
- /* down_read(&driver->sem); */
- pd = driver->default_pd;
- /* up_read(&driver->sem); */
-
- return 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)
{
+ struct drm_device *dev = driver->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ PSB_WSGX32(driver->bif_ctrl, PSB_CR_BIF_CTRL);
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 *psb_mmu_driver_init(struct drm_device *dev,
+ int trap_pagefaults,
+ int invalid_type,
+ atomic_t *msvdx_mmu_invaldc)
{
struct psb_mmu_driver *driver;
+ struct drm_psb_private *dev_priv = dev->dev_private;
driver = kmalloc(sizeof(*driver), GFP_KERNEL);
if (!driver)
return NULL;
- driver->dev_priv = dev_priv;
+ driver->dev = dev;
driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults,
invalid_type);
if (!driver->default_pd)
@@ -515,17 +467,24 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
spin_lock_init(&driver->lock);
init_rwsem(&driver->sem);
down_write(&driver->sem);
- driver->register_map = registers;
atomic_set(&driver->needs_tlbflush, 1);
+ driver->msvdx_mmu_invaldc = msvdx_mmu_invaldc;
+
+ driver->bif_ctrl = PSB_RSGX32(PSB_CR_BIF_CTRL);
+ PSB_WSGX32(driver->bif_ctrl | _PSB_CB_CTRL_CLEAR_FAULT,
+ PSB_CR_BIF_CTRL);
+ PSB_WSGX32(driver->bif_ctrl & ~_PSB_CB_CTRL_CLEAR_FAULT,
+ PSB_CR_BIF_CTRL);
driver->has_clflush = 0;
+#if defined(CONFIG_X86)
if (boot_cpu_has(X86_FEATURE_CLFLUSH)) {
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.
+ * 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);
@@ -536,6 +495,7 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
driver->clflush_mask = driver->clflush_add - 1;
driver->clflush_mask = ~driver->clflush_mask;
}
+#endif
up_write(&driver->sem);
return driver;
@@ -545,9 +505,9 @@ out_err1:
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,
+#if defined(CONFIG_X86)
+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;
@@ -561,11 +521,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
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);
+ if (!pd->driver->has_clflush)
return;
- }
if (hw_tile_stride)
rows = num_pages / desired_tile_stride;
@@ -586,10 +543,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
if (!pt)
continue;
do {
- psb_clflush(&pt->v
- [psb_mmu_pt_index(addr)]);
- } while (addr +=
- clflush_add,
+ psb_clflush(&pt->v[psb_mmu_pt_index(addr)]);
+ } while (addr += clflush_add,
(addr & clflush_mask) < next);
psb_mmu_pt_unmap_unlock(pt);
@@ -598,6 +553,14 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
}
mb();
}
+#else
+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)
+{
+ drm_ttm_cache_flush();
+}
+#endif
void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
unsigned long address, uint32_t num_pages)
@@ -633,7 +596,7 @@ out:
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
- psb_mmu_flush(pd->driver, 0);
+ psb_mmu_flush(pd->driver);
return;
}
@@ -660,7 +623,7 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
add = desired_tile_stride << PAGE_SHIFT;
row_add = hw_tile_stride << PAGE_SHIFT;
- /* down_read(&pd->driver->sem); */
+ down_read(&pd->driver->sem);
/* Make sure we only need to flush this processor's cache */
@@ -688,10 +651,10 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
psb_mmu_flush_ptes(pd, f_address, num_pages,
desired_tile_stride, hw_tile_stride);
- /* up_read(&pd->driver->sem); */
+ up_read(&pd->driver->sem);
if (pd->hw_context != -1)
- psb_mmu_flush(pd->driver, 0);
+ psb_mmu_flush(pd->driver);
}
int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
@@ -704,7 +667,7 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
unsigned long end;
unsigned long next;
unsigned long f_address = address;
- int ret = 0;
+ int ret = -ENOMEM;
down_read(&pd->driver->sem);
@@ -726,6 +689,7 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
psb_mmu_pt_unmap_unlock(pt);
} while (addr = next, next != end);
+ ret = 0;
out:
if (pd->hw_context != -1)
@@ -734,15 +698,15 @@ out:
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
- psb_mmu_flush(pd->driver, 1);
+ psb_mmu_flush(pd->driver);
- return ret;
+ return 0;
}
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)
+ uint32_t desired_tile_stride, uint32_t hw_tile_stride,
+ int type)
{
struct psb_mmu_pt *pt;
uint32_t rows = 1;
@@ -754,7 +718,7 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
unsigned long add;
unsigned long row_add;
unsigned long f_address = address;
- int ret = 0;
+ int ret = -ENOMEM;
if (hw_tile_stride) {
if (num_pages % desired_tile_stride != 0)
@@ -777,14 +741,11 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
do {
next = psb_pd_addr_end(addr, end);
pt = psb_mmu_pt_alloc_map_lock(pd, addr);
- if (!pt) {
- ret = -ENOMEM;
+ if (!pt)
goto out;
- }
do {
- pte =
- psb_mmu_mask_pte(page_to_pfn(*pages++),
- type);
+ 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);
@@ -794,6 +755,8 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
address += row_add;
}
+
+ ret = 0;
out:
if (pd->hw_context != -1)
psb_mmu_flush_ptes(pd, f_address, num_pages,
@@ -802,7 +765,7 @@ out:
up_read(&pd->driver->sem);
if (pd->hw_context != -1)
- psb_mmu_flush(pd->driver, 1);
+ psb_mmu_flush(pd->driver);
return ret;
}
diff --git a/drivers/gpu/drm/gma500/mmu.h b/drivers/gpu/drm/gma500/mmu.h
new file mode 100644
index 000000000000..e89abec6209d
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mmu.h
@@ -0,0 +1,93 @@
+/**************************************************************************
+ * 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.
+ **************************************************************************/
+
+#ifndef __MMU_H
+#define __MMU_H
+
+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;
+ atomic_t *msvdx_mmu_invaldc;
+ struct psb_mmu_pd *default_pd;
+ uint32_t bif_ctrl;
+ int has_clflush;
+ int clflush_add;
+ unsigned long clflush_mask;
+
+ struct drm_device *dev;
+};
+
+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;
+};
+
+extern struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev,
+ int trap_pagefaults,
+ int invalid_type,
+ atomic_t *msvdx_mmu_invaldc);
+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 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);
+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);
+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);
+
+#endif
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c
index 8195e8592107..2de216c2374f 100644
--- a/drivers/gpu/drm/gma500/oaktrail_crtc.c
+++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c
@@ -599,7 +599,7 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv = dev->dev_private;
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
- struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
+ struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb);
int pipe = gma_crtc->pipe;
const struct psb_offset *map = &dev_priv->regmap[pipe];
unsigned long start, offset;
@@ -608,7 +608,7 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
int ret = 0;
/* no fb bound */
- if (!crtc->fb) {
+ if (!crtc->primary->fb) {
dev_dbg(dev->dev, "No FB bound\n");
return 0;
}
@@ -617,19 +617,19 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
return 0;
start = psbfb->gtt->offset;
- offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
+ offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8);
- REG_WRITE(map->stride, crtc->fb->pitches[0]);
+ REG_WRITE(map->stride, crtc->primary->fb->pitches[0]);
dspcntr = REG_READ(map->cntr);
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
break;
case 16:
- if (crtc->fb->depth == 15)
+ if (crtc->primary->fb->depth == 15)
dspcntr |= DISPPLANE_15_16BPP;
else
dspcntr |= DISPPLANE_16BPP;
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
index 38153143ed8c..cf018ddcc5a6 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
@@ -523,13 +523,6 @@ static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
-static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- return true;
-}
-
static enum drm_connector_status
oaktrail_hdmi_detect(struct drm_connector *connector, bool force)
{
@@ -608,7 +601,7 @@ static void oaktrail_hdmi_destroy(struct drm_connector *connector)
static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
.dpms = oaktrail_hdmi_dpms,
- .mode_fixup = oaktrail_hdmi_mode_fixup,
+ .mode_fixup = gma_encoder_mode_fixup,
.prepare = gma_encoder_prepare,
.mode_set = oaktrail_hdmi_mode_set,
.commit = gma_encoder_commit,
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index 5e0697862736..9b099468a5db 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -359,6 +359,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
* if closed, act like it's not there for now
*/
+ mutex_lock(&dev->mode_config.mutex);
i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
if (i2c_adap == NULL)
dev_err(dev->dev, "No ddc adapter available!\n");
@@ -401,10 +402,14 @@ void oaktrail_lvds_init(struct drm_device *dev,
}
out:
+ mutex_unlock(&dev->mode_config.mutex);
+
drm_sysfs_connector_add(connector);
return;
failed_find:
+ mutex_unlock(&dev->mode_config.mutex);
+
dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
if (gma_encoder->ddc_bus)
psb_intel_i2c_destroy(gma_encoder->ddc_bus);
diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c
index 13ec6283bf59..ab696ca7eeec 100644
--- a/drivers/gpu/drm/gma500/opregion.c
+++ b/drivers/gpu/drm/gma500/opregion.c
@@ -173,10 +173,13 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
return 0;
}
-void psb_intel_opregion_asle_intr(struct drm_device *dev)
+static void psb_intel_opregion_asle_work(struct work_struct *work)
{
- struct drm_psb_private *dev_priv = dev->dev_private;
- struct opregion_asle *asle = dev_priv->opregion.asle;
+ struct psb_intel_opregion *opregion =
+ container_of(work, struct psb_intel_opregion, asle_work);
+ struct drm_psb_private *dev_priv =
+ container_of(opregion, struct drm_psb_private, opregion);
+ struct opregion_asle *asle = opregion->asle;
u32 asle_stat = 0;
u32 asle_req;
@@ -190,9 +193,18 @@ void psb_intel_opregion_asle_intr(struct drm_device *dev)
}
if (asle_req & ASLE_SET_BACKLIGHT)
- asle_stat |= asle_set_backlight(dev, asle->bclp);
+ asle_stat |= asle_set_backlight(dev_priv->dev, asle->bclp);
asle->aslc = asle_stat;
+
+}
+
+void psb_intel_opregion_asle_intr(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->opregion.asle)
+ schedule_work(&dev_priv->opregion.asle_work);
}
#define ASLE_ALS_EN (1<<0)
@@ -282,6 +294,8 @@ void psb_intel_opregion_fini(struct drm_device *dev)
unregister_acpi_notifier(&psb_intel_opregion_notifier);
}
+ cancel_work_sync(&opregion->asle_work);
+
/* just clear all opregion memory pointers now */
iounmap(opregion->header);
opregion->header = NULL;
@@ -304,6 +318,9 @@ int psb_intel_opregion_setup(struct drm_device *dev)
DRM_DEBUG_DRIVER("ACPI Opregion not supported\n");
return -ENOTSUPP;
}
+
+ INIT_WORK(&opregion->asle_work, psb_intel_opregion_asle_work);
+
DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy);
base = acpi_os_ioremap(opregion_phy, 8*1024);
if (!base)
diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c
index 23fb33f1471b..07df7d4eea72 100644
--- a/drivers/gpu/drm/gma500/psb_device.c
+++ b/drivers/gpu/drm/gma500/psb_device.c
@@ -26,6 +26,7 @@
#include "psb_intel_reg.h"
#include "intel_bios.h"
#include "psb_device.h"
+#include "gma_device.h"
static int psb_output_init(struct drm_device *dev)
{
@@ -257,45 +258,6 @@ 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;
- break;
- default:
- dev_priv->core_freq = 0;
- }
-}
-
/* Poulsbo */
static const struct psb_offset psb_regmap[2] = {
{
@@ -352,7 +314,7 @@ static int psb_chip_setup(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
dev_priv->regmap = psb_regmap;
- psb_get_core_freq(dev);
+ gma_get_core_freq(dev);
gma_intel_setup_gmbus(dev);
psb_intel_opregion_init(dev);
psb_intel_init_bios(dev);
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 1199180667c9..b686e56646eb 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -21,7 +21,6 @@
#include <drm/drmP.h>
#include <drm/drm.h>
-#include <drm/gma_drm.h>
#include "psb_drv.h"
#include "framebuffer.h"
#include "psb_reg.h"
@@ -37,56 +36,65 @@
#include <acpi/video.h>
#include <linux/module.h>
-static int drm_psb_trap_pagefaults;
-
-static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
-
-MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults");
-module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600);
-
+static struct drm_driver driver;
+static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+/*
+ * The table below contains a mapping of the PCI vendor ID and the PCI Device ID
+ * to the different groups of PowerVR 5-series chip designs
+ *
+ * 0x8086 = Intel Corporation
+ *
+ * PowerVR SGX535 - Poulsbo - Intel GMA 500, Intel Atom Z5xx
+ * PowerVR SGX535 - Moorestown - Intel GMA 600
+ * PowerVR SGX535 - Oaktrail - Intel GMA 600, Intel Atom Z6xx, E6xx
+ * PowerVR SGX540 - Medfield - Intel Atom Z2460
+ * PowerVR SGX544MP2 - Medfield -
+ * PowerVR SGX545 - Cedartrail - Intel GMA 3600, Intel Atom D2500, N2600
+ * PowerVR SGX545 - Cedartrail - Intel GMA 3650, Intel Atom D2550, D2700,
+ * N2800
+ */
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_GMA600)
- { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
- /* Atom E620 */
- { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
+ { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
+ { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops },
#endif
#if defined(CONFIG_DRM_MEDFIELD)
- {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},
+ { 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_GMA3600)
- { 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},
- { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
- { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
+ { 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 },
+ { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
+ { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops },
#endif
{ 0, }
};
@@ -95,59 +103,10 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
/*
* Standard IOCTLs.
*/
-
-#define DRM_IOCTL_GMA_ADB \
- DRM_IOWR(DRM_GMA_ADB + DRM_COMMAND_BASE, uint32_t)
-#define DRM_IOCTL_GMA_MODE_OPERATION \
- DRM_IOWR(DRM_GMA_MODE_OPERATION + DRM_COMMAND_BASE, \
- struct drm_psb_mode_operation_arg)
-#define DRM_IOCTL_GMA_STOLEN_MEMORY \
- DRM_IOWR(DRM_GMA_STOLEN_MEMORY + DRM_COMMAND_BASE, \
- struct drm_psb_stolen_memory_arg)
-#define DRM_IOCTL_GMA_GAMMA \
- DRM_IOWR(DRM_GMA_GAMMA + DRM_COMMAND_BASE, \
- struct drm_psb_dpst_lut_arg)
-#define DRM_IOCTL_GMA_DPST_BL \
- DRM_IOWR(DRM_GMA_DPST_BL + DRM_COMMAND_BASE, \
- uint32_t)
-#define DRM_IOCTL_GMA_GET_PIPE_FROM_CRTC_ID \
- DRM_IOWR(DRM_GMA_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \
- struct drm_psb_get_pipe_from_crtc_id_arg)
-#define DRM_IOCTL_GMA_GEM_CREATE \
- DRM_IOWR(DRM_GMA_GEM_CREATE + DRM_COMMAND_BASE, \
- struct drm_psb_gem_create)
-#define DRM_IOCTL_GMA_GEM_MMAP \
- DRM_IOWR(DRM_GMA_GEM_MMAP + DRM_COMMAND_BASE, \
- struct drm_psb_gem_mmap)
-
-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_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);
-
static const struct drm_ioctl_desc psb_ioctls[] = {
- DRM_IOCTL_DEF_DRV(GMA_ADB, psb_adb_ioctl, DRM_AUTH),
- DRM_IOCTL_DEF_DRV(GMA_MODE_OPERATION, psb_mode_operation_ioctl,
- DRM_AUTH),
- DRM_IOCTL_DEF_DRV(GMA_STOLEN_MEMORY, psb_stolen_memory_ioctl,
- DRM_AUTH),
- DRM_IOCTL_DEF_DRV(GMA_GAMMA, psb_gamma_ioctl, DRM_AUTH),
- DRM_IOCTL_DEF_DRV(GMA_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH),
- DRM_IOCTL_DEF_DRV(GMA_GET_PIPE_FROM_CRTC_ID,
- psb_intel_get_pipe_from_crtc_id, 0),
- DRM_IOCTL_DEF_DRV(GMA_GEM_CREATE, psb_gem_create_ioctl,
- DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(GMA_GEM_MMAP, psb_gem_mmap_ioctl,
- DRM_UNLOCKED | DRM_AUTH),
};
-static void psb_lastclose(struct drm_device *dev)
+static void psb_driver_lastclose(struct drm_device *dev)
{
int ret;
struct drm_psb_private *dev_priv = dev->dev_private;
@@ -169,19 +128,14 @@ static int psb_do_init(struct drm_device *dev)
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;
+ return -EINVAL;
}
-
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;
+ 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;
@@ -192,23 +146,26 @@ static int psb_do_init(struct drm_device *dev)
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);
+
+ /* Do not bypass any MMU access, let them pagefault instead */
+ PSB_WSGX32((PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_MMU_ER_MASK),
+ PSB_CR_BIF_CTRL);
+ PSB_RSGX32(PSB_CR_BIF_CTRL);
+
psb_spank(dev_priv);
/* mmu_gatt ?? */
PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE);
+ PSB_RSGX32(PSB_CR_BIF_TWOD_REQ_BASE); /* Post */
+
return 0;
-out_err:
- return ret;
}
static int psb_driver_unload(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
- /* Kill vblank etc here */
-
+ /* TODO: Kill vblank etc here */
if (dev_priv) {
if (dev_priv->backlight_device)
@@ -268,8 +225,7 @@ static int psb_driver_unload(struct drm_device *dev)
return 0;
}
-
-static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
+static int psb_driver_load(struct drm_device *dev, unsigned long flags)
{
struct drm_psb_private *dev_priv;
unsigned long resource_start, resource_len;
@@ -277,15 +233,19 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
int ret = -ENOMEM;
struct drm_connector *connector;
struct gma_encoder *gma_encoder;
+ struct psb_gtt *pg;
+ /* allocating and initializing driver private data */
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
if (dev_priv == NULL)
return -ENOMEM;
- dev_priv->ops = (struct psb_ops *)chipset;
+ dev_priv->ops = (struct psb_ops *)flags;
dev_priv->dev = dev;
dev->dev_private = (void *) dev_priv;
+ pg = &dev_priv->gtt;
+
pci_set_master(dev->pdev);
dev_priv->num_pipe = dev_priv->ops->pipes;
@@ -347,9 +307,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
if (ret)
goto out_err;
- dev_priv->mmu = psb_mmu_driver_init((void *)0,
- drm_psb_trap_pagefaults, 0,
- dev_priv);
+ dev_priv->mmu = psb_mmu_driver_init(dev, 1, 0, 0);
if (!dev_priv->mmu)
goto out_err;
@@ -357,18 +315,27 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
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;
+ /* Add stolen memory to SGX MMU */
+ down_read(&pg->sem);
+ ret = psb_mmu_insert_pfn_sequence(psb_mmu_get_default_pd(dev_priv->mmu),
+ dev_priv->stolen_base >> PAGE_SHIFT,
+ pg->gatt_start,
+ pg->stolen_size >> PAGE_SHIFT, 0);
+ up_read(&pg->sem);
+
+ psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0);
+ psb_mmu_set_pd_context(dev_priv->pf_pd, 1);
+
PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE);
PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE);
acpi_video_register();
+ /* Setup vertical blanking handling */
ret = drm_vblank_init(dev, dev_priv->num_pipe);
if (ret)
goto out_err;
@@ -390,9 +357,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
drm_irq_install(dev);
dev->vblank_disable_allowed = true;
-
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
-
dev->driver->get_vblank_counter = psb_get_vblank_counter;
psb_modeset_init(dev);
@@ -416,11 +381,11 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
return ret;
psb_intel_opregion_enable_asle(dev);
#if 0
- /*enable runtime pm at last*/
+ /* Enable runtime pm at last */
pm_runtime_enable(&dev->pdev->dev);
pm_runtime_set_active(&dev->pdev->dev);
#endif
- /*Intel drm driver load is done, continue doing pvr load*/
+ /* Intel drm driver load is done, continue doing pvr load */
return 0;
out_err:
psb_driver_unload(dev);
@@ -442,161 +407,6 @@ static inline void get_brightness(struct backlight_device *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;
-}
-
-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 gma_crtc *gma_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 -ENOENT;
- }
-
- connector = obj_to_connector(obj);
- crtc = connector->encoder->crtc;
- gma_crtc = to_gma_crtc(crtc);
-
- for (i = 0; i < 256; i++)
- gma_crtc->lut_adj[i] = lut_arg->lut[i];
-
- gma_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_connector_helper_funcs *connector_funcs;
- int ret = 0;
- int resp = MODE_OK;
-
- arg = (struct drm_psb_mode_operation_arg *)data;
- obj_id = arg->obj_id;
- op = arg->operation;
-
- switch (op) {
- case PSB_MODE_OPERATION_MODE_VALID:
- umode = &arg->mode;
-
- drm_modeset_lock_all(dev);
-
- obj = drm_mode_object_find(dev, obj_id,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!obj) {
- ret = -ENOENT;
- 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 = resp;
- }
-
- /*do some clean up work*/
- if (mode)
- drm_mode_destroy(dev, mode);
-mode_op_out:
- drm_modeset_unlock_all(dev);
- 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;
-}
-
-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)
{
@@ -614,15 +424,21 @@ static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd,
/* FIXME: do we need to wrap the other side of this */
}
-
-/* When a client dies:
+/*
+ * When a client dies:
* - Check for and clean up flipped page state
*/
static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv)
{
}
-static void psb_remove(struct pci_dev *pdev)
+static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ return drm_get_pci_dev(pdev, ent, &driver);
+}
+
+
+static void psb_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_put_dev(dev);
@@ -657,11 +473,12 @@ static const struct file_operations psb_gem_fops = {
static struct drm_driver driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \
- DRIVER_MODESET | DRIVER_GEM ,
+ DRIVER_MODESET | DRIVER_GEM,
.load = psb_driver_load,
.unload = psb_driver_unload,
+ .lastclose = psb_driver_lastclose,
+ .preclose = psb_driver_preclose,
- .ioctls = psb_ioctls,
.num_ioctls = DRM_ARRAY_SIZE(psb_ioctls),
.device_is_agp = psb_driver_device_is_agp,
.irq_preinstall = psb_irq_preinstall,
@@ -671,40 +488,31 @@ static struct drm_driver driver = {
.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,
.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 = drm_gem_dumb_destroy,
+ .ioctls = psb_ioctls,
.fops = &psb_gem_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
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = 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,
- }
+ .probe = psb_pci_probe,
+ .remove = psb_pci_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);
@@ -718,6 +526,6 @@ static void __exit psb_exit(void)
late_initcall(psb_init);
module_exit(psb_exit);
-MODULE_AUTHOR("Alan Cox <alan@linux.intel.com> and others");
+MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
index 5ad6a03e477e..55ebe2bd88dd 100644
--- a/drivers/gpu/drm/gma500/psb_drv.h
+++ b/drivers/gpu/drm/gma500/psb_drv.h
@@ -33,6 +33,18 @@
#include "power.h"
#include "opregion.h"
#include "oaktrail.h"
+#include "mmu.h"
+
+#define DRIVER_AUTHOR "Alan Cox <alan@linux.intel.com> and others"
+#define DRIVER_LICENSE "GPL"
+
+#define DRIVER_NAME "gma500"
+#define DRIVER_DESC "DRM driver for the Intel GMA500, GMA600, GMA3600, GMA3650"
+#define DRIVER_DATE "20140314"
+
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
/* Append new drm mode definition here, align with libdrm definition */
#define DRM_MODE_SCALE_NO_SCALE 2
@@ -49,21 +61,7 @@ enum {
#define IS_MFLD(dev) (((dev)->pdev->device & 0xfff8) == 0x0130)
#define IS_CDV(dev) (((dev)->pdev->device & 0xfff0) == 0x0be0)
-/*
- * 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
- */
+/* Hardware offsets */
#define PSB_VDC_OFFSET 0x00000000
#define PSB_VDC_SIZE 0x000080000
#define MRST_MMIO_SIZE 0x0000C0000
@@ -71,16 +69,14 @@ enum {
#define PSB_SGX_SIZE 0x8000
#define PSB_SGX_OFFSET 0x00040000
#define MRST_SGX_OFFSET 0x00080000
-/*
- * PCI resource identifiers
- */
+
+/* PCI resource identifiers */
#define PSB_MMIO_RESOURCE 0
#define PSB_AUX_RESOURCE 0
#define PSB_GATT_RESOURCE 2
#define PSB_GTT_RESOURCE 3
-/*
- * PCI configuration
- */
+
+/* PCI configuration */
#define PSB_GMCH_CTRL 0x52
#define PSB_BSM 0x5C
#define _PSB_GMCH_ENABLED 0x4
@@ -88,37 +84,29 @@ enum {
#define _PSB_PGETBL_ENABLED 0x00000001
#define PSB_SGX_2D_SLAVE_PORT 0x4000
-/* To get rid of */
+/* TODO: 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)
- */
+/* SGX side MMU definitions (these can probably go) */
-/*
- * Flags for external memory type field.
- */
+/* 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
- */
+
+/* PTE's and PDE's */
#define PSB_PDE_MASK 0x003FFFFF
#define PSB_PDE_SHIFT 22
#define PSB_PTE_SHIFT 12
-/*
- * Cache control
- */
+
+/* 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
- */
+/* VDC registers and bits */
#define PSB_MSVDX_CLOCKGATING 0x2064
#define PSB_TOPAZ_CLOCKGATING 0x2068
#define PSB_HWSTAM 0x2098
@@ -265,6 +253,7 @@ struct psb_intel_opregion {
struct opregion_asle *asle;
void *vbt;
u32 __iomem *lid_state;
+ struct work_struct asle_work;
};
struct sdvo_device_mapping {
@@ -283,10 +272,7 @@ struct intel_gmbus {
u32 reg0;
};
-/*
- * Register offset maps
- */
-
+/* Register offset maps */
struct psb_offset {
u32 fp0;
u32 fp1;
@@ -320,9 +306,7 @@ struct psb_offset {
* update the register cache instead.
*/
-/*
- * Common status for pipes.
- */
+/* Common status for pipes */
struct psb_pipe {
u32 fp0;
u32 fp1;
@@ -482,35 +466,24 @@ struct drm_psb_private {
struct psb_mmu_driver *mmu;
struct psb_mmu_pd *pf_pd;
- /*
- * Register base
- */
-
+ /* Register base */
uint8_t __iomem *sgx_reg;
uint8_t __iomem *vdc_reg;
uint8_t __iomem *aux_reg; /* Auxillary vdc pipe regs */
uint32_t gatt_free_offset;
- /*
- * Fencing / irq.
- */
-
+ /* Fencing / irq */
uint32_t vdc_irq_mask;
uint32_t pipestat[PSB_NUM_PIPE];
spinlock_t irqmask_lock;
- /*
- * Power
- */
-
+ /* Power */
bool suspended;
bool display_power;
int display_count;
- /*
- * Modesetting
- */
+ /* Modesetting */
struct psb_intel_mode_device mode_dev;
bool modeset; /* true if we have done the mode_device setup */
@@ -518,15 +491,10 @@ struct drm_psb_private {
struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE];
uint32_t num_pipe;
- /*
- * OSPM info (Power management base) (can go ?)
- */
+ /* OSPM info (Power management base) (TODO: can go ?) */
uint32_t ospm_base;
- /*
- * Sizes info
- */
-
+ /* Sizes info */
u32 fuse_reg_value;
u32 video_device_fuse;
@@ -546,9 +514,7 @@ struct drm_psb_private {
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
- /*
- * LVDS info
- */
+ /* LVDS info */
int backlight_duty_cycle; /* restore backlight to this value */
bool panel_wants_dither;
struct drm_display_mode *panel_fixed_mode;
@@ -582,34 +548,23 @@ struct drm_psb_private {
/* Oaktrail HDMI state */
struct oaktrail_hdmi_dev *hdmi_priv;
- /*
- * Register state
- */
-
+ /* Register state */
struct psb_save_area regs;
/* MSI reg save */
uint32_t msi_addr;
uint32_t msi_data;
- /*
- * Hotplug handling
- */
-
+ /* Hotplug handling */
struct work_struct hotplug_work;
- /*
- * LID-Switch
- */
+ /* LID-Switch */
spinlock_t lid_lock;
struct timer_list lid_timer;
struct psb_intel_opregion opregion;
u32 lid_last_state;
- /*
- * Watchdog
- */
-
+ /* Watchdog */
uint32_t apm_reg;
uint16_t apm_base;
@@ -629,9 +584,7 @@ struct drm_psb_private {
/* 2D acceleration */
spinlock_t lock_2d;
- /*
- * Panel brightness
- */
+ /* Panel brightness */
int brightness;
int brightness_adjusted;
@@ -664,10 +617,7 @@ struct drm_psb_private {
};
-/*
- * Operations for each board type
- */
-
+/* Operations for each board type */
struct psb_ops {
const char *name;
unsigned int accel_2d:1;
@@ -713,8 +663,6 @@ struct psb_ops {
-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);
@@ -723,52 +671,7 @@ 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
- */
-
+/* psb_irq.c */
extern irqreturn_t psb_irq_handler(int irq, void *arg);
extern int psb_irq_enable_dpst(struct drm_device *dev);
extern int psb_irq_disable_dpst(struct drm_device *dev);
@@ -791,24 +694,17 @@ 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);
-/*
- * framebuffer.c
- */
+/* 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
- */
+/* 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);
-/*
- * psb_reset.c
- */
-
+/* 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);
@@ -867,9 +763,7 @@ extern const struct psb_ops mdfld_chip_ops;
/* cdv_device.c */
extern const struct psb_ops cdv_chip_ops;
-/*
- * Debug print bits setting
- */
+/* Debug print bits setting */
#define PSB_D_GENERAL (1 << 0)
#define PSB_D_INIT (1 << 1)
#define PSB_D_IRQ (1 << 2)
@@ -885,10 +779,7 @@ extern const struct psb_ops cdv_chip_ops;
extern int drm_idle_check_interval;
-/*
- * Utilities
- */
-
+/* Utilities */
static inline u32 MRST_MSG_READ32(uint port, uint offset)
{
int mcr = (0xD0<<24) | (port << 16) | (offset << 8);
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index c8841ac6c8f1..87b50ba64ed4 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -120,7 +120,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc,
const struct gma_limit_t *limit;
/* No scan out no play */
- if (crtc->fb == NULL) {
+ if (crtc->primary->fb == NULL) {
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
return 0;
}
@@ -469,7 +469,8 @@ static void psb_intel_cursor_init(struct drm_device *dev,
/* Allocate 4 pages of stolen mem for a hardware cursor. That
* is enough for the 64 x 64 ARGB cursors we support.
*/
- cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1);
+ cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1,
+ PAGE_SIZE);
if (!cursor_gt) {
gma_crtc->cursor_gt = NULL;
goto out;
@@ -554,33 +555,6 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe,
gma_crtc->active = true;
}
-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 gma_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 -ENOENT;
- }
-
- crtc = to_gma_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;
diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h
index dc2c8eb030fa..336bd3aa1a06 100644
--- a/drivers/gpu/drm/gma500/psb_intel_drv.h
+++ b/drivers/gpu/drm/gma500/psb_intel_drv.h
@@ -238,8 +238,6 @@ static inline struct gma_encoder *gma_attached_encoder(
extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc);
-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,
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index 32342f6990d9..d7778d0472c1 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -614,7 +614,7 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
&crtc->saved_mode,
encoder->crtc->x,
encoder->crtc->y,
- encoder->crtc->fb))
+ encoder->crtc->primary->fb))
goto set_prop_error;
}
} else if (!strcmp(property->name, "backlight")) {
@@ -777,6 +777,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
+ mutex_lock(&dev->mode_config.mutex);
psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter);
list_for_each_entry(scan, &connector->probed_modes, head) {
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
@@ -827,10 +828,12 @@ void psb_intel_lvds_init(struct drm_device *dev,
* actually having one.
*/
out:
+ mutex_unlock(&dev->mode_config.mutex);
drm_sysfs_connector_add(connector);
return;
failed_find:
+ mutex_unlock(&dev->mode_config.mutex);
if (lvds_priv->ddc_bus)
psb_intel_i2c_destroy(lvds_priv->ddc_bus);
failed_ddc:
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index 07d3a9e6d79b..deeb0829b129 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -406,18 +406,18 @@ static void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo, u8
DRM_DEBUG_KMS("%s: W: %02X ",
SDVO_NAME(psb_intel_sdvo), cmd);
for (i = 0; i < args_len; i++)
- DRM_LOG_KMS("%02X ", ((u8 *)args)[i]);
+ DRM_DEBUG_KMS("%02X ", ((u8 *)args)[i]);
for (; i < 8; i++)
- DRM_LOG_KMS(" ");
+ DRM_DEBUG_KMS(" ");
for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) {
if (cmd == sdvo_cmd_names[i].cmd) {
- DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name);
+ DRM_DEBUG_KMS("(%s)", sdvo_cmd_names[i].name);
break;
}
}
if (i == ARRAY_SIZE(sdvo_cmd_names))
- DRM_LOG_KMS("(%02X)", cmd);
- DRM_LOG_KMS("\n");
+ DRM_DEBUG_KMS("(%02X)", cmd);
+ DRM_DEBUG_KMS("\n");
}
static const char *cmd_status_names[] = {
@@ -512,9 +512,9 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
}
if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
- DRM_LOG_KMS("(%s)", cmd_status_names[status]);
+ DRM_DEBUG_KMS("(%s)", cmd_status_names[status]);
else
- DRM_LOG_KMS("(??? %d)", status);
+ DRM_DEBUG_KMS("(??? %d)", status);
if (status != SDVO_CMD_STATUS_SUCCESS)
goto log_fail;
@@ -525,13 +525,13 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
SDVO_I2C_RETURN_0 + i,
&((u8 *)response)[i]))
goto log_fail;
- DRM_LOG_KMS(" %02X", ((u8 *)response)[i]);
+ DRM_DEBUG_KMS(" %02X", ((u8 *)response)[i]);
}
- DRM_LOG_KMS("\n");
+ DRM_DEBUG_KMS("\n");
return true;
log_fail:
- DRM_LOG_KMS("... failed\n");
+ DRM_DEBUG_KMS("... failed\n");
return false;
}
@@ -1844,7 +1844,7 @@ done:
if (psb_intel_sdvo->base.base.crtc) {
struct drm_crtc *crtc = psb_intel_sdvo->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
- crtc->y, crtc->fb);
+ crtc->y, crtc->primary->fb);
}
return 0;
diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c
index f883f9e4c524..624eb36511c5 100644
--- a/drivers/gpu/drm/gma500/psb_irq.c
+++ b/drivers/gpu/drm/gma500/psb_irq.c
@@ -200,11 +200,64 @@ static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat)
mid_pipe_event_handler(dev, 1);
}
+/*
+ * SGX interrupt handler
+ */
+static void psb_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 val, addr;
+ int error = false;
+
+ if (stat_1 & _PSB_CE_TWOD_COMPLETE)
+ val = PSB_RSGX32(PSB_CR_2D_BLIT_STATUS);
+
+ if (stat_2 & _PSB_CE2_BIF_REQUESTER_FAULT) {
+ val = PSB_RSGX32(PSB_CR_BIF_INT_STAT);
+ addr = PSB_RSGX32(PSB_CR_BIF_FAULT);
+ if (val) {
+ if (val & _PSB_CBI_STAT_PF_N_RW)
+ DRM_ERROR("SGX MMU page fault:");
+ else
+ DRM_ERROR("SGX MMU read / write protection fault:");
+
+ if (val & _PSB_CBI_STAT_FAULT_CACHE)
+ DRM_ERROR("\tCache requestor");
+ if (val & _PSB_CBI_STAT_FAULT_TA)
+ DRM_ERROR("\tTA requestor");
+ if (val & _PSB_CBI_STAT_FAULT_VDM)
+ DRM_ERROR("\tVDM requestor");
+ if (val & _PSB_CBI_STAT_FAULT_2D)
+ DRM_ERROR("\t2D requestor");
+ if (val & _PSB_CBI_STAT_FAULT_PBE)
+ DRM_ERROR("\tPBE requestor");
+ if (val & _PSB_CBI_STAT_FAULT_TSP)
+ DRM_ERROR("\tTSP requestor");
+ if (val & _PSB_CBI_STAT_FAULT_ISP)
+ DRM_ERROR("\tISP requestor");
+ if (val & _PSB_CBI_STAT_FAULT_USSEPDS)
+ DRM_ERROR("\tUSSEPDS requestor");
+ if (val & _PSB_CBI_STAT_FAULT_HOST)
+ DRM_ERROR("\tHost requestor");
+
+ DRM_ERROR("\tMMU failing address is 0x%08x.\n",
+ (unsigned int)addr);
+ error = true;
+ }
+ }
+
+ /* Clear bits */
+ PSB_WSGX32(stat_1, PSB_CR_EVENT_HOST_CLEAR);
+ PSB_WSGX32(stat_2, PSB_CR_EVENT_HOST_CLEAR2);
+ PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR2);
+}
+
irqreturn_t psb_irq_handler(int irq, void *arg)
{
struct drm_device *dev = arg;
struct drm_psb_private *dev_priv = dev->dev_private;
uint32_t vdc_stat, dsp_int = 0, sgx_int = 0, hotplug_int = 0;
+ u32 sgx_stat_1, sgx_stat_2;
int handled = 0;
spin_lock(&dev_priv->irqmask_lock);
@@ -233,14 +286,9 @@ irqreturn_t psb_irq_handler(int irq, void *arg)
}
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 ! */
+ sgx_stat_1 = PSB_RSGX32(PSB_CR_EVENT_STATUS);
+ sgx_stat_2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2);
+ psb_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2);
handled = 1;
}
@@ -269,8 +317,13 @@ void psb_irq_preinstall(struct drm_device *dev)
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
- if (gma_power_is_on(dev))
+ if (gma_power_is_on(dev)) {
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
+ PSB_WVDC32(0x00000000, PSB_INT_MASK_R);
+ PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R);
+ PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE);
+ PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE);
+ }
if (dev->vblank[0].enabled)
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
if (dev->vblank[1].enabled)
@@ -286,7 +339,7 @@ void psb_irq_preinstall(struct drm_device *dev)
/* Revisit this area - want per device masks ? */
if (dev_priv->ops->hotplug)
dev_priv->vdc_irq_mask |= _PSB_IRQ_DISP_HOTSYNC;
- dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE;
+ dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE | _PSB_IRQ_SGX_FLAG;
/* This register is safe even if display island is off */
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
@@ -295,12 +348,16 @@ void psb_irq_preinstall(struct drm_device *dev)
int psb_irq_postinstall(struct drm_device *dev)
{
- struct drm_psb_private *dev_priv =
- (struct drm_psb_private *) dev->dev_private;
+ struct drm_psb_private *dev_priv = dev->dev_private;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+ /* Enable 2D and MMU fault interrupts */
+ PSB_WSGX32(_PSB_CE2_BIF_REQUESTER_FAULT, PSB_CR_EVENT_HOST_ENABLE2);
+ PSB_WSGX32(_PSB_CE_TWOD_COMPLETE, PSB_CR_EVENT_HOST_ENABLE);
+ PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); /* Post */
+
/* 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);
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index faa77f543a07..48af5cac1902 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -19,6 +19,8 @@
#include <linux/hdmi.h>
#include <linux/module.h>
+#include <linux/irq.h>
+#include <sound/asoundef.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@@ -30,6 +32,7 @@
struct tda998x_priv {
struct i2c_client *cec;
+ struct i2c_client *hdmi;
uint16_t rev;
uint8_t current_page;
int dpms;
@@ -38,6 +41,10 @@ struct tda998x_priv {
u8 vip_cntrl_1;
u8 vip_cntrl_2;
struct tda998x_encoder_params params;
+
+ wait_queue_head_t wq_edid;
+ volatile int wq_edid_wait;
+ struct drm_encoder *encoder;
};
#define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -120,6 +127,8 @@ struct tda998x_priv {
# define VIP_CNTRL_5_CKCASE (1 << 0)
# define VIP_CNTRL_5_SP_CNT(x) (((x) & 3) << 1)
#define REG_MUX_AP REG(0x00, 0x26) /* read/write */
+# define MUX_AP_SELECT_I2S 0x64
+# define MUX_AP_SELECT_SPDIF 0x40
#define REG_MUX_VP_VIP_OUT REG(0x00, 0x27) /* read/write */
#define REG_MAT_CONTRL REG(0x00, 0x80) /* write */
# define MAT_CONTRL_MAT_SC(x) (((x) & 3) << 0)
@@ -197,10 +206,11 @@ struct tda998x_priv {
#define REG_I2S_FORMAT REG(0x00, 0xfc) /* read/write */
# define I2S_FORMAT(x) (((x) & 3) << 0)
#define REG_AIP_CLKSEL REG(0x00, 0xfd) /* write */
-# define AIP_CLKSEL_FS(x) (((x) & 3) << 0)
-# define AIP_CLKSEL_CLK_POL(x) (((x) & 1) << 2)
-# define AIP_CLKSEL_AIP(x) (((x) & 7) << 3)
-
+# define AIP_CLKSEL_AIP_SPDIF (0 << 3)
+# define AIP_CLKSEL_AIP_I2S (1 << 3)
+# define AIP_CLKSEL_FS_ACLK (0 << 0)
+# define AIP_CLKSEL_FS_MCLK (1 << 0)
+# define AIP_CLKSEL_FS_FS64SPDIF (2 << 0)
/* Page 02h: PLL settings */
#define REG_PLL_SERIAL_1 REG(0x02, 0x00) /* read/write */
@@ -304,11 +314,16 @@ struct tda998x_priv {
/* CEC registers: (not paged)
*/
+#define REG_CEC_INTSTATUS 0xee /* read */
+# define CEC_INTSTATUS_CEC (1 << 0)
+# define CEC_INTSTATUS_HDMI (1 << 1)
#define REG_CEC_FRO_IM_CLK_CTRL 0xfb /* read/write */
# define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7)
# define CEC_FRO_IM_CLK_CTRL_ENA_OTP (1 << 6)
# define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL (1 << 1)
# define CEC_FRO_IM_CLK_CTRL_FRO_DIV (1 << 0)
+#define REG_CEC_RXSHPDINTENA 0xfc /* read/write */
+#define REG_CEC_RXSHPDINT 0xfd /* read */
#define REG_CEC_RXSHPDLEV 0xfe /* read */
# define CEC_RXSHPDLEV_RXSENS (1 << 0)
# define CEC_RXSHPDLEV_HPD (1 << 1)
@@ -328,21 +343,21 @@ struct tda998x_priv {
#define TDA19988 0x0301
static void
-cec_write(struct drm_encoder *encoder, uint16_t addr, uint8_t val)
+cec_write(struct tda998x_priv *priv, uint16_t addr, uint8_t val)
{
- struct i2c_client *client = to_tda998x_priv(encoder)->cec;
+ struct i2c_client *client = priv->cec;
uint8_t buf[] = {addr, val};
int ret;
- ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ ret = i2c_master_send(client, buf, sizeof(buf));
if (ret < 0)
dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
}
static uint8_t
-cec_read(struct drm_encoder *encoder, uint8_t addr)
+cec_read(struct tda998x_priv *priv, uint8_t addr)
{
- struct i2c_client *client = to_tda998x_priv(encoder)->cec;
+ struct i2c_client *client = priv->cec;
uint8_t val;
int ret;
@@ -361,32 +376,36 @@ fail:
return 0;
}
-static void
-set_page(struct drm_encoder *encoder, uint16_t reg)
+static int
+set_page(struct tda998x_priv *priv, uint16_t reg)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
if (REG2PAGE(reg) != priv->current_page) {
- struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct i2c_client *client = priv->hdmi;
uint8_t buf[] = {
REG_CURPAGE, REG2PAGE(reg)
};
int ret = i2c_master_send(client, buf, sizeof(buf));
- if (ret < 0)
- dev_err(&client->dev, "Error %d writing to REG_CURPAGE\n", ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "setpage %04x err %d\n",
+ reg, ret);
+ return ret;
+ }
priv->current_page = REG2PAGE(reg);
}
+ return 0;
}
static int
-reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt)
+reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt)
{
- struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct i2c_client *client = priv->hdmi;
uint8_t addr = REG2ADDR(reg);
int ret;
- set_page(encoder, reg);
+ ret = set_page(priv, reg);
+ if (ret < 0)
+ return ret;
ret = i2c_master_send(client, &addr, sizeof(addr));
if (ret < 0)
@@ -404,100 +423,147 @@ fail:
}
static void
-reg_write_range(struct drm_encoder *encoder, uint16_t reg, uint8_t *p, int cnt)
+reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt)
{
- struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct i2c_client *client = priv->hdmi;
uint8_t buf[cnt+1];
int ret;
buf[0] = REG2ADDR(reg);
memcpy(&buf[1], p, cnt);
- set_page(encoder, reg);
+ ret = set_page(priv, reg);
+ if (ret < 0)
+ return;
ret = i2c_master_send(client, buf, cnt + 1);
if (ret < 0)
dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
}
-static uint8_t
-reg_read(struct drm_encoder *encoder, uint16_t reg)
+static int
+reg_read(struct tda998x_priv *priv, uint16_t reg)
{
uint8_t val = 0;
- reg_read_range(encoder, reg, &val, sizeof(val));
+ int ret;
+
+ ret = reg_read_range(priv, reg, &val, sizeof(val));
+ if (ret < 0)
+ return ret;
return val;
}
static void
-reg_write(struct drm_encoder *encoder, uint16_t reg, uint8_t val)
+reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
{
- struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct i2c_client *client = priv->hdmi;
uint8_t buf[] = {REG2ADDR(reg), val};
int ret;
- set_page(encoder, reg);
+ ret = set_page(priv, reg);
+ if (ret < 0)
+ return;
- ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ ret = i2c_master_send(client, buf, sizeof(buf));
if (ret < 0)
dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
}
static void
-reg_write16(struct drm_encoder *encoder, uint16_t reg, uint16_t val)
+reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val)
{
- struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct i2c_client *client = priv->hdmi;
uint8_t buf[] = {REG2ADDR(reg), val >> 8, val};
int ret;
- set_page(encoder, reg);
+ ret = set_page(priv, reg);
+ if (ret < 0)
+ return;
- ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ ret = i2c_master_send(client, buf, sizeof(buf));
if (ret < 0)
dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
}
static void
-reg_set(struct drm_encoder *encoder, uint16_t reg, uint8_t val)
+reg_set(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
{
- reg_write(encoder, reg, reg_read(encoder, reg) | val);
+ int old_val;
+
+ old_val = reg_read(priv, reg);
+ if (old_val >= 0)
+ reg_write(priv, reg, old_val | val);
}
static void
-reg_clear(struct drm_encoder *encoder, uint16_t reg, uint8_t val)
+reg_clear(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
{
- reg_write(encoder, reg, reg_read(encoder, reg) & ~val);
+ int old_val;
+
+ old_val = reg_read(priv, reg);
+ if (old_val >= 0)
+ reg_write(priv, reg, old_val & ~val);
}
static void
-tda998x_reset(struct drm_encoder *encoder)
+tda998x_reset(struct tda998x_priv *priv)
{
/* reset audio and i2c master: */
- reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER);
+ reg_write(priv, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER);
msleep(50);
- reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER);
+ reg_write(priv, REG_SOFTRESET, 0);
msleep(50);
/* reset transmitter: */
- reg_set(encoder, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR);
- reg_clear(encoder, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+ reg_set(priv, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR);
+ reg_clear(priv, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR);
/* PLL registers common configuration */
- reg_write(encoder, REG_PLL_SERIAL_1, 0x00);
- reg_write(encoder, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1));
- reg_write(encoder, REG_PLL_SERIAL_3, 0x00);
- reg_write(encoder, REG_SERIALIZER, 0x00);
- reg_write(encoder, REG_BUFFER_OUT, 0x00);
- reg_write(encoder, REG_PLL_SCG1, 0x00);
- reg_write(encoder, REG_AUDIO_DIV, AUDIO_DIV_SERCLK_8);
- reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
- reg_write(encoder, REG_PLL_SCGN1, 0xfa);
- reg_write(encoder, REG_PLL_SCGN2, 0x00);
- reg_write(encoder, REG_PLL_SCGR1, 0x5b);
- reg_write(encoder, REG_PLL_SCGR2, 0x00);
- reg_write(encoder, REG_PLL_SCG2, 0x10);
+ reg_write(priv, REG_PLL_SERIAL_1, 0x00);
+ reg_write(priv, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1));
+ reg_write(priv, REG_PLL_SERIAL_3, 0x00);
+ reg_write(priv, REG_SERIALIZER, 0x00);
+ reg_write(priv, REG_BUFFER_OUT, 0x00);
+ reg_write(priv, REG_PLL_SCG1, 0x00);
+ reg_write(priv, REG_AUDIO_DIV, AUDIO_DIV_SERCLK_8);
+ reg_write(priv, REG_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
+ reg_write(priv, REG_PLL_SCGN1, 0xfa);
+ reg_write(priv, REG_PLL_SCGN2, 0x00);
+ reg_write(priv, REG_PLL_SCGR1, 0x5b);
+ reg_write(priv, REG_PLL_SCGR2, 0x00);
+ reg_write(priv, REG_PLL_SCG2, 0x10);
/* Write the default value MUX register */
- reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24);
+ reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24);
+}
+
+/*
+ * only 2 interrupts may occur: screen plug/unplug and EDID read
+ */
+static irqreturn_t tda998x_irq_thread(int irq, void *data)
+{
+ struct tda998x_priv *priv = data;
+ u8 sta, cec, lvl, flag0, flag1, flag2;
+
+ if (!priv)
+ return IRQ_HANDLED;
+ sta = cec_read(priv, REG_CEC_INTSTATUS);
+ cec = cec_read(priv, REG_CEC_RXSHPDINT);
+ lvl = cec_read(priv, REG_CEC_RXSHPDLEV);
+ flag0 = reg_read(priv, REG_INT_FLAGS_0);
+ flag1 = reg_read(priv, REG_INT_FLAGS_1);
+ flag2 = reg_read(priv, REG_INT_FLAGS_2);
+ DRM_DEBUG_DRIVER(
+ "tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n",
+ sta, cec, lvl, flag0, flag1, flag2);
+ if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
+ priv->wq_edid_wait = 0;
+ wake_up(&priv->wq_edid);
+ } else if (cec != 0) { /* HPD change */
+ if (priv->encoder && priv->encoder->dev)
+ drm_helper_hpd_irq_event(priv->encoder->dev);
+ }
+ return IRQ_HANDLED;
}
static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
@@ -513,91 +579,88 @@ static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
#define PB(x) (HB(2) + 1 + (x))
static void
-tda998x_write_if(struct drm_encoder *encoder, uint8_t bit, uint16_t addr,
+tda998x_write_if(struct tda998x_priv *priv, uint8_t bit, uint16_t addr,
uint8_t *buf, size_t size)
{
buf[PB(0)] = tda998x_cksum(buf, size);
- reg_clear(encoder, REG_DIP_IF_FLAGS, bit);
- reg_write_range(encoder, addr, buf, size);
- reg_set(encoder, REG_DIP_IF_FLAGS, bit);
+ reg_clear(priv, REG_DIP_IF_FLAGS, bit);
+ reg_write_range(priv, addr, buf, size);
+ reg_set(priv, REG_DIP_IF_FLAGS, bit);
}
static void
-tda998x_write_aif(struct drm_encoder *encoder, struct tda998x_encoder_params *p)
+tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p)
{
- uint8_t buf[PB(5) + 1];
+ u8 buf[PB(HDMI_AUDIO_INFOFRAME_SIZE) + 1];
memset(buf, 0, sizeof(buf));
- buf[HB(0)] = 0x84;
+ buf[HB(0)] = HDMI_INFOFRAME_TYPE_AUDIO;
buf[HB(1)] = 0x01;
- buf[HB(2)] = 10;
+ buf[HB(2)] = HDMI_AUDIO_INFOFRAME_SIZE;
buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */
buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */
buf[PB(4)] = p->audio_frame[4];
buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */
- tda998x_write_if(encoder, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
+ tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
sizeof(buf));
}
static void
-tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
+tda998x_write_avi(struct tda998x_priv *priv, struct drm_display_mode *mode)
{
- uint8_t buf[PB(13) + 1];
+ u8 buf[PB(HDMI_AVI_INFOFRAME_SIZE) + 1];
memset(buf, 0, sizeof(buf));
- buf[HB(0)] = 0x82;
+ buf[HB(0)] = HDMI_INFOFRAME_TYPE_AVI;
buf[HB(1)] = 0x02;
- buf[HB(2)] = 13;
+ buf[HB(2)] = HDMI_AVI_INFOFRAME_SIZE;
buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
+ buf[PB(2)] = HDMI_ACTIVE_ASPECT_PICTURE;
buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;
buf[PB(4)] = drm_match_cea_mode(mode);
- tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
+ tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,
sizeof(buf));
}
-static void tda998x_audio_mute(struct drm_encoder *encoder, bool on)
+static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
{
if (on) {
- reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
- reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO);
- reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+ reg_set(priv, REG_SOFTRESET, SOFTRESET_AUDIO);
+ reg_clear(priv, REG_SOFTRESET, SOFTRESET_AUDIO);
+ reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
} else {
- reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+ reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
}
}
static void
-tda998x_configure_audio(struct drm_encoder *encoder,
+tda998x_configure_audio(struct tda998x_priv *priv,
struct drm_display_mode *mode, struct tda998x_encoder_params *p)
{
- uint8_t buf[6], clksel_aip, clksel_fs, ca_i2s, cts_n, adiv;
+ uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv;
uint32_t n;
/* Enable audio ports */
- reg_write(encoder, REG_ENA_AP, p->audio_cfg);
- reg_write(encoder, REG_ENA_ACLK, p->audio_clk_cfg);
+ reg_write(priv, REG_ENA_AP, p->audio_cfg);
+ reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
/* Set audio input source */
switch (p->audio_format) {
case AFMT_SPDIF:
- reg_write(encoder, REG_MUX_AP, 0x40);
- clksel_aip = AIP_CLKSEL_AIP(0);
- /* FS64SPDIF */
- clksel_fs = AIP_CLKSEL_FS(2);
+ reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
+ clksel_aip = AIP_CLKSEL_AIP_SPDIF;
+ clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
cts_n = CTS_N_M(3) | CTS_N_K(3);
- ca_i2s = 0;
break;
case AFMT_I2S:
- reg_write(encoder, REG_MUX_AP, 0x64);
- clksel_aip = AIP_CLKSEL_AIP(1);
- /* ACLK */
- clksel_fs = AIP_CLKSEL_FS(0);
+ reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
+ clksel_aip = AIP_CLKSEL_AIP_I2S;
+ clksel_fs = AIP_CLKSEL_FS_ACLK;
cts_n = CTS_N_M(3) | CTS_N_K(3);
- ca_i2s = CA_I2S_CA_I2S(0);
break;
default:
@@ -605,12 +668,10 @@ tda998x_configure_audio(struct drm_encoder *encoder,
return;
}
- reg_write(encoder, REG_AIP_CLKSEL, clksel_aip);
- reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT);
-
- /* Enable automatic CTS generation */
- reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_ACR_MAN);
- reg_write(encoder, REG_CTS_N, cts_n);
+ reg_write(priv, REG_AIP_CLKSEL, clksel_aip);
+ reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT |
+ AIP_CNTRL_0_ACR_MAN); /* auto CTS */
+ reg_write(priv, REG_CTS_N, cts_n);
/*
* Audio input somehow depends on HDMI line rate which is
@@ -619,11 +680,15 @@ tda998x_configure_audio(struct drm_encoder *encoder,
* There is no detailed info in the datasheet, so we just
* assume 100MHz requires larger divider.
*/
+ adiv = AUDIO_DIV_SERCLK_8;
if (mode->clock > 100000)
- adiv = AUDIO_DIV_SERCLK_16;
- else
- adiv = AUDIO_DIV_SERCLK_8;
- reg_write(encoder, REG_AUDIO_DIV, adiv);
+ adiv++; /* AUDIO_DIV_SERCLK_16 */
+
+ /* S/PDIF asks for a larger divider */
+ if (p->audio_format == AFMT_SPDIF)
+ adiv++; /* AUDIO_DIV_SERCLK_16 or _32 */
+
+ reg_write(priv, REG_AUDIO_DIV, adiv);
/*
* This is the approximate value of N, which happens to be
@@ -638,28 +703,29 @@ tda998x_configure_audio(struct drm_encoder *encoder,
buf[3] = n;
buf[4] = n >> 8;
buf[5] = n >> 16;
- reg_write_range(encoder, REG_ACR_CTS_0, buf, 6);
+ reg_write_range(priv, REG_ACR_CTS_0, buf, 6);
/* Set CTS clock reference */
- reg_write(encoder, REG_AIP_CLKSEL, clksel_aip | clksel_fs);
+ reg_write(priv, REG_AIP_CLKSEL, clksel_aip | clksel_fs);
/* Reset CTS generator */
- reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
- reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
+ reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
+ reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
/* Write the channel status */
- buf[0] = 0x04;
+ buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
buf[1] = 0x00;
- buf[2] = 0x00;
- buf[3] = 0xf1;
- reg_write_range(encoder, REG_CH_STAT_B(0), buf, 4);
+ buf[2] = IEC958_AES3_CON_FS_NOTID;
+ buf[3] = IEC958_AES4_CON_ORIGFS_NOTID |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ reg_write_range(priv, REG_CH_STAT_B(0), buf, 4);
- tda998x_audio_mute(encoder, true);
- mdelay(20);
- tda998x_audio_mute(encoder, false);
+ tda998x_audio_mute(priv, true);
+ msleep(20);
+ tda998x_audio_mute(priv, false);
/* Write the audio information packet */
- tda998x_write_aif(encoder, p);
+ tda998x_write_aif(priv, p);
}
/* DRM encoder functions */
@@ -701,19 +767,19 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
switch (mode) {
case DRM_MODE_DPMS_ON:
/* enable video ports, audio will be enabled later */
- reg_write(encoder, REG_ENA_VP_0, 0xff);
- reg_write(encoder, REG_ENA_VP_1, 0xff);
- reg_write(encoder, REG_ENA_VP_2, 0xff);
+ reg_write(priv, REG_ENA_VP_0, 0xff);
+ reg_write(priv, REG_ENA_VP_1, 0xff);
+ reg_write(priv, REG_ENA_VP_2, 0xff);
/* set muxing after enabling ports: */
- reg_write(encoder, REG_VIP_CNTRL_0, priv->vip_cntrl_0);
- reg_write(encoder, REG_VIP_CNTRL_1, priv->vip_cntrl_1);
- reg_write(encoder, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
+ reg_write(priv, REG_VIP_CNTRL_0, priv->vip_cntrl_0);
+ reg_write(priv, REG_VIP_CNTRL_1, priv->vip_cntrl_1);
+ reg_write(priv, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
break;
case DRM_MODE_DPMS_OFF:
/* disable video ports */
- reg_write(encoder, REG_ENA_VP_0, 0x00);
- reg_write(encoder, REG_ENA_VP_1, 0x00);
- reg_write(encoder, REG_ENA_VP_2, 0x00);
+ reg_write(priv, REG_ENA_VP_0, 0x00);
+ reg_write(priv, REG_ENA_VP_1, 0x00);
+ reg_write(priv, REG_ENA_VP_2, 0x00);
break;
}
@@ -831,110 +897,110 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
}
/* mute the audio FIFO: */
- reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
+ reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
/* set HDMI HDCP mode off: */
- reg_set(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
- reg_clear(encoder, REG_TX33, TX33_HDMI);
+ reg_write(priv, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
+ reg_clear(priv, REG_TX33, TX33_HDMI);
+ reg_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0));
- reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0));
/* no pre-filter or interpolator: */
- reg_write(encoder, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) |
+ reg_write(priv, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) |
HVF_CNTRL_0_INTPOL(0));
- reg_write(encoder, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
- reg_write(encoder, REG_VIP_CNTRL_4, VIP_CNTRL_4_BLANKIT(0) |
+ reg_write(priv, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
+ reg_write(priv, REG_VIP_CNTRL_4, VIP_CNTRL_4_BLANKIT(0) |
VIP_CNTRL_4_BLC(0));
- reg_clear(encoder, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR);
- reg_clear(encoder, REG_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IZ);
- reg_clear(encoder, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE);
- reg_write(encoder, REG_SERIALIZER, 0);
- reg_write(encoder, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0));
+ reg_clear(priv, REG_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IZ);
+ reg_clear(priv, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR |
+ PLL_SERIAL_3_SRL_DE);
+ reg_write(priv, REG_SERIALIZER, 0);
+ reg_write(priv, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0));
/* TODO enable pixel repeat for pixel rates less than 25Msamp/s */
rep = 0;
- reg_write(encoder, REG_RPT_CNTRL, 0);
- reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
+ reg_write(priv, REG_RPT_CNTRL, 0);
+ reg_write(priv, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
- reg_write(encoder, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
+ reg_write(priv, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
PLL_SERIAL_2_SRL_PR(rep));
/* set color matrix bypass flag: */
- reg_set(encoder, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP);
+ reg_write(priv, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP |
+ MAT_CONTRL_MAT_SC(1));
/* set BIAS tmds value: */
- reg_write(encoder, REG_ANA_GENERAL, 0x09);
-
- reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD);
+ reg_write(priv, REG_ANA_GENERAL, 0x09);
/*
* Sync on rising HSYNC/VSYNC
*/
- reg_write(encoder, REG_VIP_CNTRL_3, 0);
- reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_SYNC_HS);
+ reg = VIP_CNTRL_3_SYNC_HS;
/*
* TDA19988 requires high-active sync at input stage,
* so invert low-active sync provided by master encoder here
*/
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
- reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_H_TGL);
+ reg |= VIP_CNTRL_3_H_TGL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
- reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_V_TGL);
+ reg |= VIP_CNTRL_3_V_TGL;
+ reg_write(priv, REG_VIP_CNTRL_3, reg);
+
+ reg_write(priv, REG_VIDFORMAT, 0x00);
+ reg_write16(priv, REG_REFPIX_MSB, ref_pix);
+ reg_write16(priv, REG_REFLINE_MSB, ref_line);
+ reg_write16(priv, REG_NPIX_MSB, n_pix);
+ reg_write16(priv, REG_NLINE_MSB, n_line);
+ reg_write16(priv, REG_VS_LINE_STRT_1_MSB, vs1_line_s);
+ reg_write16(priv, REG_VS_PIX_STRT_1_MSB, vs1_pix_s);
+ reg_write16(priv, REG_VS_LINE_END_1_MSB, vs1_line_e);
+ reg_write16(priv, REG_VS_PIX_END_1_MSB, vs1_pix_e);
+ reg_write16(priv, REG_VS_LINE_STRT_2_MSB, vs2_line_s);
+ reg_write16(priv, REG_VS_PIX_STRT_2_MSB, vs2_pix_s);
+ reg_write16(priv, REG_VS_LINE_END_2_MSB, vs2_line_e);
+ reg_write16(priv, REG_VS_PIX_END_2_MSB, vs2_pix_e);
+ reg_write16(priv, REG_HS_PIX_START_MSB, hs_pix_s);
+ reg_write16(priv, REG_HS_PIX_STOP_MSB, hs_pix_e);
+ reg_write16(priv, REG_VWIN_START_1_MSB, vwin1_line_s);
+ reg_write16(priv, REG_VWIN_END_1_MSB, vwin1_line_e);
+ reg_write16(priv, REG_VWIN_START_2_MSB, vwin2_line_s);
+ reg_write16(priv, REG_VWIN_END_2_MSB, vwin2_line_e);
+ reg_write16(priv, REG_DE_START_MSB, de_pix_s);
+ reg_write16(priv, REG_DE_STOP_MSB, de_pix_e);
+
+ if (priv->rev == TDA19988) {
+ /* let incoming pixels fill the active space (if any) */
+ reg_write(priv, REG_ENABLE_SPACE, 0x00);
+ }
/*
* Always generate sync polarity relative to input sync and
* revert input stage toggled sync at output stage
*/
- reg = TBG_CNTRL_1_TGL_EN;
+ reg = TBG_CNTRL_1_DWIN_DIS | TBG_CNTRL_1_TGL_EN;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
reg |= TBG_CNTRL_1_H_TGL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
reg |= TBG_CNTRL_1_V_TGL;
- reg_write(encoder, REG_TBG_CNTRL_1, reg);
-
- reg_write(encoder, REG_VIDFORMAT, 0x00);
- reg_write16(encoder, REG_REFPIX_MSB, ref_pix);
- reg_write16(encoder, REG_REFLINE_MSB, ref_line);
- reg_write16(encoder, REG_NPIX_MSB, n_pix);
- reg_write16(encoder, REG_NLINE_MSB, n_line);
- reg_write16(encoder, REG_VS_LINE_STRT_1_MSB, vs1_line_s);
- reg_write16(encoder, REG_VS_PIX_STRT_1_MSB, vs1_pix_s);
- reg_write16(encoder, REG_VS_LINE_END_1_MSB, vs1_line_e);
- reg_write16(encoder, REG_VS_PIX_END_1_MSB, vs1_pix_e);
- reg_write16(encoder, REG_VS_LINE_STRT_2_MSB, vs2_line_s);
- reg_write16(encoder, REG_VS_PIX_STRT_2_MSB, vs2_pix_s);
- reg_write16(encoder, REG_VS_LINE_END_2_MSB, vs2_line_e);
- reg_write16(encoder, REG_VS_PIX_END_2_MSB, vs2_pix_e);
- reg_write16(encoder, REG_HS_PIX_START_MSB, hs_pix_s);
- reg_write16(encoder, REG_HS_PIX_STOP_MSB, hs_pix_e);
- reg_write16(encoder, REG_VWIN_START_1_MSB, vwin1_line_s);
- reg_write16(encoder, REG_VWIN_END_1_MSB, vwin1_line_e);
- reg_write16(encoder, REG_VWIN_START_2_MSB, vwin2_line_s);
- reg_write16(encoder, REG_VWIN_END_2_MSB, vwin2_line_e);
- reg_write16(encoder, REG_DE_START_MSB, de_pix_s);
- reg_write16(encoder, REG_DE_STOP_MSB, de_pix_e);
-
- if (priv->rev == TDA19988) {
- /* let incoming pixels fill the active space (if any) */
- reg_write(encoder, REG_ENABLE_SPACE, 0x00);
- }
+ reg_write(priv, REG_TBG_CNTRL_1, reg);
/* must be last register set: */
- reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
+ reg_write(priv, REG_TBG_CNTRL_0, 0);
/* Only setup the info frames if the sink is HDMI */
if (priv->is_hdmi_sink) {
/* We need to turn HDMI HDCP stuff on to get audio through */
- reg_clear(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
- reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
- reg_set(encoder, REG_TX33, TX33_HDMI);
+ reg &= ~TBG_CNTRL_1_DWIN_DIS;
+ reg_write(priv, REG_TBG_CNTRL_1, reg);
+ reg_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
+ reg_set(priv, REG_TX33, TX33_HDMI);
- tda998x_write_avi(encoder, adjusted_mode);
+ tda998x_write_avi(priv, adjusted_mode);
if (priv->params.audio_cfg)
- tda998x_configure_audio(encoder, adjusted_mode,
+ tda998x_configure_audio(priv, adjusted_mode,
&priv->params);
}
}
@@ -943,7 +1009,9 @@ static enum drm_connector_status
tda998x_encoder_detect(struct drm_encoder *encoder,
struct drm_connector *connector)
{
- uint8_t val = cec_read(encoder, REG_CEC_RXSHPDLEV);
+ struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV);
+
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
connector_status_disconnected;
}
@@ -951,46 +1019,57 @@ tda998x_encoder_detect(struct drm_encoder *encoder,
static int
read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
{
+ struct tda998x_priv *priv = to_tda998x_priv(encoder);
uint8_t offset, segptr;
int ret, i;
- /* enable EDID read irq: */
- reg_set(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
-
offset = (blk & 1) ? 128 : 0;
segptr = blk / 2;
- reg_write(encoder, REG_DDC_ADDR, 0xa0);
- reg_write(encoder, REG_DDC_OFFS, offset);
- reg_write(encoder, REG_DDC_SEGM_ADDR, 0x60);
- reg_write(encoder, REG_DDC_SEGM, segptr);
+ reg_write(priv, REG_DDC_ADDR, 0xa0);
+ reg_write(priv, REG_DDC_OFFS, offset);
+ reg_write(priv, REG_DDC_SEGM_ADDR, 0x60);
+ reg_write(priv, REG_DDC_SEGM, segptr);
/* enable reading EDID: */
- reg_write(encoder, REG_EDID_CTRL, 0x1);
+ priv->wq_edid_wait = 1;
+ reg_write(priv, REG_EDID_CTRL, 0x1);
/* flag must be cleared by sw: */
- reg_write(encoder, REG_EDID_CTRL, 0x0);
+ reg_write(priv, REG_EDID_CTRL, 0x0);
/* wait for block read to complete: */
- for (i = 100; i > 0; i--) {
- uint8_t val = reg_read(encoder, REG_INT_FLAGS_2);
- if (val & INT_FLAGS_2_EDID_BLK_RD)
- break;
- msleep(1);
+ if (priv->hdmi->irq) {
+ i = wait_event_timeout(priv->wq_edid,
+ !priv->wq_edid_wait,
+ msecs_to_jiffies(100));
+ if (i < 0) {
+ dev_err(&priv->hdmi->dev, "read edid wait err %d\n", i);
+ return i;
+ }
+ } else {
+ for (i = 10; i > 0; i--) {
+ msleep(10);
+ ret = reg_read(priv, REG_INT_FLAGS_2);
+ if (ret < 0)
+ return ret;
+ if (ret & INT_FLAGS_2_EDID_BLK_RD)
+ break;
+ }
}
- if (i == 0)
+ if (i == 0) {
+ dev_err(&priv->hdmi->dev, "read edid timeout\n");
return -ETIMEDOUT;
+ }
- ret = reg_read_range(encoder, REG_EDID_DATA_0, buf, EDID_LENGTH);
+ ret = reg_read_range(priv, REG_EDID_DATA_0, buf, EDID_LENGTH);
if (ret != EDID_LENGTH) {
- dev_err(encoder->dev->dev, "failed to read edid block %d: %d",
- blk, ret);
+ dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n",
+ blk, ret);
return ret;
}
- reg_clear(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
-
return 0;
}
@@ -998,7 +1077,7 @@ static uint8_t *
do_get_edid(struct drm_encoder *encoder)
{
struct tda998x_priv *priv = to_tda998x_priv(encoder);
- int j = 0, valid_extensions = 0;
+ int j, valid_extensions = 0;
uint8_t *block, *new;
bool print_bad_edid = drm_debug & DRM_UT_KMS;
@@ -1006,7 +1085,7 @@ do_get_edid(struct drm_encoder *encoder)
return NULL;
if (priv->rev == TDA19988)
- reg_clear(encoder, REG_TX4, TX4_PD_RAM);
+ reg_clear(priv, REG_TX4, TX4_PD_RAM);
/* base block fetch */
if (read_edid_block(encoder, block, 0))
@@ -1046,14 +1125,14 @@ do_get_edid(struct drm_encoder *encoder)
done:
if (priv->rev == TDA19988)
- reg_set(encoder, REG_TX4, TX4_PD_RAM);
+ reg_set(priv, REG_TX4, TX4_PD_RAM);
return block;
fail:
if (priv->rev == TDA19988)
- reg_set(encoder, REG_TX4, TX4_PD_RAM);
- dev_warn(encoder->dev->dev, "failed to read EDID\n");
+ reg_set(priv, REG_TX4, TX4_PD_RAM);
+ dev_warn(&priv->hdmi->dev, "failed to read EDID\n");
kfree(block);
return NULL;
}
@@ -1080,7 +1159,13 @@ static int
tda998x_encoder_create_resources(struct drm_encoder *encoder,
struct drm_connector *connector)
{
- DBG("");
+ struct tda998x_priv *priv = to_tda998x_priv(encoder);
+
+ if (priv->hdmi->irq)
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ else
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
}
@@ -1099,6 +1184,13 @@ tda998x_encoder_destroy(struct drm_encoder *encoder)
{
struct tda998x_priv *priv = to_tda998x_priv(encoder);
drm_i2c_encoder_destroy(encoder);
+
+ /* disable all IRQs and free the IRQ handler */
+ cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
+ reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+ if (priv->hdmi->irq)
+ free_irq(priv->hdmi->irq, priv);
+
if (priv->cec)
i2c_unregister_device(priv->cec);
kfree(priv);
@@ -1138,8 +1230,10 @@ tda998x_encoder_init(struct i2c_client *client,
struct drm_device *dev,
struct drm_encoder_slave *encoder_slave)
{
- struct drm_encoder *encoder = &encoder_slave->base;
struct tda998x_priv *priv;
+ struct device_node *np = client->dev.of_node;
+ u32 video;
+ int rev_lo, rev_hi, ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -1150,52 +1244,113 @@ tda998x_encoder_init(struct i2c_client *client,
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
priv->current_page = 0xff;
+ priv->hdmi = client;
priv->cec = i2c_new_dummy(client->adapter, 0x34);
if (!priv->cec) {
kfree(priv);
return -ENODEV;
}
+
+ priv->encoder = &encoder_slave->base;
priv->dpms = DRM_MODE_DPMS_OFF;
encoder_slave->slave_priv = priv;
encoder_slave->slave_funcs = &tda998x_encoder_funcs;
/* wake up the device: */
- cec_write(encoder, REG_CEC_ENAMODS,
+ cec_write(priv, REG_CEC_ENAMODS,
CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
- tda998x_reset(encoder);
+ tda998x_reset(priv);
/* read version: */
- priv->rev = reg_read(encoder, REG_VERSION_LSB) |
- reg_read(encoder, REG_VERSION_MSB) << 8;
+ rev_lo = reg_read(priv, REG_VERSION_LSB);
+ rev_hi = reg_read(priv, REG_VERSION_MSB);
+ if (rev_lo < 0 || rev_hi < 0) {
+ ret = rev_lo < 0 ? rev_lo : rev_hi;
+ goto fail;
+ }
+
+ priv->rev = rev_lo | rev_hi << 8;
/* mask off feature bits: */
priv->rev &= ~0x30; /* not-hdcp and not-scalar bit */
switch (priv->rev) {
- case TDA9989N2: dev_info(dev->dev, "found TDA9989 n2"); break;
- case TDA19989: dev_info(dev->dev, "found TDA19989"); break;
- case TDA19989N2: dev_info(dev->dev, "found TDA19989 n2"); break;
- case TDA19988: dev_info(dev->dev, "found TDA19988"); break;
+ case TDA9989N2:
+ dev_info(&client->dev, "found TDA9989 n2");
+ break;
+ case TDA19989:
+ dev_info(&client->dev, "found TDA19989");
+ break;
+ case TDA19989N2:
+ dev_info(&client->dev, "found TDA19989 n2");
+ break;
+ case TDA19988:
+ dev_info(&client->dev, "found TDA19988");
+ break;
default:
- DBG("found unsupported device: %04x", priv->rev);
+ dev_err(&client->dev, "found unsupported device: %04x\n",
+ priv->rev);
goto fail;
}
/* after reset, enable DDC: */
- reg_write(encoder, REG_DDC_DISABLE, 0x00);
+ reg_write(priv, REG_DDC_DISABLE, 0x00);
/* set clock on DDC channel: */
- reg_write(encoder, REG_TX3, 39);
+ reg_write(priv, REG_TX3, 39);
/* if necessary, disable multi-master: */
if (priv->rev == TDA19989)
- reg_set(encoder, REG_I2C_MASTER, I2C_MASTER_DIS_MM);
+ reg_set(priv, REG_I2C_MASTER, I2C_MASTER_DIS_MM);
- cec_write(encoder, REG_CEC_FRO_IM_CLK_CTRL,
+ cec_write(priv, REG_CEC_FRO_IM_CLK_CTRL,
CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
+ /* initialize the optional IRQ */
+ if (client->irq) {
+ int irqf_trigger;
+
+ /* init read EDID waitqueue */
+ init_waitqueue_head(&priv->wq_edid);
+
+ /* clear pending interrupts */
+ reg_read(priv, REG_INT_FLAGS_0);
+ reg_read(priv, REG_INT_FLAGS_1);
+ reg_read(priv, REG_INT_FLAGS_2);
+
+ irqf_trigger =
+ irqd_get_trigger_type(irq_get_irq_data(client->irq));
+ ret = request_threaded_irq(client->irq, NULL,
+ tda998x_irq_thread,
+ irqf_trigger | IRQF_ONESHOT,
+ "tda998x", priv);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to request IRQ#%u: %d\n",
+ client->irq, ret);
+ goto fail;
+ }
+
+ /* enable HPD irq */
+ cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD);
+ }
+
+ /* enable EDID read irq: */
+ reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+
+ if (!np)
+ return 0; /* non-DT */
+
+ /* get the optional video properties */
+ ret = of_property_read_u32(np, "video-ports", &video);
+ if (ret == 0) {
+ priv->vip_cntrl_0 = video >> 16;
+ priv->vip_cntrl_1 = video >> 8;
+ priv->vip_cntrl_2 = video;
+ }
+
return 0;
fail:
@@ -1210,6 +1365,14 @@ fail:
return -ENXIO;
}
+#ifdef CONFIG_OF
+static const struct of_device_id tda998x_dt_ids[] = {
+ { .compatible = "nxp,tda998x", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tda998x_dt_ids);
+#endif
+
static struct i2c_device_id tda998x_ids[] = {
{ "tda998x", 0 },
{ }
@@ -1222,6 +1385,7 @@ static struct drm_i2c_encoder_driver tda998x_driver = {
.remove = tda998x_remove,
.driver = {
.name = "tda998x",
+ .of_match_table = of_match_ptr(tda998x_dt_ids),
},
.id_table = tda998x_ids,
},
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 9fd44f5f3b3b..b1445b73465b 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -3,57 +3,69 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm
-i915-y := i915_drv.o i915_dma.o i915_irq.o \
- i915_gpu_error.o \
+
+# Please keep these build lists sorted!
+
+# core driver code
+i915-y := i915_drv.o \
+ i915_params.o \
i915_suspend.o \
- i915_gem.o \
+ i915_sysfs.o \
+ intel_pm.o
+i915-$(CONFIG_COMPAT) += i915_ioc32.o
+i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o
+
+# GEM code
+i915-y += i915_cmd_parser.o \
i915_gem_context.o \
i915_gem_debug.o \
+ i915_gem_dmabuf.o \
i915_gem_evict.o \
i915_gem_execbuffer.o \
i915_gem_gtt.o \
+ i915_gem.o \
i915_gem_stolen.o \
i915_gem_tiling.o \
- i915_sysfs.o \
+ i915_gpu_error.o \
+ i915_irq.o \
i915_trace_points.o \
- i915_ums.o \
+ intel_ringbuffer.o \
+ intel_uncore.o
+
+# modesetting core code
+i915-y += intel_bios.o \
intel_display.o \
- intel_crt.o \
- intel_lvds.o \
- intel_dsi.o \
- intel_dsi_cmd.o \
- intel_dsi_pll.o \
- intel_bios.o \
- intel_ddi.o \
- intel_dp.o \
- intel_hdmi.o \
- intel_sdvo.o \
intel_modes.o \
- intel_panel.o \
- intel_pm.o \
- intel_i2c.o \
- intel_tv.o \
- intel_dvo.o \
- intel_ringbuffer.o \
intel_overlay.o \
- intel_sprite.o \
intel_sideband.o \
- intel_uncore.o \
+ intel_sprite.o
+i915-$(CONFIG_ACPI) += intel_acpi.o intel_opregion.o
+i915-$(CONFIG_DRM_I915_FBDEV) += intel_fbdev.o
+
+# modesetting output/encoder code
+i915-y += dvo_ch7017.o \
dvo_ch7xxx.o \
- dvo_ch7017.o \
dvo_ivch.o \
- dvo_tfp410.o \
- dvo_sil164.o \
dvo_ns2501.o \
- i915_gem_dmabuf.o
-
-i915-$(CONFIG_COMPAT) += i915_ioc32.o
-
-i915-$(CONFIG_ACPI) += intel_acpi.o intel_opregion.o
-
-i915-$(CONFIG_DRM_I915_FBDEV) += intel_fbdev.o
+ dvo_sil164.o \
+ dvo_tfp410.o \
+ intel_crt.o \
+ intel_ddi.o \
+ intel_dp.o \
+ intel_dsi_cmd.o \
+ intel_dsi.o \
+ intel_dsi_pll.o \
+ intel_dvo.o \
+ intel_hdmi.o \
+ intel_i2c.o \
+ intel_lvds.o \
+ intel_panel.o \
+ intel_sdvo.o \
+ intel_tv.o
-i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o
+# legacy horrors
+i915-y += i915_dma.o \
+ i915_ums.o
obj-$(CONFIG_DRM_I915) += i915.o
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
index af42e94f6846..a0f5bdd69491 100644
--- a/drivers/gpu/drm/i915/dvo_ch7xxx.c
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
@@ -340,9 +340,9 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
for (i = 0; i < CH7xxx_NUM_REGS; i++) {
uint8_t val;
if ((i % 8) == 0)
- DRM_LOG_KMS("\n %02X: ", i);
+ DRM_DEBUG_KMS("\n %02X: ", i);
ch7xxx_readb(dvo, i, &val);
- DRM_LOG_KMS("%02X ", val);
+ DRM_DEBUG_KMS("%02X ", val);
}
}
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
index baaf65bf0bdd..0f1865d7d4d8 100644
--- a/drivers/gpu/drm/i915/dvo_ivch.c
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
@@ -377,41 +377,41 @@ static void ivch_dump_regs(struct intel_dvo_device *dvo)
uint16_t val;
ivch_read(dvo, VR00, &val);
- DRM_LOG_KMS("VR00: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR00: 0x%04x\n", val);
ivch_read(dvo, VR01, &val);
- DRM_LOG_KMS("VR01: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR01: 0x%04x\n", val);
ivch_read(dvo, VR30, &val);
- DRM_LOG_KMS("VR30: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR30: 0x%04x\n", val);
ivch_read(dvo, VR40, &val);
- DRM_LOG_KMS("VR40: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR40: 0x%04x\n", val);
/* GPIO registers */
ivch_read(dvo, VR80, &val);
- DRM_LOG_KMS("VR80: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR80: 0x%04x\n", val);
ivch_read(dvo, VR81, &val);
- DRM_LOG_KMS("VR81: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR81: 0x%04x\n", val);
ivch_read(dvo, VR82, &val);
- DRM_LOG_KMS("VR82: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR82: 0x%04x\n", val);
ivch_read(dvo, VR83, &val);
- DRM_LOG_KMS("VR83: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR83: 0x%04x\n", val);
ivch_read(dvo, VR84, &val);
- DRM_LOG_KMS("VR84: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR84: 0x%04x\n", val);
ivch_read(dvo, VR85, &val);
- DRM_LOG_KMS("VR85: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR85: 0x%04x\n", val);
ivch_read(dvo, VR86, &val);
- DRM_LOG_KMS("VR86: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR86: 0x%04x\n", val);
ivch_read(dvo, VR87, &val);
- DRM_LOG_KMS("VR87: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR87: 0x%04x\n", val);
ivch_read(dvo, VR88, &val);
- DRM_LOG_KMS("VR88: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR88: 0x%04x\n", val);
/* Scratch register 0 - AIM Panel type */
ivch_read(dvo, VR8E, &val);
- DRM_LOG_KMS("VR8E: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR8E: 0x%04x\n", val);
/* Scratch register 1 - Status register */
ivch_read(dvo, VR8F, &val);
- DRM_LOG_KMS("VR8F: 0x%04x\n", val);
+ DRM_DEBUG_KMS("VR8F: 0x%04x\n", val);
}
static void ivch_destroy(struct intel_dvo_device *dvo)
diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c
index 954acb2c7021..8155ded79079 100644
--- a/drivers/gpu/drm/i915/dvo_ns2501.c
+++ b/drivers/gpu/drm/i915/dvo_ns2501.c
@@ -490,15 +490,15 @@ static void ns2501_dump_regs(struct intel_dvo_device *dvo)
uint8_t val;
ns2501_readb(dvo, NS2501_FREQ_LO, &val);
- DRM_LOG_KMS("NS2501_FREQ_LO: 0x%02x\n", val);
+ DRM_DEBUG_KMS("NS2501_FREQ_LO: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_FREQ_HI, &val);
- DRM_LOG_KMS("NS2501_FREQ_HI: 0x%02x\n", val);
+ DRM_DEBUG_KMS("NS2501_FREQ_HI: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_REG8, &val);
- DRM_LOG_KMS("NS2501_REG8: 0x%02x\n", val);
+ DRM_DEBUG_KMS("NS2501_REG8: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_REG9, &val);
- DRM_LOG_KMS("NS2501_REG9: 0x%02x\n", val);
+ DRM_DEBUG_KMS("NS2501_REG9: 0x%02x\n", val);
ns2501_readb(dvo, NS2501_REGC, &val);
- DRM_LOG_KMS("NS2501_REGC: 0x%02x\n", val);
+ DRM_DEBUG_KMS("NS2501_REGC: 0x%02x\n", val);
}
static void ns2501_destroy(struct intel_dvo_device *dvo)
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
index 4debd32e3e4c..7b3e9e936200 100644
--- a/drivers/gpu/drm/i915/dvo_sil164.c
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
@@ -246,15 +246,15 @@ static void sil164_dump_regs(struct intel_dvo_device *dvo)
uint8_t val;
sil164_readb(dvo, SIL164_FREQ_LO, &val);
- DRM_LOG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
+ DRM_DEBUG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
sil164_readb(dvo, SIL164_FREQ_HI, &val);
- DRM_LOG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
+ DRM_DEBUG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REG8, &val);
- DRM_LOG_KMS("SIL164_REG8: 0x%02x\n", val);
+ DRM_DEBUG_KMS("SIL164_REG8: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REG9, &val);
- DRM_LOG_KMS("SIL164_REG9: 0x%02x\n", val);
+ DRM_DEBUG_KMS("SIL164_REG9: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REGC, &val);
- DRM_LOG_KMS("SIL164_REGC: 0x%02x\n", val);
+ DRM_DEBUG_KMS("SIL164_REGC: 0x%02x\n", val);
}
static void sil164_destroy(struct intel_dvo_device *dvo)
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
index e17f1b07e915..12ea4b164692 100644
--- a/drivers/gpu/drm/i915/dvo_tfp410.c
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
@@ -267,33 +267,33 @@ static void tfp410_dump_regs(struct intel_dvo_device *dvo)
uint8_t val, val2;
tfp410_readb(dvo, TFP410_REV, &val);
- DRM_LOG_KMS("TFP410_REV: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_REV: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_1, &val);
- DRM_LOG_KMS("TFP410_CTL1: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_CTL1: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_2, &val);
- DRM_LOG_KMS("TFP410_CTL2: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_CTL2: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_3, &val);
- DRM_LOG_KMS("TFP410_CTL3: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_CTL3: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_USERCFG, &val);
- DRM_LOG_KMS("TFP410_USERCFG: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_USERCFG: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_DLY, &val);
- DRM_LOG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_CTL, &val);
- DRM_LOG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_TOP, &val);
- DRM_LOG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
+ DRM_DEBUG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
- DRM_LOG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+ DRM_DEBUG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
- DRM_LOG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+ DRM_DEBUG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
tfp410_readb(dvo, TFP410_H_RES_LO, &val);
tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
- DRM_LOG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+ DRM_DEBUG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
tfp410_readb(dvo, TFP410_V_RES_LO, &val);
tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
- DRM_LOG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+ DRM_DEBUG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
}
static void tfp410_destroy(struct intel_dvo_device *dvo)
diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
new file mode 100644
index 000000000000..4cf6d020d513
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright © 2013 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:
+ * Brad Volkin <bradley.d.volkin@intel.com>
+ *
+ */
+
+#include "i915_drv.h"
+
+/**
+ * DOC: i915 batch buffer command parser
+ *
+ * Motivation:
+ * Certain OpenGL features (e.g. transform feedback, performance monitoring)
+ * require userspace code to submit batches containing commands such as
+ * MI_LOAD_REGISTER_IMM to access various registers. Unfortunately, some
+ * generations of the hardware will noop these commands in "unsecure" batches
+ * (which includes all userspace batches submitted via i915) even though the
+ * commands may be safe and represent the intended programming model of the
+ * device.
+ *
+ * The software command parser is similar in operation to the command parsing
+ * done in hardware for unsecure batches. However, the software parser allows
+ * some operations that would be noop'd by hardware, if the parser determines
+ * the operation is safe, and submits the batch as "secure" to prevent hardware
+ * parsing.
+ *
+ * Threats:
+ * At a high level, the hardware (and software) checks attempt to prevent
+ * granting userspace undue privileges. There are three categories of privilege.
+ *
+ * First, commands which are explicitly defined as privileged or which should
+ * only be used by the kernel driver. The parser generally rejects such
+ * commands, though it may allow some from the drm master process.
+ *
+ * Second, commands which access registers. To support correct/enhanced
+ * userspace functionality, particularly certain OpenGL extensions, the parser
+ * provides a whitelist of registers which userspace may safely access (for both
+ * normal and drm master processes).
+ *
+ * Third, commands which access privileged memory (i.e. GGTT, HWS page, etc).
+ * The parser always rejects such commands.
+ *
+ * The majority of the problematic commands fall in the MI_* range, with only a
+ * few specific commands on each ring (e.g. PIPE_CONTROL and MI_FLUSH_DW).
+ *
+ * Implementation:
+ * Each ring maintains tables of commands and registers which the parser uses in
+ * scanning batch buffers submitted to that ring.
+ *
+ * Since the set of commands that the parser must check for is significantly
+ * smaller than the number of commands supported, the parser tables contain only
+ * those commands required by the parser. This generally works because command
+ * opcode ranges have standard command length encodings. So for commands that
+ * the parser does not need to check, it can easily skip them. This is
+ * implementated via a per-ring length decoding vfunc.
+ *
+ * Unfortunately, there are a number of commands that do not follow the standard
+ * length encoding for their opcode range, primarily amongst the MI_* commands.
+ * To handle this, the parser provides a way to define explicit "skip" entries
+ * in the per-ring command tables.
+ *
+ * Other command table entries map fairly directly to high level categories
+ * mentioned above: rejected, master-only, register whitelist. The parser
+ * implements a number of checks, including the privileged memory checks, via a
+ * general bitmasking mechanism.
+ */
+
+static u32 gen7_render_get_cmd_length_mask(u32 cmd_header)
+{
+ u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
+ u32 subclient =
+ (cmd_header & INSTR_SUBCLIENT_MASK) >> INSTR_SUBCLIENT_SHIFT;
+
+ if (client == INSTR_MI_CLIENT)
+ return 0x3F;
+ else if (client == INSTR_RC_CLIENT) {
+ if (subclient == INSTR_MEDIA_SUBCLIENT)
+ return 0xFFFF;
+ else
+ return 0xFF;
+ }
+
+ DRM_DEBUG_DRIVER("CMD: Abnormal rcs cmd length! 0x%08X\n", cmd_header);
+ return 0;
+}
+
+static u32 gen7_bsd_get_cmd_length_mask(u32 cmd_header)
+{
+ u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
+ u32 subclient =
+ (cmd_header & INSTR_SUBCLIENT_MASK) >> INSTR_SUBCLIENT_SHIFT;
+
+ if (client == INSTR_MI_CLIENT)
+ return 0x3F;
+ else if (client == INSTR_RC_CLIENT) {
+ if (subclient == INSTR_MEDIA_SUBCLIENT)
+ return 0xFFF;
+ else
+ return 0xFF;
+ }
+
+ DRM_DEBUG_DRIVER("CMD: Abnormal bsd cmd length! 0x%08X\n", cmd_header);
+ return 0;
+}
+
+static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
+{
+ u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
+
+ if (client == INSTR_MI_CLIENT)
+ return 0x3F;
+ else if (client == INSTR_BC_CLIENT)
+ return 0xFF;
+
+ DRM_DEBUG_DRIVER("CMD: Abnormal blt cmd length! 0x%08X\n", cmd_header);
+ return 0;
+}
+
+static void validate_cmds_sorted(struct intel_ring_buffer *ring)
+{
+ int i;
+
+ if (!ring->cmd_tables || ring->cmd_table_count == 0)
+ return;
+
+ for (i = 0; i < ring->cmd_table_count; i++) {
+ const struct drm_i915_cmd_table *table = &ring->cmd_tables[i];
+ u32 previous = 0;
+ int j;
+
+ for (j = 0; j < table->count; j++) {
+ const struct drm_i915_cmd_descriptor *desc =
+ &table->table[i];
+ u32 curr = desc->cmd.value & desc->cmd.mask;
+
+ if (curr < previous)
+ DRM_ERROR("CMD: table not sorted ring=%d table=%d entry=%d cmd=0x%08X prev=0x%08X\n",
+ ring->id, i, j, curr, previous);
+
+ previous = curr;
+ }
+ }
+}
+
+static void check_sorted(int ring_id, const u32 *reg_table, int reg_count)
+{
+ int i;
+ u32 previous = 0;
+
+ for (i = 0; i < reg_count; i++) {
+ u32 curr = reg_table[i];
+
+ if (curr < previous)
+ DRM_ERROR("CMD: table not sorted ring=%d entry=%d reg=0x%08X prev=0x%08X\n",
+ ring_id, i, curr, previous);
+
+ previous = curr;
+ }
+}
+
+static void validate_regs_sorted(struct intel_ring_buffer *ring)
+{
+ check_sorted(ring->id, ring->reg_table, ring->reg_count);
+ check_sorted(ring->id, ring->master_reg_table, ring->master_reg_count);
+}
+
+/**
+ * i915_cmd_parser_init_ring() - set cmd parser related fields for a ringbuffer
+ * @ring: the ringbuffer to initialize
+ *
+ * Optionally initializes fields related to batch buffer command parsing in the
+ * struct intel_ring_buffer based on whether the platform requires software
+ * command parsing.
+ */
+void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring)
+{
+ if (!IS_GEN7(ring->dev))
+ return;
+
+ switch (ring->id) {
+ case RCS:
+ ring->get_cmd_length_mask = gen7_render_get_cmd_length_mask;
+ break;
+ case VCS:
+ ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
+ break;
+ case BCS:
+ ring->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
+ break;
+ case VECS:
+ /* VECS can use the same length_mask function as VCS */
+ ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
+ break;
+ default:
+ DRM_ERROR("CMD: cmd_parser_init with unknown ring: %d\n",
+ ring->id);
+ BUG();
+ }
+
+ validate_cmds_sorted(ring);
+ validate_regs_sorted(ring);
+}
+
+static const struct drm_i915_cmd_descriptor*
+find_cmd_in_table(const struct drm_i915_cmd_table *table,
+ u32 cmd_header)
+{
+ int i;
+
+ for (i = 0; i < table->count; i++) {
+ const struct drm_i915_cmd_descriptor *desc = &table->table[i];
+ u32 masked_cmd = desc->cmd.mask & cmd_header;
+ u32 masked_value = desc->cmd.value & desc->cmd.mask;
+
+ if (masked_cmd == masked_value)
+ return desc;
+ }
+
+ return NULL;
+}
+
+/*
+ * Returns a pointer to a descriptor for the command specified by cmd_header.
+ *
+ * The caller must supply space for a default descriptor via the default_desc
+ * parameter. If no descriptor for the specified command exists in the ring's
+ * command parser tables, this function fills in default_desc based on the
+ * ring's default length encoding and returns default_desc.
+ */
+static const struct drm_i915_cmd_descriptor*
+find_cmd(struct intel_ring_buffer *ring,
+ u32 cmd_header,
+ struct drm_i915_cmd_descriptor *default_desc)
+{
+ u32 mask;
+ int i;
+
+ for (i = 0; i < ring->cmd_table_count; i++) {
+ const struct drm_i915_cmd_descriptor *desc;
+
+ desc = find_cmd_in_table(&ring->cmd_tables[i], cmd_header);
+ if (desc)
+ return desc;
+ }
+
+ mask = ring->get_cmd_length_mask(cmd_header);
+ if (!mask)
+ return NULL;
+
+ BUG_ON(!default_desc);
+ default_desc->flags = CMD_DESC_SKIP;
+ default_desc->length.mask = mask;
+
+ return default_desc;
+}
+
+static bool valid_reg(const u32 *table, int count, u32 addr)
+{
+ if (table && count != 0) {
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (table[i] == addr)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static u32 *vmap_batch(struct drm_i915_gem_object *obj)
+{
+ int i;
+ void *addr = NULL;
+ struct sg_page_iter sg_iter;
+ struct page **pages;
+
+ pages = drm_malloc_ab(obj->base.size >> PAGE_SHIFT, sizeof(*pages));
+ if (pages == NULL) {
+ DRM_DEBUG_DRIVER("Failed to get space for pages\n");
+ goto finish;
+ }
+
+ i = 0;
+ for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
+ pages[i] = sg_page_iter_page(&sg_iter);
+ i++;
+ }
+
+ addr = vmap(pages, i, 0, PAGE_KERNEL);
+ if (addr == NULL) {
+ DRM_DEBUG_DRIVER("Failed to vmap pages\n");
+ goto finish;
+ }
+
+finish:
+ if (pages)
+ drm_free_large(pages);
+ return (u32*)addr;
+}
+
+/**
+ * i915_needs_cmd_parser() - should a given ring use software command parsing?
+ * @ring: the ring in question
+ *
+ * Only certain platforms require software batch buffer command parsing, and
+ * only when enabled via module paramter.
+ *
+ * Return: true if the ring requires software command parsing
+ */
+bool i915_needs_cmd_parser(struct intel_ring_buffer *ring)
+{
+ /* No command tables indicates a platform without parsing */
+ if (!ring->cmd_tables)
+ return false;
+
+ return (i915.enable_cmd_parser == 1);
+}
+
+#define LENGTH_BIAS 2
+
+/**
+ * i915_parse_cmds() - parse a submitted batch buffer for privilege violations
+ * @ring: the ring on which the batch is to execute
+ * @batch_obj: the batch buffer in question
+ * @batch_start_offset: byte offset in the batch at which execution starts
+ * @is_master: is the submitting process the drm master?
+ *
+ * Parses the specified batch buffer looking for privilege violations as
+ * described in the overview.
+ *
+ * Return: non-zero if the parser finds violations or otherwise fails
+ */
+int i915_parse_cmds(struct intel_ring_buffer *ring,
+ struct drm_i915_gem_object *batch_obj,
+ u32 batch_start_offset,
+ bool is_master)
+{
+ int ret = 0;
+ u32 *cmd, *batch_base, *batch_end;
+ struct drm_i915_cmd_descriptor default_desc = { 0 };
+ int needs_clflush = 0;
+
+ ret = i915_gem_obj_prepare_shmem_read(batch_obj, &needs_clflush);
+ if (ret) {
+ DRM_DEBUG_DRIVER("CMD: failed to prep read\n");
+ return ret;
+ }
+
+ batch_base = vmap_batch(batch_obj);
+ if (!batch_base) {
+ DRM_DEBUG_DRIVER("CMD: Failed to vmap batch\n");
+ i915_gem_object_unpin_pages(batch_obj);
+ return -ENOMEM;
+ }
+
+ if (needs_clflush)
+ drm_clflush_virt_range((char *)batch_base, batch_obj->base.size);
+
+ cmd = batch_base + (batch_start_offset / sizeof(*cmd));
+ batch_end = cmd + (batch_obj->base.size / sizeof(*batch_end));
+
+ while (cmd < batch_end) {
+ const struct drm_i915_cmd_descriptor *desc;
+ u32 length;
+
+ if (*cmd == MI_BATCH_BUFFER_END)
+ break;
+
+ desc = find_cmd(ring, *cmd, &default_desc);
+ if (!desc) {
+ DRM_DEBUG_DRIVER("CMD: Unrecognized command: 0x%08X\n",
+ *cmd);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (desc->flags & CMD_DESC_FIXED)
+ length = desc->length.fixed;
+ else
+ length = ((*cmd & desc->length.mask) + LENGTH_BIAS);
+
+ if ((batch_end - cmd) < length) {
+ DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%d batchlen=%td\n",
+ *cmd,
+ length,
+ (unsigned long)(batch_end - cmd));
+ ret = -EINVAL;
+ break;
+ }
+
+ if (desc->flags & CMD_DESC_REJECT) {
+ DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd);
+ ret = -EINVAL;
+ break;
+ }
+
+ if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
+ DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
+ *cmd);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (desc->flags & CMD_DESC_REGISTER) {
+ u32 reg_addr = cmd[desc->reg.offset] & desc->reg.mask;
+
+ if (!valid_reg(ring->reg_table,
+ ring->reg_count, reg_addr)) {
+ if (!is_master ||
+ !valid_reg(ring->master_reg_table,
+ ring->master_reg_count,
+ reg_addr)) {
+ DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n",
+ reg_addr,
+ *cmd,
+ ring->id);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ }
+
+ if (desc->flags & CMD_DESC_BITMASK) {
+ int i;
+
+ for (i = 0; i < MAX_CMD_DESC_BITMASKS; i++) {
+ u32 dword;
+
+ if (desc->bits[i].mask == 0)
+ break;
+
+ dword = cmd[desc->bits[i].offset] &
+ desc->bits[i].mask;
+
+ if (dword != desc->bits[i].expected) {
+ DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n",
+ *cmd,
+ desc->bits[i].mask,
+ desc->bits[i].expected,
+ dword, ring->id);
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ if (ret)
+ break;
+ }
+
+ cmd += length;
+ }
+
+ if (cmd >= batch_end) {
+ DRM_DEBUG_DRIVER("CMD: Got to the end of the buffer w/o a BBE cmd!\n");
+ ret = -EINVAL;
+ }
+
+ vunmap(batch_base);
+
+ i915_gem_object_unpin_pages(batch_obj);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index b2b46c52294c..195fe5bc0aac 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -98,7 +98,7 @@ static const char *get_pin_flag(struct drm_i915_gem_object *obj)
{
if (obj->user_pin_count > 0)
return "P";
- else if (obj->pin_count > 0)
+ else if (i915_gem_obj_is_pinned(obj))
return "p";
else
return " ";
@@ -123,6 +123,8 @@ static void
describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
{
struct i915_vma *vma;
+ int pin_count = 0;
+
seq_printf(m, "%pK: %s%s%s %8zdKiB %02x %02x %u %u %u%s%s%s",
&obj->base,
get_pin_flag(obj),
@@ -139,8 +141,10 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
if (obj->base.name)
seq_printf(m, " (name: %d)", obj->base.name);
- if (obj->pin_count)
- seq_printf(m, " (pinned x %d)", obj->pin_count);
+ list_for_each_entry(vma, &obj->vma_list, vma_link)
+ if (vma->pin_count > 0)
+ pin_count++;
+ seq_printf(m, " (pinned x %d)", pin_count);
if (obj->pin_display)
seq_printf(m, " (display)");
if (obj->fence_reg != I915_FENCE_REG_NONE)
@@ -295,28 +299,62 @@ static int i915_gem_stolen_list_info(struct seq_file *m, void *data)
} while (0)
struct file_stats {
+ struct drm_i915_file_private *file_priv;
int count;
- size_t total, active, inactive, unbound;
+ size_t total, unbound;
+ size_t global, shared;
+ size_t active, inactive;
};
static int per_file_stats(int id, void *ptr, void *data)
{
struct drm_i915_gem_object *obj = ptr;
struct file_stats *stats = data;
+ struct i915_vma *vma;
stats->count++;
stats->total += obj->base.size;
- if (i915_gem_obj_ggtt_bound(obj)) {
- if (!list_empty(&obj->ring_list))
- stats->active += obj->base.size;
- else
- stats->inactive += obj->base.size;
+ if (obj->base.name || obj->base.dma_buf)
+ stats->shared += obj->base.size;
+
+ if (USES_FULL_PPGTT(obj->base.dev)) {
+ list_for_each_entry(vma, &obj->vma_list, vma_link) {
+ struct i915_hw_ppgtt *ppgtt;
+
+ if (!drm_mm_node_allocated(&vma->node))
+ continue;
+
+ if (i915_is_ggtt(vma->vm)) {
+ stats->global += obj->base.size;
+ continue;
+ }
+
+ ppgtt = container_of(vma->vm, struct i915_hw_ppgtt, base);
+ if (ppgtt->ctx && ppgtt->ctx->file_priv != stats->file_priv)
+ continue;
+
+ if (obj->ring) /* XXX per-vma statistic */
+ stats->active += obj->base.size;
+ else
+ stats->inactive += obj->base.size;
+
+ return 0;
+ }
} else {
- if (!list_empty(&obj->global_list))
- stats->unbound += obj->base.size;
+ if (i915_gem_obj_ggtt_bound(obj)) {
+ stats->global += obj->base.size;
+ if (obj->ring)
+ stats->active += obj->base.size;
+ else
+ stats->inactive += obj->base.size;
+ return 0;
+ }
}
+ if (!list_empty(&obj->global_list))
+ stats->unbound += obj->base.size;
+
return 0;
}
@@ -407,6 +445,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
struct task_struct *task;
memset(&stats, 0, sizeof(stats));
+ stats.file_priv = file->driver_priv;
idr_for_each(&file->object_idr, per_file_stats, &stats);
/*
* Although we have a valid reference on file->pid, that does
@@ -416,12 +455,14 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
*/
rcu_read_lock();
task = pid_task(file->pid, PIDTYPE_PID);
- seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu unbound)\n",
+ seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu shared, %zu unbound)\n",
task ? task->comm : "<unknown>",
stats.count,
stats.total,
stats.active,
stats.inactive,
+ stats.global,
+ stats.shared,
stats.unbound);
rcu_read_unlock();
}
@@ -447,7 +488,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data)
total_obj_size = total_gtt_size = count = 0;
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
- if (list == PINNED_LIST && obj->pin_count == 0)
+ if (list == PINNED_LIST && !i915_gem_obj_is_pinned(obj))
continue;
seq_puts(m, " ");
@@ -520,7 +561,7 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
struct drm_i915_gem_request *gem_request;
int ret, count, i;
@@ -565,7 +606,7 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
int ret, i;
@@ -588,7 +629,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
int ret, i, pipe;
@@ -598,7 +639,6 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
intel_runtime_pm_get(dev_priv);
if (INTEL_INFO(dev)->gen >= 8) {
- int i;
seq_printf(m, "Master Interrupt Control:\t%08x\n",
I915_READ(GEN8_MASTER_IRQ));
@@ -611,16 +651,16 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
i, I915_READ(GEN8_GT_IER(i)));
}
- for_each_pipe(i) {
+ for_each_pipe(pipe) {
seq_printf(m, "Pipe %c IMR:\t%08x\n",
- pipe_name(i),
- I915_READ(GEN8_DE_PIPE_IMR(i)));
+ pipe_name(pipe),
+ I915_READ(GEN8_DE_PIPE_IMR(pipe)));
seq_printf(m, "Pipe %c IIR:\t%08x\n",
- pipe_name(i),
- I915_READ(GEN8_DE_PIPE_IIR(i)));
+ pipe_name(pipe),
+ I915_READ(GEN8_DE_PIPE_IIR(pipe)));
seq_printf(m, "Pipe %c IER:\t%08x\n",
- pipe_name(i),
- I915_READ(GEN8_DE_PIPE_IER(i)));
+ pipe_name(pipe),
+ I915_READ(GEN8_DE_PIPE_IER(pipe)));
}
seq_printf(m, "Display Engine port interrupt mask:\t%08x\n",
@@ -712,8 +752,6 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
seq_printf(m, "Graphics Interrupt mask: %08x\n",
I915_READ(GTIMR));
}
- seq_printf(m, "Interrupts received: %d\n",
- atomic_read(&dev_priv->irq_received));
for_each_ring(ring, dev_priv, i) {
if (INTEL_INFO(dev)->gen >= 6) {
seq_printf(m,
@@ -732,7 +770,7 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int i, ret;
ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -761,7 +799,7 @@ static int i915_hws_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
const u32 *hws;
int i;
@@ -872,7 +910,7 @@ static int
i915_next_seqno_get(void *data, u64 *val)
{
struct drm_device *dev = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -909,7 +947,7 @@ static int i915_rstdby_delays(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u16 crstanddelay;
int ret;
@@ -932,7 +970,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
intel_runtime_pm_get(dev_priv);
@@ -1025,7 +1063,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
max_freq * GT_FREQUENCY_MULTIPLIER);
seq_printf(m, "Max overclocked frequency: %dMHz\n",
- dev_priv->rps.hw_max * GT_FREQUENCY_MULTIPLIER);
+ dev_priv->rps.max_freq * GT_FREQUENCY_MULTIPLIER);
} else if (IS_VALLEYVIEW(dev)) {
u32 freq_sts, val;
@@ -1058,7 +1096,7 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 delayfreq;
int ret, i;
@@ -1089,7 +1127,7 @@ static int i915_inttoext_table(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 inttoext;
int ret, i;
@@ -1113,7 +1151,7 @@ static int ironlake_drpc_info(struct seq_file *m)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 rgvmodectl, rstdbyctl;
u16 crstandvid;
int ret;
@@ -1339,13 +1377,15 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (!HAS_FBC(dev)) {
seq_puts(m, "FBC unsupported on this chipset\n");
return 0;
}
+ intel_runtime_pm_get(dev_priv);
+
if (intel_fbc_enabled(dev)) {
seq_puts(m, "FBC enabled\n");
} else {
@@ -1389,6 +1429,9 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
}
seq_putc(m, '\n');
}
+
+ intel_runtime_pm_put(dev_priv);
+
return 0;
}
@@ -1403,11 +1446,15 @@ static int i915_ips_status(struct seq_file *m, void *unused)
return 0;
}
+ intel_runtime_pm_get(dev_priv);
+
if (IS_BROADWELL(dev) || I915_READ(IPS_CTL) & IPS_ENABLE)
seq_puts(m, "enabled\n");
else
seq_puts(m, "disabled\n");
+ intel_runtime_pm_put(dev_priv);
+
return 0;
}
@@ -1415,9 +1462,11 @@ static int i915_sr_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
bool sr_enabled = false;
+ intel_runtime_pm_get(dev_priv);
+
if (HAS_PCH_SPLIT(dev))
sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN;
else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev))
@@ -1427,6 +1476,8 @@ static int i915_sr_status(struct seq_file *m, void *unused)
else if (IS_PINEVIEW(dev))
sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN;
+ intel_runtime_pm_put(dev_priv);
+
seq_printf(m, "self-refresh: %s\n",
sr_enabled ? "enabled" : "disabled");
@@ -1437,7 +1488,7 @@ static int i915_emon_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long temp, chipset, gfx;
int ret;
@@ -1465,8 +1516,8 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- int ret;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret = 0;
int gpu_freq, ia_freq;
if (!(IS_GEN6(dev) || IS_GEN7(dev))) {
@@ -1474,17 +1525,18 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
return 0;
}
+ intel_runtime_pm_get(dev_priv);
+
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
if (ret)
- return ret;
- intel_runtime_pm_get(dev_priv);
+ goto out;
seq_puts(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\tEffective Ring freq (MHz)\n");
- for (gpu_freq = dev_priv->rps.min_delay;
- gpu_freq <= dev_priv->rps.max_delay;
+ for (gpu_freq = dev_priv->rps.min_freq_softlimit;
+ gpu_freq <= dev_priv->rps.max_freq_softlimit;
gpu_freq++) {
ia_freq = gpu_freq;
sandybridge_pcode_read(dev_priv,
@@ -1496,17 +1548,18 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
((ia_freq >> 8) & 0xff) * 100);
}
- intel_runtime_pm_put(dev_priv);
mutex_unlock(&dev_priv->rps.hw_lock);
- return 0;
+out:
+ intel_runtime_pm_put(dev_priv);
+ return ret;
}
static int i915_gfxec(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -1526,7 +1579,7 @@ static int i915_opregion(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
void *data = kmalloc(OPREGION_SIZE, GFP_KERNEL);
int ret;
@@ -1600,7 +1653,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
struct i915_hw_context *ctx;
int ret, i;
@@ -1733,6 +1786,17 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
return 0;
}
+static int per_file_ctx(int id, void *ptr, void *data)
+{
+ struct i915_hw_context *ctx = ptr;
+ struct seq_file *m = data;
+ struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(ctx);
+
+ ppgtt->debug_dump(ppgtt, m);
+
+ return 0;
+}
+
static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1744,7 +1808,7 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
return;
seq_printf(m, "Page directories: %d\n", ppgtt->num_pd_pages);
- seq_printf(m, "Page tables: %d\n", ppgtt->num_pt_pages);
+ seq_printf(m, "Page tables: %d\n", ppgtt->num_pd_entries);
for_each_ring(ring, dev_priv, unused) {
seq_printf(m, "%s\n", ring->name);
for (i = 0; i < 4; i++) {
@@ -1762,6 +1826,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
+ struct drm_file *file;
int i;
if (INTEL_INFO(dev)->gen == 6)
@@ -1780,6 +1845,20 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
seq_puts(m, "aliasing PPGTT:\n");
seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset);
+
+ ppgtt->debug_dump(ppgtt, m);
+ } else
+ return;
+
+ list_for_each_entry_reverse(file, &dev->filelist, lhead) {
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct i915_hw_ppgtt *pvt_ppgtt;
+
+ pvt_ppgtt = ctx_to_ppgtt(file_priv->private_default_ctx);
+ seq_printf(m, "proc: %s\n",
+ get_pid_task(file->pid, PIDTYPE_PID)->comm);
+ seq_puts(m, " default context:\n");
+ idr_for_each(&file_priv->context_idr, per_file_ctx, m);
}
seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK));
}
@@ -1892,6 +1971,47 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
return 0;
}
+static int i915_sink_crc(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct intel_encoder *encoder;
+ struct intel_connector *connector;
+ struct intel_dp *intel_dp = NULL;
+ int ret;
+ u8 crc[6];
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list,
+ base.head) {
+
+ if (connector->base.dpms != DRM_MODE_DPMS_ON)
+ continue;
+
+ if (!connector->base.encoder)
+ continue;
+
+ encoder = to_intel_encoder(connector->base.encoder);
+ if (encoder->type != INTEL_OUTPUT_EDP)
+ continue;
+
+ intel_dp = enc_to_intel_dp(&encoder->base);
+
+ ret = intel_dp_sink_crc(intel_dp, crc);
+ if (ret)
+ goto out;
+
+ seq_printf(m, "%02x%02x%02x%02x%02x%02x\n",
+ crc[0], crc[1], crc[2],
+ crc[3], crc[4], crc[5]);
+ goto out;
+ }
+ ret = -ENODEV;
+out:
+ drm_modeset_unlock_all(dev);
+ return ret;
+}
+
static int i915_energy_uJ(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
@@ -1903,12 +2023,16 @@ static int i915_energy_uJ(struct seq_file *m, void *data)
if (INTEL_INFO(dev)->gen < 6)
return -ENODEV;
+ intel_runtime_pm_get(dev_priv);
+
rdmsrl(MSR_RAPL_POWER_UNIT, power);
power = (power & 0x1f00) >> 8;
units = 1000000 / (1 << power); /* convert to uJ */
power = I915_READ(MCH_SECP_NRG_STTS);
power *= units;
+ intel_runtime_pm_put(dev_priv);
+
seq_printf(m, "%llu", (long long unsigned)power);
return 0;
@@ -1925,15 +2049,9 @@ static int i915_pc8_status(struct seq_file *m, void *unused)
return 0;
}
- mutex_lock(&dev_priv->pc8.lock);
- seq_printf(m, "Requirements met: %s\n",
- yesno(dev_priv->pc8.requirements_met));
- seq_printf(m, "GPU idle: %s\n", yesno(dev_priv->pc8.gpu_idle));
- seq_printf(m, "Disable count: %d\n", dev_priv->pc8.disable_count);
+ seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy));
seq_printf(m, "IRQs disabled: %s\n",
- yesno(dev_priv->pc8.irqs_disabled));
- seq_printf(m, "Enabled: %s\n", yesno(dev_priv->pc8.enabled));
- mutex_unlock(&dev_priv->pc8.lock);
+ yesno(dev_priv->pm.irqs_disabled));
return 0;
}
@@ -1961,6 +2079,28 @@ static const char *power_domain_str(enum intel_display_power_domain domain)
return "TRANSCODER_C";
case POWER_DOMAIN_TRANSCODER_EDP:
return "TRANSCODER_EDP";
+ case POWER_DOMAIN_PORT_DDI_A_2_LANES:
+ return "PORT_DDI_A_2_LANES";
+ case POWER_DOMAIN_PORT_DDI_A_4_LANES:
+ return "PORT_DDI_A_4_LANES";
+ case POWER_DOMAIN_PORT_DDI_B_2_LANES:
+ return "PORT_DDI_B_2_LANES";
+ case POWER_DOMAIN_PORT_DDI_B_4_LANES:
+ return "PORT_DDI_B_4_LANES";
+ case POWER_DOMAIN_PORT_DDI_C_2_LANES:
+ return "PORT_DDI_C_2_LANES";
+ case POWER_DOMAIN_PORT_DDI_C_4_LANES:
+ return "PORT_DDI_C_4_LANES";
+ case POWER_DOMAIN_PORT_DDI_D_2_LANES:
+ return "PORT_DDI_D_2_LANES";
+ case POWER_DOMAIN_PORT_DDI_D_4_LANES:
+ return "PORT_DDI_D_4_LANES";
+ case POWER_DOMAIN_PORT_DSI:
+ return "PORT_DSI";
+ case POWER_DOMAIN_PORT_CRT:
+ return "PORT_CRT";
+ case POWER_DOMAIN_PORT_OTHER:
+ return "PORT_OTHER";
case POWER_DOMAIN_VGA:
return "VGA";
case POWER_DOMAIN_AUDIO:
@@ -2008,6 +2148,215 @@ static int i915_power_domain_info(struct seq_file *m, void *unused)
return 0;
}
+static void intel_seq_print_mode(struct seq_file *m, int tabs,
+ struct drm_display_mode *mode)
+{
+ int i;
+
+ for (i = 0; i < tabs; i++)
+ seq_putc(m, '\t');
+
+ seq_printf(m, "id %d:\"%s\" freq %d clock %d hdisp %d hss %d hse %d htot %d vdisp %d vss %d vse %d vtot %d type 0x%x flags 0x%x\n",
+ mode->base.id, mode->name,
+ mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal,
+ mode->type, mode->flags);
+}
+
+static void intel_encoder_info(struct seq_file *m,
+ struct intel_crtc *intel_crtc,
+ struct intel_encoder *intel_encoder)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_crtc *crtc = &intel_crtc->base;
+ struct intel_connector *intel_connector;
+ struct drm_encoder *encoder;
+
+ encoder = &intel_encoder->base;
+ seq_printf(m, "\tencoder %d: type: %s, connectors:\n",
+ encoder->base.id, drm_get_encoder_name(encoder));
+ for_each_connector_on_encoder(dev, encoder, intel_connector) {
+ struct drm_connector *connector = &intel_connector->base;
+ seq_printf(m, "\t\tconnector %d: type: %s, status: %s",
+ connector->base.id,
+ drm_get_connector_name(connector),
+ drm_get_connector_status_name(connector->status));
+ if (connector->status == connector_status_connected) {
+ struct drm_display_mode *mode = &crtc->mode;
+ seq_printf(m, ", mode:\n");
+ intel_seq_print_mode(m, 2, mode);
+ } else {
+ seq_putc(m, '\n');
+ }
+ }
+}
+
+static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_crtc *crtc = &intel_crtc->base;
+ struct intel_encoder *intel_encoder;
+
+ seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n",
+ crtc->primary->fb->base.id, crtc->x, crtc->y,
+ crtc->primary->fb->width, crtc->primary->fb->height);
+ for_each_encoder_on_crtc(dev, crtc, intel_encoder)
+ intel_encoder_info(m, intel_crtc, intel_encoder);
+}
+
+static void intel_panel_info(struct seq_file *m, struct intel_panel *panel)
+{
+ struct drm_display_mode *mode = panel->fixed_mode;
+
+ seq_printf(m, "\tfixed mode:\n");
+ intel_seq_print_mode(m, 2, mode);
+}
+
+static void intel_dp_info(struct seq_file *m,
+ struct intel_connector *intel_connector)
+{
+ struct intel_encoder *intel_encoder = intel_connector->encoder;
+ struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
+
+ seq_printf(m, "\tDPCD rev: %x\n", intel_dp->dpcd[DP_DPCD_REV]);
+ seq_printf(m, "\taudio support: %s\n", intel_dp->has_audio ? "yes" :
+ "no");
+ if (intel_encoder->type == INTEL_OUTPUT_EDP)
+ intel_panel_info(m, &intel_connector->panel);
+}
+
+static void intel_hdmi_info(struct seq_file *m,
+ struct intel_connector *intel_connector)
+{
+ struct intel_encoder *intel_encoder = intel_connector->encoder;
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
+
+ seq_printf(m, "\taudio support: %s\n", intel_hdmi->has_audio ? "yes" :
+ "no");
+}
+
+static void intel_lvds_info(struct seq_file *m,
+ struct intel_connector *intel_connector)
+{
+ intel_panel_info(m, &intel_connector->panel);
+}
+
+static void intel_connector_info(struct seq_file *m,
+ struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_encoder *intel_encoder = intel_connector->encoder;
+ struct drm_display_mode *mode;
+
+ seq_printf(m, "connector %d: type %s, status: %s\n",
+ connector->base.id, drm_get_connector_name(connector),
+ drm_get_connector_status_name(connector->status));
+ if (connector->status == connector_status_connected) {
+ seq_printf(m, "\tname: %s\n", connector->display_info.name);
+ seq_printf(m, "\tphysical dimensions: %dx%dmm\n",
+ connector->display_info.width_mm,
+ connector->display_info.height_mm);
+ seq_printf(m, "\tsubpixel order: %s\n",
+ drm_get_subpixel_order_name(connector->display_info.subpixel_order));
+ seq_printf(m, "\tCEA rev: %d\n",
+ connector->display_info.cea_rev);
+ }
+ if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
+ intel_encoder->type == INTEL_OUTPUT_EDP)
+ intel_dp_info(m, intel_connector);
+ else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
+ intel_hdmi_info(m, intel_connector);
+ else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
+ intel_lvds_info(m, intel_connector);
+
+ seq_printf(m, "\tmodes:\n");
+ list_for_each_entry(mode, &connector->modes, head)
+ intel_seq_print_mode(m, 2, mode);
+}
+
+static bool cursor_active(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 state;
+
+ if (IS_845G(dev) || IS_I865G(dev))
+ state = I915_READ(_CURACNTR) & CURSOR_ENABLE;
+ else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev))
+ state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
+ else
+ state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE;
+
+ return state;
+}
+
+static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pos;
+
+ if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
+ pos = I915_READ(CURPOS_IVB(pipe));
+ else
+ pos = I915_READ(CURPOS(pipe));
+
+ *x = (pos >> CURSOR_X_SHIFT) & CURSOR_POS_MASK;
+ if (pos & (CURSOR_POS_SIGN << CURSOR_X_SHIFT))
+ *x = -*x;
+
+ *y = (pos >> CURSOR_Y_SHIFT) & CURSOR_POS_MASK;
+ if (pos & (CURSOR_POS_SIGN << CURSOR_Y_SHIFT))
+ *y = -*y;
+
+ return cursor_active(dev, pipe);
+}
+
+static int i915_display_info(struct seq_file *m, void *unused)
+{
+ 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;
+ struct intel_crtc *crtc;
+ struct drm_connector *connector;
+
+ intel_runtime_pm_get(dev_priv);
+ drm_modeset_lock_all(dev);
+ seq_printf(m, "CRTC info\n");
+ seq_printf(m, "---------\n");
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ bool active;
+ int x, y;
+
+ seq_printf(m, "CRTC %d: pipe: %c, active: %s\n",
+ crtc->base.base.id, pipe_name(crtc->pipe),
+ yesno(crtc->active));
+ if (crtc->active) {
+ intel_crtc_info(m, crtc);
+
+ active = cursor_position(dev, crtc->pipe, &x, &y);
+ seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n",
+ yesno(crtc->cursor_visible),
+ x, y, crtc->cursor_addr,
+ yesno(active));
+ }
+ }
+
+ seq_printf(m, "\n");
+ seq_printf(m, "Connector info\n");
+ seq_printf(m, "--------------\n");
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ intel_connector_info(m, connector);
+ }
+ drm_modeset_unlock_all(dev);
+ intel_runtime_pm_put(dev_priv);
+
+ return 0;
+}
+
struct pipe_crc_info {
const char *name;
struct drm_device *dev;
@@ -2332,8 +2681,6 @@ static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
if (need_stable_symbols) {
uint32_t tmp = I915_READ(PORT_DFT2_G4X);
- WARN_ON(!IS_G4X(dev));
-
tmp |= DC_BALANCE_RESET_VLV;
if (pipe == PIPE_A)
tmp |= PIPE_A_SCRAMBLE_RESET;
@@ -2756,11 +3103,179 @@ static const struct file_operations i915_display_crc_ctl_fops = {
.write = display_crc_ctl_write
};
+static void wm_latency_show(struct seq_file *m, const uint16_t wm[5])
+{
+ struct drm_device *dev = m->private;
+ int num_levels = IS_HASWELL(dev) || IS_BROADWELL(dev) ? 5 : 4;
+ int level;
+
+ drm_modeset_lock_all(dev);
+
+ for (level = 0; level < num_levels; level++) {
+ unsigned int latency = wm[level];
+
+ /* WM1+ latency values in 0.5us units */
+ if (level > 0)
+ latency *= 5;
+
+ seq_printf(m, "WM%d %u (%u.%u usec)\n",
+ level, wm[level],
+ latency / 10, latency % 10);
+ }
+
+ drm_modeset_unlock_all(dev);
+}
+
+static int pri_wm_latency_show(struct seq_file *m, void *data)
+{
+ struct drm_device *dev = m->private;
+
+ wm_latency_show(m, to_i915(dev)->wm.pri_latency);
+
+ return 0;
+}
+
+static int spr_wm_latency_show(struct seq_file *m, void *data)
+{
+ struct drm_device *dev = m->private;
+
+ wm_latency_show(m, to_i915(dev)->wm.spr_latency);
+
+ return 0;
+}
+
+static int cur_wm_latency_show(struct seq_file *m, void *data)
+{
+ struct drm_device *dev = m->private;
+
+ wm_latency_show(m, to_i915(dev)->wm.cur_latency);
+
+ return 0;
+}
+
+static int pri_wm_latency_open(struct inode *inode, struct file *file)
+{
+ struct drm_device *dev = inode->i_private;
+
+ if (!HAS_PCH_SPLIT(dev))
+ return -ENODEV;
+
+ return single_open(file, pri_wm_latency_show, dev);
+}
+
+static int spr_wm_latency_open(struct inode *inode, struct file *file)
+{
+ struct drm_device *dev = inode->i_private;
+
+ if (!HAS_PCH_SPLIT(dev))
+ return -ENODEV;
+
+ return single_open(file, spr_wm_latency_show, dev);
+}
+
+static int cur_wm_latency_open(struct inode *inode, struct file *file)
+{
+ struct drm_device *dev = inode->i_private;
+
+ if (!HAS_PCH_SPLIT(dev))
+ return -ENODEV;
+
+ return single_open(file, cur_wm_latency_show, dev);
+}
+
+static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp, uint16_t wm[5])
+{
+ struct seq_file *m = file->private_data;
+ struct drm_device *dev = m->private;
+ uint16_t new[5] = { 0 };
+ int num_levels = IS_HASWELL(dev) || IS_BROADWELL(dev) ? 5 : 4;
+ int level;
+ int ret;
+ char tmp[32];
+
+ if (len >= sizeof(tmp))
+ return -EINVAL;
+
+ if (copy_from_user(tmp, ubuf, len))
+ return -EFAULT;
+
+ tmp[len] = '\0';
+
+ ret = sscanf(tmp, "%hu %hu %hu %hu %hu", &new[0], &new[1], &new[2], &new[3], &new[4]);
+ if (ret != num_levels)
+ return -EINVAL;
+
+ drm_modeset_lock_all(dev);
+
+ for (level = 0; level < num_levels; level++)
+ wm[level] = new[level];
+
+ drm_modeset_unlock_all(dev);
+
+ return len;
+}
+
+
+static ssize_t pri_wm_latency_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct drm_device *dev = m->private;
+
+ return wm_latency_write(file, ubuf, len, offp, to_i915(dev)->wm.pri_latency);
+}
+
+static ssize_t spr_wm_latency_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct drm_device *dev = m->private;
+
+ return wm_latency_write(file, ubuf, len, offp, to_i915(dev)->wm.spr_latency);
+}
+
+static ssize_t cur_wm_latency_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct drm_device *dev = m->private;
+
+ return wm_latency_write(file, ubuf, len, offp, to_i915(dev)->wm.cur_latency);
+}
+
+static const struct file_operations i915_pri_wm_latency_fops = {
+ .owner = THIS_MODULE,
+ .open = pri_wm_latency_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = pri_wm_latency_write
+};
+
+static const struct file_operations i915_spr_wm_latency_fops = {
+ .owner = THIS_MODULE,
+ .open = spr_wm_latency_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = spr_wm_latency_write
+};
+
+static const struct file_operations i915_cur_wm_latency_fops = {
+ .owner = THIS_MODULE,
+ .open = cur_wm_latency_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cur_wm_latency_write
+};
+
static int
i915_wedged_get(void *data, u64 *val)
{
struct drm_device *dev = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
*val = atomic_read(&dev_priv->gpu_error.reset_counter);
@@ -2772,9 +3287,8 @@ i915_wedged_set(void *data, u64 val)
{
struct drm_device *dev = data;
- DRM_INFO("Manually setting wedged to %llu\n", val);
- i915_handle_error(dev, val);
-
+ i915_handle_error(dev, val,
+ "Manually setting wedged to %llu", val);
return 0;
}
@@ -2786,7 +3300,7 @@ static int
i915_ring_stop_get(void *data, u64 *val)
{
struct drm_device *dev = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
*val = dev_priv->gpu_error.stop_rings;
@@ -2929,7 +3443,7 @@ i915_drop_caches_set(void *data, u64 val)
list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
list_for_each_entry_safe(vma, x, &vm->inactive_list,
mm_list) {
- if (vma->obj->pin_count)
+ if (vma->pin_count)
continue;
ret = i915_vma_unbind(vma);
@@ -2963,7 +3477,7 @@ static int
i915_max_freq_get(void *data, u64 *val)
{
struct drm_device *dev = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
@@ -2976,9 +3490,9 @@ i915_max_freq_get(void *data, u64 *val)
return ret;
if (IS_VALLEYVIEW(dev))
- *val = vlv_gpu_freq(dev_priv, dev_priv->rps.max_delay);
+ *val = vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit);
else
- *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
+ *val = dev_priv->rps.max_freq_softlimit * GT_FREQUENCY_MULTIPLIER;
mutex_unlock(&dev_priv->rps.hw_lock);
return 0;
@@ -2989,6 +3503,7 @@ i915_max_freq_set(void *data, u64 val)
{
struct drm_device *dev = data;
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 rp_state_cap, hw_max, hw_min;
int ret;
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
@@ -3007,14 +3522,29 @@ i915_max_freq_set(void *data, u64 val)
*/
if (IS_VALLEYVIEW(dev)) {
val = vlv_freq_opcode(dev_priv, val);
- dev_priv->rps.max_delay = val;
- valleyview_set_rps(dev, val);
+
+ hw_max = valleyview_rps_max_freq(dev_priv);
+ hw_min = valleyview_rps_min_freq(dev_priv);
} else {
do_div(val, GT_FREQUENCY_MULTIPLIER);
- dev_priv->rps.max_delay = val;
- gen6_set_rps(dev, val);
+
+ rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+ hw_max = dev_priv->rps.max_freq;
+ hw_min = (rp_state_cap >> 16) & 0xff;
+ }
+
+ if (val < hw_min || val > hw_max || val < dev_priv->rps.min_freq_softlimit) {
+ mutex_unlock(&dev_priv->rps.hw_lock);
+ return -EINVAL;
}
+ dev_priv->rps.max_freq_softlimit = val;
+
+ if (IS_VALLEYVIEW(dev))
+ valleyview_set_rps(dev, val);
+ else
+ gen6_set_rps(dev, val);
+
mutex_unlock(&dev_priv->rps.hw_lock);
return 0;
@@ -3028,7 +3558,7 @@ static int
i915_min_freq_get(void *data, u64 *val)
{
struct drm_device *dev = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
@@ -3041,9 +3571,9 @@ i915_min_freq_get(void *data, u64 *val)
return ret;
if (IS_VALLEYVIEW(dev))
- *val = vlv_gpu_freq(dev_priv, dev_priv->rps.min_delay);
+ *val = vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit);
else
- *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
+ *val = dev_priv->rps.min_freq_softlimit * GT_FREQUENCY_MULTIPLIER;
mutex_unlock(&dev_priv->rps.hw_lock);
return 0;
@@ -3054,6 +3584,7 @@ i915_min_freq_set(void *data, u64 val)
{
struct drm_device *dev = data;
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 rp_state_cap, hw_max, hw_min;
int ret;
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
@@ -3072,13 +3603,29 @@ i915_min_freq_set(void *data, u64 val)
*/
if (IS_VALLEYVIEW(dev)) {
val = vlv_freq_opcode(dev_priv, val);
- dev_priv->rps.min_delay = val;
- valleyview_set_rps(dev, val);
+
+ hw_max = valleyview_rps_max_freq(dev_priv);
+ hw_min = valleyview_rps_min_freq(dev_priv);
} else {
do_div(val, GT_FREQUENCY_MULTIPLIER);
- dev_priv->rps.min_delay = val;
- gen6_set_rps(dev, val);
+
+ rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+ hw_max = dev_priv->rps.max_freq;
+ hw_min = (rp_state_cap >> 16) & 0xff;
+ }
+
+ if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) {
+ mutex_unlock(&dev_priv->rps.hw_lock);
+ return -EINVAL;
}
+
+ dev_priv->rps.min_freq_softlimit = val;
+
+ if (IS_VALLEYVIEW(dev))
+ valleyview_set_rps(dev, val);
+ else
+ gen6_set_rps(dev, val);
+
mutex_unlock(&dev_priv->rps.hw_lock);
return 0;
@@ -3092,7 +3639,7 @@ static int
i915_cache_sharing_get(void *data, u64 *val)
{
struct drm_device *dev = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 snpcr;
int ret;
@@ -3152,7 +3699,6 @@ static int i915_forcewake_open(struct inode *inode, struct file *file)
if (INTEL_INFO(dev)->gen < 6)
return 0;
- intel_runtime_pm_get(dev_priv);
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
return 0;
@@ -3167,7 +3713,6 @@ static int i915_forcewake_release(struct inode *inode, struct file *file)
return 0;
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
- intel_runtime_pm_put(dev_priv);
return 0;
}
@@ -3248,9 +3793,11 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_dpio", i915_dpio_info, 0},
{"i915_llc", i915_llc, 0},
{"i915_edp_psr_status", i915_edp_psr_status, 0},
+ {"i915_sink_crc_eDP1", i915_sink_crc, 0},
{"i915_energy_uJ", i915_energy_uJ, 0},
{"i915_pc8_status", i915_pc8_status, 0},
{"i915_power_domain_info", i915_power_domain_info, 0},
+ {"i915_display_info", i915_display_info, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
@@ -3269,6 +3816,9 @@ static const struct i915_debugfs_files {
{"i915_error_state", &i915_error_state_fops},
{"i915_next_seqno", &i915_next_seqno_fops},
{"i915_display_crc_ctl", &i915_display_crc_ctl_fops},
+ {"i915_pri_wm_latency", &i915_pri_wm_latency_fops},
+ {"i915_spr_wm_latency", &i915_spr_wm_latency_fops},
+ {"i915_cur_wm_latency", &i915_cur_wm_latency_fops},
};
void intel_display_crc_init(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 15a74f979b4b..96177eec0a0e 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -82,7 +82,7 @@ intel_read_legacy_status_page(struct drm_i915_private *dev_priv, int reg)
void i915_update_dri1_breadcrumb(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv;
/*
@@ -103,7 +103,7 @@ void i915_update_dri1_breadcrumb(struct drm_device *dev)
static void i915_write_hws_pga(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 addr;
addr = dev_priv->status_page_dmah->busaddr;
@@ -118,7 +118,7 @@ static void i915_write_hws_pga(struct drm_device *dev)
*/
static void i915_free_hws(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = LP_RING(dev_priv);
if (dev_priv->status_page_dmah) {
@@ -137,7 +137,7 @@ static void i915_free_hws(struct drm_device *dev)
void i915_kernel_lost_context(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv;
struct intel_ring_buffer *ring = LP_RING(dev_priv);
@@ -164,7 +164,7 @@ void i915_kernel_lost_context(struct drm_device * dev)
static int i915_dma_cleanup(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int i;
/* Make sure interrupts are disabled here because the uninstall ioctl
@@ -188,7 +188,7 @@ static int i915_dma_cleanup(struct drm_device * dev)
static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
int ret;
@@ -233,7 +233,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
static int i915_dma_resume(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = LP_RING(dev_priv);
DRM_DEBUG_DRIVER("%s\n", __func__);
@@ -357,7 +357,7 @@ static int validate_cmd(int cmd)
static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int i, ret;
if ((dwords+1) * sizeof(int) >= LP_RING(dev_priv)->size - 8)
@@ -431,7 +431,7 @@ i915_emit_box(struct drm_device *dev,
static void i915_emit_breadcrumb(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
dev_priv->dri1.counter++;
@@ -547,7 +547,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
static int i915_dispatch_flip(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv =
dev->primary->master->driver_priv;
int ret;
@@ -625,10 +625,9 @@ static int i915_flush_ioctl(struct drm_device *dev, void *data,
static int i915_batchbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
- drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- master_priv->sarea_priv;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
+ drm_i915_sarea_t *sarea_priv;
drm_i915_batchbuffer_t *batch = data;
int ret;
struct drm_clip_rect *cliprects = NULL;
@@ -636,6 +635,9 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
if (drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
+ master_priv = dev->primary->master->driver_priv;
+ sarea_priv = (drm_i915_sarea_t *) master_priv->sarea_priv;
+
if (!dev_priv->dri1.allow_batchbuffer) {
DRM_ERROR("Batchbuffer ioctl disabled\n");
return -EINVAL;
@@ -681,10 +683,9 @@ fail_free:
static int i915_cmdbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
- drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- master_priv->sarea_priv;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
+ drm_i915_sarea_t *sarea_priv;
drm_i915_cmdbuffer_t *cmdbuf = data;
struct drm_clip_rect *cliprects = NULL;
void *batch_data;
@@ -696,6 +697,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
if (drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
+ master_priv = dev->primary->master->driver_priv;
+ sarea_priv = (drm_i915_sarea_t *) master_priv->sarea_priv;
+
RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
if (cmdbuf->num_cliprects < 0)
@@ -749,7 +753,7 @@ fail_batch_free:
static int i915_emit_irq(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
i915_kernel_lost_context(dev);
@@ -775,7 +779,7 @@ static int i915_emit_irq(struct drm_device * dev)
static int i915_wait_irq(struct drm_device * dev, int irq_nr)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
int ret = 0;
struct intel_ring_buffer *ring = LP_RING(dev_priv);
@@ -812,7 +816,7 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
static int i915_irq_emit(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_irq_emit_t *emit = data;
int result;
@@ -843,7 +847,7 @@ static int i915_irq_emit(struct drm_device *dev, void *data,
static int i915_irq_wait(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_irq_wait_t *irqwait = data;
if (drm_core_check_feature(dev, DRIVER_MODESET))
@@ -860,7 +864,7 @@ static int i915_irq_wait(struct drm_device *dev, void *data,
static int i915_vblank_pipe_get(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_vblank_pipe_t *pipe = data;
if (drm_core_check_feature(dev, DRIVER_MODESET))
@@ -921,7 +925,7 @@ static int i915_flip_bufs(struct drm_device *dev, void *data,
static int i915_getparam(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_getparam_t *param = data;
int value;
@@ -990,7 +994,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
value = HAS_WT(dev);
break;
case I915_PARAM_HAS_ALIASING_PPGTT:
- value = dev_priv->mm.aliasing_ppgtt ? 1 : 0;
+ value = dev_priv->mm.aliasing_ppgtt || USES_FULL_PPGTT(dev);
break;
case I915_PARAM_HAS_WAIT_TIMEOUT:
value = 1;
@@ -1029,7 +1033,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
static int i915_setparam(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_setparam_t *param = data;
if (!dev_priv) {
@@ -1064,7 +1068,7 @@ static int i915_setparam(struct drm_device *dev, void *data,
static int i915_set_status_page(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_hws_addr_t *hws = data;
struct intel_ring_buffer *ring;
@@ -1132,7 +1136,7 @@ static int i915_get_bridge_dev(struct drm_device *dev)
static int
intel_alloc_mchbar_resource(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp_lo, temp_hi = 0;
u64 mchbar_addr;
@@ -1178,11 +1182,14 @@ intel_alloc_mchbar_resource(struct drm_device *dev)
static void
intel_setup_mchbar(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp;
bool enabled;
+ if (IS_VALLEYVIEW(dev))
+ return;
+
dev_priv->mchbar_need_disable = false;
if (IS_I915G(dev) || IS_I915GM(dev)) {
@@ -1215,7 +1222,7 @@ intel_setup_mchbar(struct drm_device *dev)
static void
intel_teardown_mchbar(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp;
@@ -1317,12 +1324,12 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto cleanup_vga_switcheroo;
+ intel_power_domains_init_hw(dev_priv);
+
ret = drm_irq_install(dev);
if (ret)
goto cleanup_gem_stolen;
- intel_power_domains_init_hw(dev);
-
/* Important: The output setup functions called by modeset_init need
* working irqs for e.g. gmbus and dp aux transfers. */
intel_modeset_init(dev);
@@ -1339,7 +1346,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
/* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = true;
if (INTEL_INFO(dev)->num_pipes == 0) {
- intel_display_power_put(dev, POWER_DOMAIN_VGA);
+ intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
return 0;
}
@@ -1374,10 +1381,10 @@ cleanup_gem:
i915_gem_cleanup_ringbuffer(dev);
i915_gem_context_fini(dev);
mutex_unlock(&dev->struct_mutex);
- i915_gem_cleanup_aliasing_ppgtt(dev);
+ WARN_ON(dev_priv->mm.aliasing_ppgtt);
drm_mm_takedown(&dev_priv->gtt.base.mm);
cleanup_power:
- intel_display_power_put(dev, POWER_DOMAIN_VGA);
+ intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
drm_irq_uninstall(dev);
cleanup_gem_stolen:
i915_gem_cleanup_stolen(dev);
@@ -1442,7 +1449,7 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
static void i915_dump_device_info(struct drm_i915_private *dev_priv)
{
- const struct intel_device_info *info = dev_priv->info;
+ const struct intel_device_info *info = &dev_priv->info;
#define PRINT_S(name) "%s"
#define SEP_EMPTY
@@ -1459,6 +1466,62 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv)
#undef SEP_COMMA
}
+/*
+ * Determine various intel_device_info fields at runtime.
+ *
+ * Use it when either:
+ * - it's judged too laborious to fill n static structures with the limit
+ * when a simple if statement does the job,
+ * - run-time checks (eg read fuse/strap registers) are needed.
+ *
+ * This function needs to be called:
+ * - after the MMIO has been setup as we are reading registers,
+ * - after the PCH has been detected,
+ * - before the first usage of the fields it can tweak.
+ */
+static void intel_device_info_runtime_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_device_info *info;
+ enum pipe pipe;
+
+ info = (struct intel_device_info *)&dev_priv->info;
+
+ if (IS_VALLEYVIEW(dev))
+ for_each_pipe(pipe)
+ info->num_sprites[pipe] = 2;
+ else
+ for_each_pipe(pipe)
+ info->num_sprites[pipe] = 1;
+
+ if (i915.disable_display) {
+ DRM_INFO("Display disabled (module parameter)\n");
+ info->num_pipes = 0;
+ } else if (info->num_pipes > 0 &&
+ (INTEL_INFO(dev)->gen == 7 || INTEL_INFO(dev)->gen == 8) &&
+ !IS_VALLEYVIEW(dev)) {
+ u32 fuse_strap = I915_READ(FUSE_STRAP);
+ u32 sfuse_strap = I915_READ(SFUSE_STRAP);
+
+ /*
+ * SFUSE_STRAP is supposed to have a bit signalling the display
+ * is fused off. Unfortunately it seems that, at least in
+ * certain cases, fused off display means that PCH display
+ * reads don't land anywhere. In that case, we read 0s.
+ *
+ * On CPT/PPT, we can detect this case as SFUSE_STRAP_FUSE_LOCK
+ * should be set when taking over after the firmware.
+ */
+ if (fuse_strap & ILK_INTERNAL_DISPLAY_DISABLE ||
+ sfuse_strap & SFUSE_STRAP_DISPLAY_DISABLED ||
+ (dev_priv->pch_type == PCH_CPT &&
+ !(sfuse_strap & SFUSE_STRAP_FUSE_LOCK))) {
+ DRM_INFO("Display fused off, disabling\n");
+ info->num_pipes = 0;
+ }
+ }
+}
+
/**
* i915_driver_load - setup chip and create an initial config
* @dev: DRM device
@@ -1473,7 +1536,7 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv)
int i915_driver_load(struct drm_device *dev, unsigned long flags)
{
struct drm_i915_private *dev_priv;
- struct intel_device_info *info;
+ struct intel_device_info *info, *device_info;
int ret = 0, mmio_bar, mmio_size;
uint32_t aperture_size;
@@ -1496,7 +1559,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = (void *)dev_priv;
dev_priv->dev = dev;
- dev_priv->info = info;
+
+ /* copy initial configuration to dev_priv->info */
+ device_info = (struct intel_device_info *)&dev_priv->info;
+ *device_info = *info;
spin_lock_init(&dev_priv->irq_lock);
spin_lock_init(&dev_priv->gpu_error.lock);
@@ -1545,8 +1611,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto put_bridge;
}
- intel_uncore_early_sanitize(dev);
-
/* This must be called before any calls to HAS_PCH_* */
intel_detect_pch(dev);
@@ -1635,9 +1699,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!IS_I945G(dev) && !IS_I945GM(dev))
pci_enable_msi(dev->pdev);
- dev_priv->num_plane = 1;
- if (IS_VALLEYVIEW(dev))
- dev_priv->num_plane = 2;
+ intel_device_info_runtime_init(dev);
if (INTEL_INFO(dev)->num_pipes) {
ret = drm_vblank_init(dev, INTEL_INFO(dev)->num_pipes);
@@ -1645,7 +1707,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto out_gem_unload;
}
- intel_power_domains_init(dev);
+ intel_power_domains_init(dev_priv);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
ret = i915_load_modeset_init(dev);
@@ -1674,7 +1736,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return 0;
out_power_well:
- intel_power_domains_remove(dev);
+ intel_power_domains_remove(dev_priv);
drm_vblank_cleanup(dev);
out_gem_unload:
if (dev_priv->mm.inactive_shrinker.scan_objects)
@@ -1724,8 +1786,8 @@ int i915_driver_unload(struct drm_device *dev)
/* The i915.ko module is still not prepared to be loaded when
* the power well is not enabled, so just enable it in case
* we're going to unload/reload. */
- intel_display_set_init_power(dev, true);
- intel_power_domains_remove(dev);
+ intel_display_set_init_power(dev_priv, true);
+ intel_power_domains_remove(dev_priv);
i915_teardown_sysfs(dev);
@@ -1761,8 +1823,6 @@ int i915_driver_unload(struct drm_device *dev)
cancel_work_sync(&dev_priv->gpu_error.work);
i915_destroy_error_state(dev);
- cancel_delayed_work_sync(&dev_priv->pc8.enable_work);
-
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
@@ -1776,8 +1836,8 @@ int i915_driver_unload(struct drm_device *dev)
i915_gem_free_all_phys_object(dev);
i915_gem_cleanup_ringbuffer(dev);
i915_gem_context_fini(dev);
+ WARN_ON(dev_priv->mm.aliasing_ppgtt);
mutex_unlock(&dev->struct_mutex);
- i915_gem_cleanup_aliasing_ppgtt(dev);
i915_gem_cleanup_stolen(dev);
if (!I915_NEED_GFX_HWS(dev))
@@ -1835,7 +1895,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file)
*/
void i915_driver_lastclose(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
/* On gen6+ we refuse to init without kms enabled, but then the drm core
* goes right around and calls lastclose. Check for this and don't clean
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index ec7bb0fc71bc..82f4d1f47d3b 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -38,134 +38,30 @@
#include <linux/module.h>
#include <drm/drm_crtc_helper.h>
-static int i915_modeset __read_mostly = -1;
-module_param_named(modeset, i915_modeset, int, 0400);
-MODULE_PARM_DESC(modeset,
- "Use kernel modesetting [KMS] (0=DRM_I915_KMS from .config, "
- "1=on, -1=force vga console preference [default])");
-
-unsigned int i915_fbpercrtc __always_unused = 0;
-module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
-
-int i915_panel_ignore_lid __read_mostly = 1;
-module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600);
-MODULE_PARM_DESC(panel_ignore_lid,
- "Override lid status (0=autodetect, 1=autodetect disabled [default], "
- "-1=force lid closed, -2=force lid open)");
-
-unsigned int i915_powersave __read_mostly = 1;
-module_param_named(powersave, i915_powersave, int, 0600);
-MODULE_PARM_DESC(powersave,
- "Enable powersavings, fbc, downclocking, etc. (default: true)");
-
-int i915_semaphores __read_mostly = -1;
-module_param_named(semaphores, i915_semaphores, int, 0400);
-MODULE_PARM_DESC(semaphores,
- "Use semaphores for inter-ring sync (default: -1 (use per-chip defaults))");
-
-int i915_enable_rc6 __read_mostly = -1;
-module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0400);
-MODULE_PARM_DESC(i915_enable_rc6,
- "Enable power-saving render C-state 6. "
- "Different stages can be selected via bitmask values "
- "(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). "
- "For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. "
- "default: -1 (use per-chip default)");
-
-int i915_enable_fbc __read_mostly = -1;
-module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600);
-MODULE_PARM_DESC(i915_enable_fbc,
- "Enable frame buffer compression for power savings "
- "(default: -1 (use per-chip default))");
-
-unsigned int i915_lvds_downclock __read_mostly = 0;
-module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
-MODULE_PARM_DESC(lvds_downclock,
- "Use panel (LVDS/eDP) downclocking for power savings "
- "(default: false)");
-
-int i915_lvds_channel_mode __read_mostly;
-module_param_named(lvds_channel_mode, i915_lvds_channel_mode, int, 0600);
-MODULE_PARM_DESC(lvds_channel_mode,
- "Specify LVDS channel mode "
- "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)");
-
-int i915_panel_use_ssc __read_mostly = -1;
-module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600);
-MODULE_PARM_DESC(lvds_use_ssc,
- "Use Spread Spectrum Clock with panels [LVDS/eDP] "
- "(default: auto from VBT)");
-
-int i915_vbt_sdvo_panel_type __read_mostly = -1;
-module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600);
-MODULE_PARM_DESC(vbt_sdvo_panel_type,
- "Override/Ignore selection of SDVO panel mode in the VBT "
- "(-2=ignore, -1=auto [default], index in VBT BIOS table)");
-
-static bool i915_try_reset __read_mostly = true;
-module_param_named(reset, i915_try_reset, bool, 0600);
-MODULE_PARM_DESC(reset, "Attempt GPU resets (default: true)");
-
-bool i915_enable_hangcheck __read_mostly = true;
-module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644);
-MODULE_PARM_DESC(enable_hangcheck,
- "Periodically check GPU activity for detecting hangs. "
- "WARNING: Disabling this can cause system wide hangs. "
- "(default: true)");
-
-int i915_enable_ppgtt __read_mostly = -1;
-module_param_named(i915_enable_ppgtt, i915_enable_ppgtt, int, 0400);
-MODULE_PARM_DESC(i915_enable_ppgtt,
- "Enable PPGTT (default: true)");
-
-int i915_enable_psr __read_mostly = 0;
-module_param_named(enable_psr, i915_enable_psr, int, 0600);
-MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)");
-
-unsigned int i915_preliminary_hw_support __read_mostly = IS_ENABLED(CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT);
-module_param_named(preliminary_hw_support, i915_preliminary_hw_support, int, 0600);
-MODULE_PARM_DESC(preliminary_hw_support,
- "Enable preliminary hardware support.");
-
-int i915_disable_power_well __read_mostly = 1;
-module_param_named(disable_power_well, i915_disable_power_well, int, 0600);
-MODULE_PARM_DESC(disable_power_well,
- "Disable the power well when possible (default: true)");
-
-int i915_enable_ips __read_mostly = 1;
-module_param_named(enable_ips, i915_enable_ips, int, 0600);
-MODULE_PARM_DESC(enable_ips, "Enable IPS (default: true)");
-
-bool i915_fastboot __read_mostly = 0;
-module_param_named(fastboot, i915_fastboot, bool, 0600);
-MODULE_PARM_DESC(fastboot, "Try to skip unnecessary mode sets at boot time "
- "(default: false)");
-
-int i915_enable_pc8 __read_mostly = 1;
-module_param_named(enable_pc8, i915_enable_pc8, int, 0600);
-MODULE_PARM_DESC(enable_pc8, "Enable support for low power package C states (PC8+) (default: true)");
-
-int i915_pc8_timeout __read_mostly = 5000;
-module_param_named(pc8_timeout, i915_pc8_timeout, int, 0600);
-MODULE_PARM_DESC(pc8_timeout, "Number of msecs of idleness required to enter PC8+ (default: 5000)");
-
-bool i915_prefault_disable __read_mostly;
-module_param_named(prefault_disable, i915_prefault_disable, bool, 0600);
-MODULE_PARM_DESC(prefault_disable,
- "Disable page prefaulting for pread/pwrite/reloc (default:false). For developers only.");
-
static struct drm_driver driver;
+#define GEN_DEFAULT_PIPEOFFSETS \
+ .pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
+ PIPE_C_OFFSET, PIPE_EDP_OFFSET }, \
+ .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
+ TRANSCODER_C_OFFSET, TRANSCODER_EDP_OFFSET }, \
+ .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET }, \
+ .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \
+ .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
+
+
static const struct intel_device_info intel_i830_info = {
.gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_845g_info = {
.gen = 2, .num_pipes = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i85x_info = {
@@ -174,18 +70,21 @@ static const struct intel_device_info intel_i85x_info = {
.has_overlay = 1, .overlay_needs_physical = 1,
.has_fbc = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i865g_info = {
.gen = 2, .num_pipes = 1,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i915g_info = {
.gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i915gm_info = {
.gen = 3, .is_mobile = 1, .num_pipes = 2,
@@ -194,11 +93,13 @@ static const struct intel_device_info intel_i915gm_info = {
.supports_tv = 1,
.has_fbc = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i945g_info = {
.gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i945gm_info = {
.gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
@@ -207,6 +108,7 @@ static const struct intel_device_info intel_i945gm_info = {
.supports_tv = 1,
.has_fbc = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i965g_info = {
@@ -214,6 +116,7 @@ static const struct intel_device_info intel_i965g_info = {
.has_hotplug = 1,
.has_overlay = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_i965gm_info = {
@@ -222,6 +125,7 @@ static const struct intel_device_info intel_i965gm_info = {
.has_overlay = 1,
.supports_tv = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_g33_info = {
@@ -229,12 +133,14 @@ static const struct intel_device_info intel_g33_info = {
.need_gfx_hws = 1, .has_hotplug = 1,
.has_overlay = 1,
.ring_mask = RENDER_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_g45_info = {
.gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2,
.has_pipe_cxsr = 1, .has_hotplug = 1,
.ring_mask = RENDER_RING | BSD_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_gm45_info = {
@@ -243,18 +149,21 @@ static const struct intel_device_info intel_gm45_info = {
.has_pipe_cxsr = 1, .has_hotplug = 1,
.supports_tv = 1,
.ring_mask = RENDER_RING | BSD_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_pineview_info = {
.gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .num_pipes = 2,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_overlay = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_ironlake_d_info = {
.gen = 5, .num_pipes = 2,
.need_gfx_hws = 1, .has_hotplug = 1,
.ring_mask = RENDER_RING | BSD_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_ironlake_m_info = {
@@ -262,6 +171,7 @@ static const struct intel_device_info intel_ironlake_m_info = {
.need_gfx_hws = 1, .has_hotplug = 1,
.has_fbc = 1,
.ring_mask = RENDER_RING | BSD_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_sandybridge_d_info = {
@@ -270,6 +180,7 @@ static const struct intel_device_info intel_sandybridge_d_info = {
.has_fbc = 1,
.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
.has_llc = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_sandybridge_m_info = {
@@ -278,6 +189,7 @@ static const struct intel_device_info intel_sandybridge_m_info = {
.has_fbc = 1,
.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
.has_llc = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
#define GEN7_FEATURES \
@@ -290,18 +202,21 @@ static const struct intel_device_info intel_sandybridge_m_info = {
static const struct intel_device_info intel_ivybridge_d_info = {
GEN7_FEATURES,
.is_ivybridge = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_ivybridge_m_info = {
GEN7_FEATURES,
.is_ivybridge = 1,
.is_mobile = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_ivybridge_q_info = {
GEN7_FEATURES,
.is_ivybridge = 1,
.num_pipes = 0, /* legal, last one wins */
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_valleyview_m_info = {
@@ -312,6 +227,7 @@ static const struct intel_device_info intel_valleyview_m_info = {
.display_mmio_offset = VLV_DISPLAY_BASE,
.has_fbc = 0, /* legal, last one wins */
.has_llc = 0, /* legal, last one wins */
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_valleyview_d_info = {
@@ -321,6 +237,7 @@ static const struct intel_device_info intel_valleyview_d_info = {
.display_mmio_offset = VLV_DISPLAY_BASE,
.has_fbc = 0, /* legal, last one wins */
.has_llc = 0, /* legal, last one wins */
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_haswell_d_info = {
@@ -329,6 +246,7 @@ static const struct intel_device_info intel_haswell_d_info = {
.has_ddi = 1,
.has_fpga_dbg = 1,
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_haswell_m_info = {
@@ -338,6 +256,7 @@ static const struct intel_device_info intel_haswell_m_info = {
.has_ddi = 1,
.has_fpga_dbg = 1,
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_broadwell_d_info = {
@@ -346,6 +265,8 @@ static const struct intel_device_info intel_broadwell_d_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
.has_llc = 1,
.has_ddi = 1,
+ .has_fbc = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
static const struct intel_device_info intel_broadwell_m_info = {
@@ -354,6 +275,8 @@ static const struct intel_device_info intel_broadwell_m_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
.has_llc = 1,
.has_ddi = 1,
+ .has_fbc = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
};
/*
@@ -475,14 +398,12 @@ bool i915_semaphore_is_enabled(struct drm_device *dev)
if (INTEL_INFO(dev)->gen < 6)
return false;
+ if (i915.semaphores >= 0)
+ return i915.semaphores;
+
/* Until we get further testing... */
- if (IS_GEN8(dev)) {
- WARN_ON(!i915_preliminary_hw_support);
+ if (IS_GEN8(dev))
return false;
- }
-
- if (i915_semaphores >= 0)
- return i915_semaphores;
#ifdef CONFIG_INTEL_IOMMU
/* Enable semaphores on SNB when IO remapping is off */
@@ -507,8 +428,7 @@ static int i915_drm_freeze(struct drm_device *dev)
/* We do a lot of poking in a lot of registers, make sure they work
* properly. */
- hsw_disable_package_c8(dev_priv);
- intel_display_set_init_power(dev, true);
+ intel_display_set_init_power(dev_priv, true);
drm_kms_helper_poll_disable(dev);
@@ -546,11 +466,14 @@ static int i915_drm_freeze(struct drm_device *dev)
i915_save_state(dev);
intel_opregion_fini(dev);
+ intel_uncore_fini(dev);
console_lock();
intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED);
console_unlock();
+ dev_priv->suspend_count++;
+
return 0;
}
@@ -614,14 +537,21 @@ static void intel_resume_hotplug(struct drm_device *dev)
drm_helper_hpd_irq_event(dev);
}
-static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
+static int i915_drm_thaw_early(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int error = 0;
intel_uncore_early_sanitize(dev);
-
intel_uncore_sanitize(dev);
+ intel_power_domains_init_hw(dev_priv);
+
+ return 0;
+}
+
+static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int error = 0;
if (drm_core_check_feature(dev, DRIVER_MODESET) &&
restore_gtt_mappings) {
@@ -630,14 +560,13 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
mutex_unlock(&dev->struct_mutex);
}
- intel_power_domains_init_hw(dev);
-
i915_restore_state(dev);
intel_opregion_setup(dev);
/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_init_pch_refclk(dev);
+ drm_mode_config_reset(dev);
mutex_lock(&dev->struct_mutex);
@@ -650,7 +579,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
intel_modeset_init_hw(dev);
drm_modeset_lock_all(dev);
- drm_mode_config_reset(dev);
intel_modeset_setup_hw_state(dev, true);
drm_modeset_unlock_all(dev);
@@ -680,10 +608,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
schedule_work(&dev_priv->console_resume_work);
}
- /* Undo what we did at i915_drm_freeze so the refcount goes back to the
- * expected level. */
- hsw_enable_package_c8(dev_priv);
-
mutex_lock(&dev_priv->modeset_restore_lock);
dev_priv->modeset_restore = MODESET_DONE;
mutex_unlock(&dev_priv->modeset_restore_lock);
@@ -700,19 +624,33 @@ static int i915_drm_thaw(struct drm_device *dev)
return __i915_drm_thaw(dev, true);
}
-int i915_resume(struct drm_device *dev)
+static int i915_resume_early(struct drm_device *dev)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- int ret;
-
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
+ /*
+ * We have a resume ordering issue with the snd-hda driver also
+ * requiring our device to be power up. Due to the lack of a
+ * parent/child relationship we currently solve this with an early
+ * resume hook.
+ *
+ * FIXME: This should be solved with a special hdmi sink device or
+ * similar so that power domains can be employed.
+ */
if (pci_enable_device(dev->pdev))
return -EIO;
pci_set_master(dev->pdev);
+ return i915_drm_thaw_early(dev);
+}
+
+int i915_resume(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
/*
* Platforms with opregion should have sane BIOS, older ones (gen3 and
* earlier) need to restore the GTT mappings since the BIOS might clear
@@ -726,6 +664,14 @@ int i915_resume(struct drm_device *dev)
return 0;
}
+static int i915_resume_legacy(struct drm_device *dev)
+{
+ i915_resume_early(dev);
+ i915_resume(dev);
+
+ return 0;
+}
+
/**
* i915_reset - reset chip after a hang
* @dev: drm device to reset
@@ -743,11 +689,11 @@ int i915_resume(struct drm_device *dev)
*/
int i915_reset(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
bool simulated;
int ret;
- if (!i915_try_reset)
+ if (!i915.reset)
return 0;
mutex_lock(&dev->struct_mutex);
@@ -802,6 +748,17 @@ int i915_reset(struct drm_device *dev)
drm_irq_uninstall(dev);
drm_irq_install(dev);
+
+ /* rps/rc6 re-init is necessary to restore state lost after the
+ * reset and the re-install of drm irq. Skip for ironlake per
+ * previous concerns that it doesn't respond well to some forms
+ * of re-init after reset. */
+ if (INTEL_INFO(dev)->gen > 5) {
+ mutex_lock(&dev->struct_mutex);
+ intel_enable_gt_powersave(dev);
+ mutex_unlock(&dev->struct_mutex);
+ }
+
intel_hpd_init(dev);
} else {
mutex_unlock(&dev->struct_mutex);
@@ -815,7 +772,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct intel_device_info *intel_info =
(struct intel_device_info *) ent->driver_data;
- if (IS_PRELIMINARY_HW(intel_info) && !i915_preliminary_hw_support) {
+ if (IS_PRELIMINARY_HW(intel_info) && !i915.preliminary_hw_support) {
DRM_INFO("This hardware requires preliminary hardware support.\n"
"See CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT, and/or modparam preliminary_hw_support\n");
return -ENODEV;
@@ -846,7 +803,6 @@ static int i915_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- int error;
if (!drm_dev || !drm_dev->dev_private) {
dev_err(dev, "DRM not initialized, aborting suspend.\n");
@@ -856,9 +812,25 @@ static int i915_pm_suspend(struct device *dev)
if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
- error = i915_drm_freeze(drm_dev);
- if (error)
- return error;
+ return i915_drm_freeze(drm_dev);
+}
+
+static int i915_pm_suspend_late(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+ /*
+ * We have a suspedn ordering issue with the snd-hda driver also
+ * requiring our device to be power up. Due to the lack of a
+ * parent/child relationship we currently solve this with an late
+ * suspend hook.
+ *
+ * FIXME: This should be solved with a special hdmi sink device or
+ * similar so that power domains can be employed.
+ */
+ if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ return 0;
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
@@ -866,6 +838,14 @@ static int i915_pm_suspend(struct device *dev)
return 0;
}
+static int i915_pm_resume_early(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+ return i915_resume_early(drm_dev);
+}
+
static int i915_pm_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -887,6 +867,14 @@ static int i915_pm_freeze(struct device *dev)
return i915_drm_freeze(drm_dev);
}
+static int i915_pm_thaw_early(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+ return i915_drm_thaw_early(drm_dev);
+}
+
static int i915_pm_thaw(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -910,9 +898,13 @@ static int i915_runtime_suspend(struct device *device)
struct drm_i915_private *dev_priv = dev->dev_private;
WARN_ON(!HAS_RUNTIME_PM(dev));
+ assert_force_wake_inactive(dev_priv);
DRM_DEBUG_KMS("Suspending device\n");
+ if (HAS_PC8(dev))
+ hsw_enable_pc8(dev_priv);
+
i915_gem_release_all_mmaps(dev_priv);
del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
@@ -927,6 +919,7 @@ static int i915_runtime_suspend(struct device *device)
*/
intel_opregion_notify_adapter(dev, PCI_D1);
+ DRM_DEBUG_KMS("Device suspended\n");
return 0;
}
@@ -943,15 +936,23 @@ static int i915_runtime_resume(struct device *device)
intel_opregion_notify_adapter(dev, PCI_D0);
dev_priv->pm.suspended = false;
+ if (HAS_PC8(dev))
+ hsw_disable_pc8(dev_priv);
+
+ DRM_DEBUG_KMS("Device resumed\n");
return 0;
}
static const struct dev_pm_ops i915_pm_ops = {
.suspend = i915_pm_suspend,
+ .suspend_late = i915_pm_suspend_late,
+ .resume_early = i915_pm_resume_early,
.resume = i915_pm_resume,
.freeze = i915_pm_freeze,
+ .thaw_early = i915_pm_thaw_early,
.thaw = i915_pm_thaw,
.poweroff = i915_pm_poweroff,
+ .restore_early = i915_pm_resume_early,
.restore = i915_pm_resume,
.runtime_suspend = i915_runtime_suspend,
.runtime_resume = i915_runtime_resume,
@@ -994,7 +995,7 @@ static struct drm_driver driver = {
/* Used in place of i915_pm_ops for non-DRIVER_MODESET */
.suspend = i915_suspend,
- .resume = i915_resume,
+ .resume = i915_resume_legacy,
.device_is_agp = i915_driver_device_is_agp,
.master_create = i915_master_create,
@@ -1046,14 +1047,14 @@ static int __init i915_init(void)
* the default behavior.
*/
#if defined(CONFIG_DRM_I915_KMS)
- if (i915_modeset != 0)
+ if (i915.modeset != 0)
driver.driver_features |= DRIVER_MODESET;
#endif
- if (i915_modeset == 1)
+ if (i915.modeset == 1)
driver.driver_features |= DRIVER_MODESET;
#ifdef CONFIG_VGA_CONSOLE
- if (vgacon_text_force() && i915_modeset == -1)
+ if (vgacon_text_force() && i915.modeset == -1)
driver.driver_features &= ~DRIVER_MODESET;
#endif
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index df77e20e3c3d..0905cd915589 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -58,7 +58,8 @@ enum pipe {
PIPE_A = 0,
PIPE_B,
PIPE_C,
- I915_MAX_PIPES
+ _PIPE_EDP,
+ I915_MAX_PIPES = _PIPE_EDP
};
#define pipe_name(p) ((p) + 'A')
@@ -66,7 +67,8 @@ enum transcoder {
TRANSCODER_A = 0,
TRANSCODER_B,
TRANSCODER_C,
- TRANSCODER_EDP = 0xF,
+ TRANSCODER_EDP,
+ I915_MAX_TRANSCODERS
};
#define transcoder_name(t) ((t) + 'A')
@@ -77,7 +79,7 @@ enum plane {
};
#define plane_name(p) ((p) + 'A')
-#define sprite_name(p, s) ((p) * dev_priv->num_plane + (s) + 'A')
+#define sprite_name(p, s) ((p) * INTEL_INFO(dev)->num_sprites[(p)] + (s) + 'A')
enum port {
PORT_A = 0,
@@ -112,6 +114,17 @@ enum intel_display_power_domain {
POWER_DOMAIN_TRANSCODER_B,
POWER_DOMAIN_TRANSCODER_C,
POWER_DOMAIN_TRANSCODER_EDP,
+ POWER_DOMAIN_PORT_DDI_A_2_LANES,
+ POWER_DOMAIN_PORT_DDI_A_4_LANES,
+ POWER_DOMAIN_PORT_DDI_B_2_LANES,
+ POWER_DOMAIN_PORT_DDI_B_4_LANES,
+ POWER_DOMAIN_PORT_DDI_C_2_LANES,
+ POWER_DOMAIN_PORT_DDI_C_4_LANES,
+ POWER_DOMAIN_PORT_DDI_D_2_LANES,
+ POWER_DOMAIN_PORT_DDI_D_4_LANES,
+ POWER_DOMAIN_PORT_DSI,
+ POWER_DOMAIN_PORT_CRT,
+ POWER_DOMAIN_PORT_OTHER,
POWER_DOMAIN_VGA,
POWER_DOMAIN_AUDIO,
POWER_DOMAIN_INIT,
@@ -119,8 +132,6 @@ enum intel_display_power_domain {
POWER_DOMAIN_NUM,
};
-#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1)
-
#define POWER_DOMAIN_PIPE(pipe) ((pipe) + POWER_DOMAIN_PIPE_A)
#define POWER_DOMAIN_PIPE_PANEL_FITTER(pipe) \
((pipe) + POWER_DOMAIN_PIPE_A_PANEL_FITTER)
@@ -128,14 +139,6 @@ enum intel_display_power_domain {
((tran) == TRANSCODER_EDP ? POWER_DOMAIN_TRANSCODER_EDP : \
(tran) + POWER_DOMAIN_TRANSCODER_A)
-#define HSW_ALWAYS_ON_POWER_DOMAINS ( \
- BIT(POWER_DOMAIN_PIPE_A) | \
- BIT(POWER_DOMAIN_TRANSCODER_EDP))
-#define BDW_ALWAYS_ON_POWER_DOMAINS ( \
- BIT(POWER_DOMAIN_PIPE_A) | \
- BIT(POWER_DOMAIN_TRANSCODER_EDP) | \
- BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER))
-
enum hpd_pin {
HPD_NONE = 0,
HPD_PORT_A = HPD_NONE, /* PORT_A is internal */
@@ -157,11 +160,16 @@ enum hpd_pin {
I915_GEM_DOMAIN_VERTEX)
#define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++)
+#define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++)
#define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \
list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \
if ((intel_encoder)->base.crtc == (__crtc))
+#define for_each_connector_on_encoder(dev, __encoder, intel_connector) \
+ list_for_each_entry((intel_connector), &(dev)->mode_config.connector_list, base.head) \
+ if ((intel_connector)->base.encoder == (__encoder))
+
struct drm_i915_private;
enum intel_dpll_id {
@@ -295,53 +303,87 @@ struct intel_display_error_state;
struct drm_i915_error_state {
struct kref ref;
+ struct timeval time;
+
+ char error_msg[128];
+ u32 reset_count;
+ u32 suspend_count;
+
+ /* Generic register state */
u32 eir;
u32 pgtbl_er;
u32 ier;
u32 ccid;
u32 derrmr;
u32 forcewake;
- bool waiting[I915_NUM_RINGS];
- u32 pipestat[I915_MAX_PIPES];
- u32 tail[I915_NUM_RINGS];
- u32 head[I915_NUM_RINGS];
- u32 ctl[I915_NUM_RINGS];
- u32 ipeir[I915_NUM_RINGS];
- u32 ipehr[I915_NUM_RINGS];
- u32 instdone[I915_NUM_RINGS];
- u32 acthd[I915_NUM_RINGS];
- u32 semaphore_mboxes[I915_NUM_RINGS][I915_NUM_RINGS - 1];
- u32 semaphore_seqno[I915_NUM_RINGS][I915_NUM_RINGS - 1];
- u32 rc_psmi[I915_NUM_RINGS]; /* sleep state */
- /* our own tracking of ring head and tail */
- u32 cpu_ring_head[I915_NUM_RINGS];
- u32 cpu_ring_tail[I915_NUM_RINGS];
u32 error; /* gen6+ */
u32 err_int; /* gen7 */
- u32 bbstate[I915_NUM_RINGS];
- u32 instpm[I915_NUM_RINGS];
- u32 instps[I915_NUM_RINGS];
- u32 extra_instdone[I915_NUM_INSTDONE_REG];
- u32 seqno[I915_NUM_RINGS];
- u64 bbaddr[I915_NUM_RINGS];
- u32 fault_reg[I915_NUM_RINGS];
u32 done_reg;
- u32 faddr[I915_NUM_RINGS];
+ u32 gac_eco;
+ u32 gam_ecochk;
+ u32 gab_ctl;
+ u32 gfx_mode;
+ u32 extra_instdone[I915_NUM_INSTDONE_REG];
+ u32 pipestat[I915_MAX_PIPES];
u64 fence[I915_MAX_NUM_FENCES];
- struct timeval time;
+ struct intel_overlay_error_state *overlay;
+ struct intel_display_error_state *display;
+
struct drm_i915_error_ring {
bool valid;
+ /* Software tracked state */
+ bool waiting;
+ int hangcheck_score;
+ enum intel_ring_hangcheck_action hangcheck_action;
+ int num_requests;
+
+ /* our own tracking of ring head and tail */
+ u32 cpu_ring_head;
+ u32 cpu_ring_tail;
+
+ u32 semaphore_seqno[I915_NUM_RINGS - 1];
+
+ /* Register state */
+ u32 tail;
+ u32 head;
+ u32 ctl;
+ u32 hws;
+ u32 ipeir;
+ u32 ipehr;
+ u32 instdone;
+ u32 bbstate;
+ u32 instpm;
+ u32 instps;
+ u32 seqno;
+ u64 bbaddr;
+ u64 acthd;
+ u32 fault_reg;
+ u32 faddr;
+ u32 rc_psmi; /* sleep state */
+ u32 semaphore_mboxes[I915_NUM_RINGS - 1];
+
struct drm_i915_error_object {
int page_count;
u32 gtt_offset;
u32 *pages[0];
- } *ringbuffer, *batchbuffer, *ctx;
+ } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
+
struct drm_i915_error_request {
long jiffies;
u32 seqno;
u32 tail;
} *requests;
- int num_requests;
+
+ struct {
+ u32 gfx_mode;
+ union {
+ u64 pdp[4];
+ u32 pp_dir_base;
+ };
+ } vm_info;
+
+ pid_t pid;
+ char comm[TASK_COMM_LEN];
} ring[I915_NUM_RINGS];
struct drm_i915_error_buffer {
u32 size;
@@ -358,15 +400,13 @@ struct drm_i915_error_state {
s32 ring:4;
u32 cache_level:3;
} **active_bo, **pinned_bo;
+
u32 *active_bo_count, *pinned_bo_count;
- struct intel_overlay_error_state *overlay;
- struct intel_display_error_state *display;
- int hangcheck_score[I915_NUM_RINGS];
- enum intel_ring_hangcheck_action hangcheck_action[I915_NUM_RINGS];
};
struct intel_connector;
struct intel_crtc_config;
+struct intel_plane_config;
struct intel_crtc;
struct intel_limit;
struct dpll;
@@ -405,6 +445,8 @@ struct drm_i915_display_funcs {
* fills out the pipe-config with the hw state. */
bool (*get_pipe_config)(struct intel_crtc *,
struct intel_crtc_config *);
+ void (*get_plane_config)(struct intel_crtc *,
+ struct intel_plane_config *);
int (*crtc_mode_set)(struct drm_crtc *crtc,
int x, int y,
struct drm_framebuffer *old_fb);
@@ -420,8 +462,9 @@ struct drm_i915_display_funcs {
struct drm_framebuffer *fb,
struct drm_i915_gem_object *obj,
uint32_t flags);
- int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
- int x, int y);
+ int (*update_primary_plane)(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y);
void (*hpd_irq_setup)(struct drm_device *dev);
/* clock updates for mode set */
/* cursor updates */
@@ -469,7 +512,7 @@ struct intel_uncore {
unsigned fw_rendercount;
unsigned fw_mediacount;
- struct delayed_work force_wake_work;
+ struct timer_list force_wake_timer;
};
#define DEV_INFO_FOR_EACH_FLAG(func, sep) \
@@ -504,9 +547,16 @@ struct intel_uncore {
struct intel_device_info {
u32 display_mmio_offset;
u8 num_pipes:3;
+ u8 num_sprites[I915_MAX_PIPES];
u8 gen;
u8 ring_mask; /* Rings supported by the HW */
DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON);
+ /* Register offsets for the various display pipes and transcoders */
+ int pipe_offsets[I915_MAX_TRANSCODERS];
+ int trans_offsets[I915_MAX_TRANSCODERS];
+ int dpll_offsets[I915_MAX_PIPES];
+ int dpll_md_offsets[I915_MAX_PIPES];
+ int palette_offsets[I915_MAX_PIPES];
};
#undef DEFINE_FLAG
@@ -524,6 +574,57 @@ enum i915_cache_level {
typedef uint32_t gen6_gtt_pte_t;
+/**
+ * A VMA represents a GEM BO that is bound into an address space. Therefore, a
+ * VMA's presence cannot be guaranteed before binding, or after unbinding the
+ * object into/from the address space.
+ *
+ * To make things as simple as possible (ie. no refcounting), a VMA's lifetime
+ * will always be <= an objects lifetime. So object refcounting should cover us.
+ */
+struct i915_vma {
+ struct drm_mm_node node;
+ struct drm_i915_gem_object *obj;
+ struct i915_address_space *vm;
+
+ /** This object's place on the active/inactive lists */
+ struct list_head mm_list;
+
+ struct list_head vma_link; /* Link in the object's VMA list */
+
+ /** This vma's place in the batchbuffer or on the eviction list */
+ struct list_head exec_list;
+
+ /**
+ * Used for performing relocations during execbuffer insertion.
+ */
+ struct hlist_node exec_node;
+ unsigned long exec_handle;
+ struct drm_i915_gem_exec_object2 *exec_entry;
+
+ /**
+ * How many users have pinned this object in GTT space. The following
+ * users can each hold at most one reference: pwrite/pread, pin_ioctl
+ * (via user_pin_count), execbuffer (objects are not allowed multiple
+ * times for the same batchbuffer), and the framebuffer code. When
+ * switching/pageflipping, the framebuffer code has at most two buffers
+ * pinned per crtc.
+ *
+ * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
+ * bits with absolutely no headroom. So use 4 bits. */
+ unsigned int pin_count:4;
+#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
+
+ /** Unmap an object from an address space. This usually consists of
+ * setting the valid PTE entries to a reserved scratch page. */
+ void (*unbind_vma)(struct i915_vma *vma);
+ /* Map an object into an address space with the given cache flags. */
+#define GLOBAL_BIND (1<<0)
+ void (*bind_vma)(struct i915_vma *vma,
+ enum i915_cache_level cache_level,
+ u32 flags);
+};
+
struct i915_address_space {
struct drm_mm mm;
struct drm_device *dev;
@@ -564,12 +665,12 @@ struct i915_address_space {
enum i915_cache_level level,
bool valid); /* Create a valid PTE */
void (*clear_range)(struct i915_address_space *vm,
- unsigned int first_entry,
- unsigned int num_entries,
+ uint64_t start,
+ uint64_t length,
bool use_scratch);
void (*insert_entries)(struct i915_address_space *vm,
struct sg_table *st,
- unsigned int first_entry,
+ uint64_t start,
enum i915_cache_level cache_level);
void (*cleanup)(struct i915_address_space *vm);
};
@@ -603,55 +704,34 @@ struct i915_gtt {
};
#define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT)
+#define GEN8_LEGACY_PDPS 4
struct i915_hw_ppgtt {
struct i915_address_space base;
+ struct kref ref;
+ struct drm_mm_node node;
unsigned num_pd_entries;
+ unsigned num_pd_pages; /* gen8+ */
union {
struct page **pt_pages;
- struct page *gen8_pt_pages;
+ struct page **gen8_pt_pages[GEN8_LEGACY_PDPS];
};
struct page *pd_pages;
- int num_pd_pages;
- int num_pt_pages;
union {
uint32_t pd_offset;
- dma_addr_t pd_dma_addr[4];
+ dma_addr_t pd_dma_addr[GEN8_LEGACY_PDPS];
};
union {
dma_addr_t *pt_dma_addr;
dma_addr_t *gen8_pt_dma_addr[4];
};
- int (*enable)(struct drm_device *dev);
-};
-
-/**
- * A VMA represents a GEM BO that is bound into an address space. Therefore, a
- * VMA's presence cannot be guaranteed before binding, or after unbinding the
- * object into/from the address space.
- *
- * To make things as simple as possible (ie. no refcounting), a VMA's lifetime
- * will always be <= an objects lifetime. So object refcounting should cover us.
- */
-struct i915_vma {
- struct drm_mm_node node;
- struct drm_i915_gem_object *obj;
- struct i915_address_space *vm;
-
- /** This object's place on the active/inactive lists */
- struct list_head mm_list;
- struct list_head vma_link; /* Link in the object's VMA list */
-
- /** This vma's place in the batchbuffer or on the eviction list */
- struct list_head exec_list;
-
- /**
- * Used for performing relocations during execbuffer insertion.
- */
- struct hlist_node exec_node;
- unsigned long exec_handle;
- struct drm_i915_gem_exec_object2 *exec_entry;
+ struct i915_hw_context *ctx;
+ int (*enable)(struct i915_hw_ppgtt *ppgtt);
+ int (*switch_mm)(struct i915_hw_ppgtt *ppgtt,
+ struct intel_ring_buffer *ring,
+ bool synchronous);
+ void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m);
};
struct i915_ctx_hang_stats {
@@ -676,9 +756,10 @@ struct i915_hw_context {
bool is_initialized;
uint8_t remap_slice;
struct drm_i915_file_private *file_priv;
- struct intel_ring_buffer *ring;
+ struct intel_ring_buffer *last_ring;
struct drm_i915_gem_object *obj;
struct i915_ctx_hang_stats hang_stats;
+ struct i915_address_space *vm;
struct list_head link;
};
@@ -831,11 +912,7 @@ struct i915_suspend_saved_registers {
u32 savePFIT_CONTROL;
u32 save_palette_a[256];
u32 save_palette_b[256];
- u32 saveDPFC_CB_BASE;
- u32 saveFBC_CFB_BASE;
- u32 saveFBC_LL_BASE;
u32 saveFBC_CONTROL;
- u32 saveFBC_CONTROL2;
u32 saveIER;
u32 saveIIR;
u32 saveIMR;
@@ -905,15 +982,24 @@ struct intel_gen6_power_mgmt {
struct work_struct work;
u32 pm_iir;
- /* The below variables an all the rps hw state are protected by
- * dev->struct mutext. */
- u8 cur_delay;
- u8 min_delay;
- u8 max_delay;
- u8 rpe_delay;
- u8 rp1_delay;
- u8 rp0_delay;
- u8 hw_max;
+ /* Frequencies are stored in potentially platform dependent multiples.
+ * In other words, *_freq needs to be multiplied by X to be interesting.
+ * Soft limits are those which are used for the dynamic reclocking done
+ * by the driver (raise frequencies under heavy loads, and lower for
+ * lighter loads). Hard limits are those imposed by the hardware.
+ *
+ * A distinction is made for overclocking, which is never enabled by
+ * default, and is considered to be above the hard limit if it's
+ * possible at all.
+ */
+ u8 cur_freq; /* Current frequency (cached, may not == HW) */
+ u8 min_freq_softlimit; /* Minimum frequency permitted by the driver */
+ u8 max_freq_softlimit; /* Max frequency permitted by the driver */
+ u8 max_freq; /* Maximum frequency, RP0 if not overclocking */
+ u8 min_freq; /* AKA RPn. Minimum frequency */
+ u8 efficient_freq; /* AKA RPe. Pre-determined balanced frequency */
+ u8 rp1_freq; /* "less than" RP0 power/freqency */
+ u8 rp0_freq; /* Non-overclocked max frequency. */
int last_adj;
enum { LOW_POWER, BETWEEN, HIGH_POWER } power;
@@ -953,6 +1039,36 @@ struct intel_ilk_power_mgmt {
struct drm_i915_gem_object *renderctx;
};
+struct drm_i915_private;
+struct i915_power_well;
+
+struct i915_power_well_ops {
+ /*
+ * Synchronize the well's hw state to match the current sw state, for
+ * example enable/disable it based on the current refcount. Called
+ * during driver init and resume time, possibly after first calling
+ * the enable/disable handlers.
+ */
+ void (*sync_hw)(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well);
+ /*
+ * Enable the well and resources that depend on it (for example
+ * interrupts located on the well). Called after the 0->1 refcount
+ * transition.
+ */
+ void (*enable)(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well);
+ /*
+ * Disable the well and resources that depend on it. Called after
+ * the 1->0 refcount transition.
+ */
+ void (*disable)(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well);
+ /* Returns the hw enabled state. */
+ bool (*is_enabled)(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well);
+};
+
/* Power well structure for haswell */
struct i915_power_well {
const char *name;
@@ -960,11 +1076,8 @@ struct i915_power_well {
/* power well enable/disable usage count */
int count;
unsigned long domains;
- void *data;
- void (*set)(struct drm_device *dev, struct i915_power_well *power_well,
- bool enable);
- bool (*is_enabled)(struct drm_device *dev,
- struct i915_power_well *power_well);
+ unsigned long data;
+ const struct i915_power_well_ops *ops;
};
struct i915_power_domains {
@@ -1061,6 +1174,14 @@ struct i915_gem_mm {
*/
bool interruptible;
+ /**
+ * Is the GPU currently considered idle, or busy executing userspace
+ * requests? Whilst idle, we attempt to power down the hardware and
+ * display clocks. In order to reduce the effect on performance, there
+ * is a slight delay before we do so.
+ */
+ bool busy;
+
/** Bit 6 swizzling required for X tiling */
uint32_t bit_6_swizzle_x;
/** Bit 6 swizzling required for Y tiling */
@@ -1226,44 +1347,19 @@ struct ilk_wm_values {
};
/*
- * This struct tracks the state needed for the Package C8+ feature.
- *
- * Package states C8 and deeper are really deep PC states that can only be
- * reached when all the devices on the system allow it, so even if the graphics
- * device allows PC8+, it doesn't mean the system will actually get to these
- * states.
- *
- * Our driver only allows PC8+ when all the outputs are disabled, the power well
- * is disabled and the GPU is idle. When these conditions are met, we manually
- * do the other conditions: disable the interrupts, clocks and switch LCPLL
- * refclk to Fclk.
- *
- * When we really reach PC8 or deeper states (not just when we allow it) we lose
- * the state of some registers, so when we come back from PC8+ we need to
- * restore this state. We don't get into PC8+ if we're not in RC6, so we don't
- * need to take care of the registers kept by RC6.
+ * This struct helps tracking the state needed for runtime PM, which puts the
+ * device in PCI D3 state. Notice that when this happens, nothing on the
+ * graphics device works, even register access, so we don't get interrupts nor
+ * anything else.
*
- * The interrupt disabling is part of the requirements. We can only leave the
- * PCH HPD interrupts enabled. If we're in PC8+ and we get another interrupt we
- * can lock the machine.
+ * Every piece of our code that needs to actually touch the hardware needs to
+ * either call intel_runtime_pm_get or call intel_display_power_get with the
+ * appropriate power domain.
*
- * Ideally every piece of our code that needs PC8+ disabled would call
- * hsw_disable_package_c8, which would increment disable_count and prevent the
- * system from reaching PC8+. But we don't have a symmetric way to do this for
- * everything, so we have the requirements_met and gpu_idle variables. When we
- * switch requirements_met or gpu_idle to true we decrease disable_count, and
- * increase it in the opposite case. The requirements_met variable is true when
- * all the CRTCs, encoders and the power well are disabled. The gpu_idle
- * variable is true when the GPU is idle.
- *
- * In addition to everything, we only actually enable PC8+ if disable_count
- * stays at zero for at least some seconds. This is implemented with the
- * enable_work variable. We do this so we don't enable/disable PC8 dozens of
- * consecutive times when all screens are disabled and some background app
- * queries the state of our connectors, or we have some application constantly
- * waking up to use the GPU. Only after the enable_work function actually
- * enables PC8+ the "enable" variable will become true, which means that it can
- * be false even if disable_count is 0.
+ * Our driver uses the autosuspend delay feature, which means we'll only really
+ * suspend if we stay with zero refcount for a certain amount of time. The
+ * default value is currently very conservative (see intel_init_runtime_pm), but
+ * it can be changed with the standard runtime PM files from sysfs.
*
* The irqs_disabled variable becomes true exactly after we disable the IRQs and
* goes back to false exactly before we reenable the IRQs. We use this variable
@@ -1273,17 +1369,11 @@ struct ilk_wm_values {
* inside struct regsave so when we restore the IRQs they will contain the
* latest expected values.
*
- * For more, read "Display Sequences for Package C8" on our documentation.
+ * For more, read the Documentation/power/runtime_pm.txt.
*/
-struct i915_package_c8 {
- bool requirements_met;
- bool gpu_idle;
+struct i915_runtime_pm {
+ bool suspended;
bool irqs_disabled;
- /* Only true after the delayed work task actually enables it. */
- bool enabled;
- int disable_count;
- struct mutex lock;
- struct delayed_work enable_work;
struct {
uint32_t deimr;
@@ -1294,10 +1384,6 @@ struct i915_package_c8 {
} regsave;
};
-struct i915_runtime_pm {
- bool suspended;
-};
-
enum intel_pipe_crc_source {
INTEL_PIPE_CRC_SOURCE_NONE,
INTEL_PIPE_CRC_SOURCE_PLANE1,
@@ -1332,7 +1418,7 @@ typedef struct drm_i915_private {
struct drm_device *dev;
struct kmem_cache *slab;
- const struct intel_device_info *info;
+ const struct intel_device_info info;
int relative_constants_mode;
@@ -1361,11 +1447,11 @@ typedef struct drm_i915_private {
drm_dma_handle_t *status_page_dmah;
struct resource mch_res;
- atomic_t irq_received;
-
/* protects the irq masks */
spinlock_t irq_lock;
+ bool display_irqs_enabled;
+
/* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
struct pm_qos_request pm_qos;
@@ -1379,6 +1465,8 @@ typedef struct drm_i915_private {
};
u32 gt_irq_mask;
u32 pm_irq_mask;
+ u32 pm_rps_events;
+ u32 pipestat_irq_mask[I915_MAX_PIPES];
struct work_struct hotplug_work;
bool enable_hotplug_processing;
@@ -1394,8 +1482,6 @@ typedef struct drm_i915_private {
u32 hpd_event_bits;
struct timer_list hotplug_reenable_timer;
- int num_plane;
-
struct i915_fbc fbc;
struct intel_opregion opregion;
struct intel_vbt_data vbt;
@@ -1445,8 +1531,8 @@ typedef struct drm_i915_private {
struct sdvo_device_mapping sdvo_mappings[2];
- struct drm_crtc *plane_to_crtc_mapping[3];
- struct drm_crtc *pipe_to_crtc_mapping[3];
+ struct drm_crtc *plane_to_crtc_mapping[I915_MAX_PIPES];
+ struct drm_crtc *pipe_to_crtc_mapping[I915_MAX_PIPES];
wait_queue_head_t pending_flip_queue;
#ifdef CONFIG_DEBUG_FS
@@ -1506,6 +1592,7 @@ typedef struct drm_i915_private {
u32 fdi_rx_config;
+ u32 suspend_count;
struct i915_suspend_saved_registers regfile;
struct {
@@ -1525,8 +1612,6 @@ typedef struct drm_i915_private {
struct ilk_wm_values hw;
} wm;
- struct i915_package_c8 pc8;
-
struct i915_runtime_pm pm;
/* Old dri1 support infrastructure, beware the dragons ya fools entering
@@ -1627,18 +1712,6 @@ struct drm_i915_gem_object {
*/
unsigned int fence_dirty:1;
- /** How many users have pinned this object in GTT space. The following
- * users can each hold at most one reference: pwrite/pread, pin_ioctl
- * (via user_pin_count), execbuffer (objects are not allowed multiple
- * times for the same batchbuffer), and the framebuffer code. When
- * switching/pageflipping, the framebuffer code has at most two buffers
- * pinned per crtc.
- *
- * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
- * bits with absolutely no headroom. So use 4 bits. */
- unsigned int pin_count:4;
-#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
-
/**
* Is the object at the current location in the gtt mappable and
* fenceable? Used to avoid costly recalculations.
@@ -1697,7 +1770,6 @@ struct drm_i915_gem_object {
/** for phy allocated objects */
struct drm_i915_gem_phys_object *phys_obj;
};
-#define to_gem_object(obj) (&((struct drm_i915_gem_object *)(obj))->base)
#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
@@ -1743,6 +1815,7 @@ struct drm_i915_gem_request {
struct drm_i915_file_private {
struct drm_i915_private *dev_priv;
+ struct drm_file *file;
struct {
spinlock_t lock;
@@ -1751,11 +1824,95 @@ struct drm_i915_file_private {
} mm;
struct idr context_idr;
- struct i915_ctx_hang_stats hang_stats;
+ struct i915_hw_context *private_default_ctx;
atomic_t rps_wait_boost;
};
-#define INTEL_INFO(dev) (to_i915(dev)->info)
+/*
+ * A command that requires special handling by the command parser.
+ */
+struct drm_i915_cmd_descriptor {
+ /*
+ * Flags describing how the command parser processes the command.
+ *
+ * CMD_DESC_FIXED: The command has a fixed length if this is set,
+ * a length mask if not set
+ * CMD_DESC_SKIP: The command is allowed but does not follow the
+ * standard length encoding for the opcode range in
+ * which it falls
+ * CMD_DESC_REJECT: The command is never allowed
+ * CMD_DESC_REGISTER: The command should be checked against the
+ * register whitelist for the appropriate ring
+ * CMD_DESC_MASTER: The command is allowed if the submitting process
+ * is the DRM master
+ */
+ u32 flags;
+#define CMD_DESC_FIXED (1<<0)
+#define CMD_DESC_SKIP (1<<1)
+#define CMD_DESC_REJECT (1<<2)
+#define CMD_DESC_REGISTER (1<<3)
+#define CMD_DESC_BITMASK (1<<4)
+#define CMD_DESC_MASTER (1<<5)
+
+ /*
+ * The command's unique identification bits and the bitmask to get them.
+ * This isn't strictly the opcode field as defined in the spec and may
+ * also include type, subtype, and/or subop fields.
+ */
+ struct {
+ u32 value;
+ u32 mask;
+ } cmd;
+
+ /*
+ * The command's length. The command is either fixed length (i.e. does
+ * not include a length field) or has a length field mask. The flag
+ * CMD_DESC_FIXED indicates a fixed length. Otherwise, the command has
+ * a length mask. All command entries in a command table must include
+ * length information.
+ */
+ union {
+ u32 fixed;
+ u32 mask;
+ } length;
+
+ /*
+ * Describes where to find a register address in the command to check
+ * against the ring's register whitelist. Only valid if flags has the
+ * CMD_DESC_REGISTER bit set.
+ */
+ struct {
+ u32 offset;
+ u32 mask;
+ } reg;
+
+#define MAX_CMD_DESC_BITMASKS 3
+ /*
+ * Describes command checks where a particular dword is masked and
+ * compared against an expected value. If the command does not match
+ * the expected value, the parser rejects it. Only valid if flags has
+ * the CMD_DESC_BITMASK bit set. Only entries where mask is non-zero
+ * are valid.
+ */
+ struct {
+ u32 offset;
+ u32 mask;
+ u32 expected;
+ } bits[MAX_CMD_DESC_BITMASKS];
+};
+
+/*
+ * A table of commands requiring special handling by the command parser.
+ *
+ * Each ring has an array of tables. Each table consists of an array of command
+ * descriptors, which must be sorted with command opcodes in ascending order.
+ */
+struct drm_i915_cmd_table {
+ const struct drm_i915_cmd_descriptor *table;
+ int count;
+};
+
+#define INTEL_INFO(dev) (&to_i915(dev)->info)
#define IS_I830(dev) ((dev)->pdev->device == 0x3577)
#define IS_845G(dev) ((dev)->pdev->device == 0x2562)
@@ -1824,7 +1981,11 @@ struct drm_i915_file_private {
#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
#define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6)
-#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >=6 && !IS_VALLEYVIEW(dev))
+#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >= 6 && !IS_VALLEYVIEW(dev))
+#define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev) \
+ && !IS_BROADWELL(dev))
+#define USES_PPGTT(dev) intel_enable_ppgtt(dev, false)
+#define USES_FULL_PPGTT(dev) intel_enable_ppgtt(dev, true)
#define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay)
#define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical)
@@ -1887,32 +2048,40 @@ struct drm_i915_file_private {
extern const struct drm_ioctl_desc i915_ioctls[];
extern int i915_max_ioctl;
-extern unsigned int i915_fbpercrtc __always_unused;
-extern int i915_panel_ignore_lid __read_mostly;
-extern unsigned int i915_powersave __read_mostly;
-extern int i915_semaphores __read_mostly;
-extern unsigned int i915_lvds_downclock __read_mostly;
-extern int i915_lvds_channel_mode __read_mostly;
-extern int i915_panel_use_ssc __read_mostly;
-extern int i915_vbt_sdvo_panel_type __read_mostly;
-extern int i915_enable_rc6 __read_mostly;
-extern int i915_enable_fbc __read_mostly;
-extern bool i915_enable_hangcheck __read_mostly;
-extern int i915_enable_ppgtt __read_mostly;
-extern int i915_enable_psr __read_mostly;
-extern unsigned int i915_preliminary_hw_support __read_mostly;
-extern int i915_disable_power_well __read_mostly;
-extern int i915_enable_ips __read_mostly;
-extern bool i915_fastboot __read_mostly;
-extern int i915_enable_pc8 __read_mostly;
-extern int i915_pc8_timeout __read_mostly;
-extern bool i915_prefault_disable __read_mostly;
extern int i915_suspend(struct drm_device *dev, pm_message_t state);
extern int i915_resume(struct drm_device *dev);
extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master);
+/* i915_params.c */
+struct i915_params {
+ int modeset;
+ int panel_ignore_lid;
+ unsigned int powersave;
+ int semaphores;
+ unsigned int lvds_downclock;
+ int lvds_channel_mode;
+ int panel_use_ssc;
+ int vbt_sdvo_panel_type;
+ int enable_rc6;
+ int enable_fbc;
+ int enable_ppgtt;
+ int enable_psr;
+ unsigned int preliminary_hw_support;
+ int disable_power_well;
+ int enable_ips;
+ int invert_brightness;
+ int enable_cmd_parser;
+ /* leave bools at the end to not create holes */
+ bool enable_hangcheck;
+ bool fastboot;
+ bool prefault_disable;
+ bool reset;
+ bool disable_display;
+};
+extern struct i915_params i915 __read_mostly;
+
/* i915_dma.c */
void i915_update_dri1_breadcrumb(struct drm_device *dev);
extern void i915_kernel_lost_context(struct drm_device * dev);
@@ -1943,8 +2112,12 @@ extern void intel_console_resume(struct work_struct *work);
/* i915_irq.c */
void i915_queue_hangcheck(struct drm_device *dev);
-void i915_handle_error(struct drm_device *dev, bool wedged);
+__printf(3, 4)
+void i915_handle_error(struct drm_device *dev, bool wedged,
+ const char *fmt, ...);
+void gen6_set_pm_mask(struct drm_i915_private *dev_priv, u32 pm_iir,
+ int new_delay);
extern void intel_irq_init(struct drm_device *dev);
extern void intel_hpd_init(struct drm_device *dev);
@@ -1955,10 +2128,15 @@ extern void intel_uncore_check_errors(struct drm_device *dev);
extern void intel_uncore_fini(struct drm_device *dev);
void
-i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask);
+i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
+ u32 status_mask);
void
-i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask);
+i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
+ u32 status_mask);
+
+void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv);
+void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv);
/* i915_gem.c */
int i915_gem_init_ioctl(struct drm_device *dev, void *data,
@@ -2014,22 +2192,27 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
const struct drm_i915_gem_object_ops *ops);
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
size_t size);
+void i915_init_vm(struct drm_i915_private *dev_priv,
+ struct i915_address_space *vm);
void i915_gem_free_object(struct drm_gem_object *obj);
void i915_gem_vma_destroy(struct i915_vma *vma);
+#define PIN_MAPPABLE 0x1
+#define PIN_NONBLOCK 0x2
+#define PIN_GLOBAL 0x4
int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
struct i915_address_space *vm,
uint32_t alignment,
- bool map_and_fenceable,
- bool nonblocking);
-void i915_gem_object_unpin(struct drm_i915_gem_object *obj);
+ unsigned flags);
int __must_check i915_vma_unbind(struct i915_vma *vma);
-int __must_check i915_gem_object_ggtt_unbind(struct drm_i915_gem_object *obj);
int i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv);
void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
void i915_gem_lastclose(struct drm_device *dev);
+int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
+ int *needs_clflush);
+
int __must_check i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
static inline struct page *i915_gem_object_get_page(struct drm_i915_gem_object *obj, int n)
{
@@ -2096,8 +2279,10 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
}
}
+struct drm_i915_gem_request *
+i915_gem_find_active_request(struct intel_ring_buffer *ring);
+
bool i915_gem_retire_requests(struct drm_device *dev);
-void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
int __must_check i915_gem_check_wedge(struct i915_gpu_error *error,
bool interruptible);
static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
@@ -2186,6 +2371,13 @@ i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
struct i915_address_space *vm);
struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj);
+static inline bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj) {
+ struct i915_vma *vma;
+ list_for_each_entry(vma, &obj->vma_list, vma_link)
+ if (vma->pin_count > 0)
+ return true;
+ return false;
+}
/* Some GGTT VM helpers */
#define obj_to_ggtt(obj) \
@@ -2217,54 +2409,69 @@ i915_gem_obj_ggtt_size(struct drm_i915_gem_object *obj)
static inline int __must_check
i915_gem_obj_ggtt_pin(struct drm_i915_gem_object *obj,
uint32_t alignment,
- bool map_and_fenceable,
- bool nonblocking)
+ unsigned flags)
{
- return i915_gem_object_pin(obj, obj_to_ggtt(obj), alignment,
- map_and_fenceable, nonblocking);
+ return i915_gem_object_pin(obj, obj_to_ggtt(obj), alignment, flags | PIN_GLOBAL);
}
+static inline int
+i915_gem_object_ggtt_unbind(struct drm_i915_gem_object *obj)
+{
+ return i915_vma_unbind(i915_gem_obj_to_ggtt(obj));
+}
+
+void i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj);
+
/* i915_gem_context.c */
+#define ctx_to_ppgtt(ctx) container_of((ctx)->vm, struct i915_hw_ppgtt, base)
int __must_check i915_gem_context_init(struct drm_device *dev);
void i915_gem_context_fini(struct drm_device *dev);
+void i915_gem_context_reset(struct drm_device *dev);
+int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
+int i915_gem_context_enable(struct drm_i915_private *dev_priv);
void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
int i915_switch_context(struct intel_ring_buffer *ring,
- struct drm_file *file, int to_id);
+ struct drm_file *file, struct i915_hw_context *to);
+struct i915_hw_context *
+i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
void i915_gem_context_free(struct kref *ctx_ref);
static inline void i915_gem_context_reference(struct i915_hw_context *ctx)
{
- kref_get(&ctx->ref);
+ if (ctx->obj && HAS_HW_CONTEXTS(ctx->obj->base.dev))
+ kref_get(&ctx->ref);
}
static inline void i915_gem_context_unreference(struct i915_hw_context *ctx)
{
- kref_put(&ctx->ref, i915_gem_context_free);
+ if (ctx->obj && HAS_HW_CONTEXTS(ctx->obj->base.dev))
+ kref_put(&ctx->ref, i915_gem_context_free);
+}
+
+static inline bool i915_gem_context_is_default(const struct i915_hw_context *c)
+{
+ return c->id == DEFAULT_CONTEXT_ID;
}
-struct i915_ctx_hang_stats * __must_check
-i915_gem_context_get_hang_stats(struct drm_device *dev,
- struct drm_file *file,
- u32 id);
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
-/* i915_gem_gtt.c */
-void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev);
-void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
- struct drm_i915_gem_object *obj,
- enum i915_cache_level cache_level);
-void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
- struct drm_i915_gem_object *obj);
+/* i915_gem_evict.c */
+int __must_check i915_gem_evict_something(struct drm_device *dev,
+ struct i915_address_space *vm,
+ int min_size,
+ unsigned alignment,
+ unsigned cache_level,
+ unsigned flags);
+int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
+int i915_gem_evict_everything(struct drm_device *dev);
+/* i915_gem_gtt.c */
void i915_check_and_clear_faults(struct drm_device *dev);
void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
void i915_gem_restore_gtt_mappings(struct drm_device *dev);
int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
-void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
- enum i915_cache_level cache_level);
-void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj);
void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
void i915_gem_init_global_gtt(struct drm_device *dev);
void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start,
@@ -2275,18 +2482,8 @@ static inline void i915_gem_chipset_flush(struct drm_device *dev)
if (INTEL_INFO(dev)->gen < 6)
intel_gtt_chipset_flush();
}
-
-
-/* i915_gem_evict.c */
-int __must_check i915_gem_evict_something(struct drm_device *dev,
- struct i915_address_space *vm,
- int min_size,
- unsigned alignment,
- unsigned cache_level,
- bool mappable,
- bool nonblock);
-int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
-int i915_gem_evict_everything(struct drm_device *dev);
+int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt);
+bool intel_enable_ppgtt(struct drm_device *dev, bool full);
/* i915_gem_stolen.c */
int i915_gem_init_stolen(struct drm_device *dev);
@@ -2305,7 +2502,7 @@ void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj);
/* i915_gem_tiling.c */
static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
{
- drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 &&
obj->tiling_mode != I915_TILING_NONE;
@@ -2343,7 +2540,8 @@ static inline void i915_error_state_buf_release(
{
kfree(eb->buf);
}
-void i915_capture_error_state(struct drm_device *dev);
+void i915_capture_error_state(struct drm_device *dev, bool wedge,
+ const char *error_msg);
void i915_error_state_get(struct drm_device *dev,
struct i915_error_state_file_priv *error_priv);
void i915_error_state_put(struct i915_error_state_file_priv *error_priv);
@@ -2352,6 +2550,14 @@ void i915_destroy_error_state(struct drm_device *dev);
void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone);
const char *i915_cache_level_str(int type);
+/* i915_cmd_parser.c */
+void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring);
+bool i915_needs_cmd_parser(struct intel_ring_buffer *ring);
+int i915_parse_cmds(struct intel_ring_buffer *ring,
+ struct drm_i915_gem_object *batch_obj,
+ u32 batch_start_offset,
+ bool is_master);
+
/* i915_suspend.c */
extern int i915_save_state(struct drm_device *dev);
extern int i915_restore_state(struct drm_device *dev);
@@ -2425,10 +2631,12 @@ extern void intel_modeset_suspend_hw(struct drm_device *dev);
extern void intel_modeset_init(struct drm_device *dev);
extern void intel_modeset_gem_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev);
+extern void intel_connector_unregister(struct intel_connector *);
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern void intel_modeset_setup_hw_state(struct drm_device *dev,
bool force_restore);
extern void i915_redisable_vga(struct drm_device *dev);
+extern void i915_redisable_vga_power_on(struct drm_device *dev);
extern bool intel_fbc_enabled(struct drm_device *dev);
extern void intel_disable_fbc(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
@@ -2463,6 +2671,7 @@ extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
*/
void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine);
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine);
+void assert_force_wake_inactive(struct drm_i915_private *dev_priv);
int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val);
int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val);
@@ -2525,9 +2734,26 @@ void vlv_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine);
#define I915_READ_NOTRACE(reg) dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), false)
#define I915_WRITE_NOTRACE(reg, val) dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), false)
+/* Be very careful with read/write 64-bit values. On 32-bit machines, they
+ * will be implemented using 2 32-bit writes in an arbitrary order with
+ * an arbitrary delay between them. This can cause the hardware to
+ * act upon the intermediate value, possibly leading to corruption and
+ * machine death. You have been warned.
+ */
#define I915_WRITE64(reg, val) dev_priv->uncore.funcs.mmio_writeq(dev_priv, (reg), (val), true)
#define I915_READ64(reg) dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true)
+#define I915_READ64_2x32(lower_reg, upper_reg) ({ \
+ u32 upper = I915_READ(upper_reg); \
+ u32 lower = I915_READ(lower_reg); \
+ u32 tmp = I915_READ(upper_reg); \
+ if (upper != tmp) { \
+ upper = tmp; \
+ lower = I915_READ(lower_reg); \
+ WARN_ON(I915_READ(upper_reg) != upper); \
+ } \
+ (u64)upper << 32 | lower; })
+
#define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg)
#define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg)
@@ -2566,4 +2792,31 @@ timespec_to_jiffies_timeout(const struct timespec *value)
return min_t(unsigned long, MAX_JIFFY_OFFSET, j + 1);
}
+/*
+ * If you need to wait X milliseconds between events A and B, but event B
+ * doesn't happen exactly after event A, you record the timestamp (jiffies) of
+ * when event A happened, then just before event B you call this function and
+ * pass the timestamp as the first argument, and X as the second argument.
+ */
+static inline void
+wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms)
+{
+ unsigned long target_jiffies, tmp_jiffies, remaining_jiffies;
+
+ /*
+ * Don't re-read the value of "jiffies" every time since it may change
+ * behind our back and break the math.
+ */
+ tmp_jiffies = jiffies;
+ target_jiffies = timestamp_jiffies +
+ msecs_to_jiffies_timeout(to_wait_ms);
+
+ if (time_after(target_jiffies, tmp_jiffies)) {
+ remaining_jiffies = target_jiffies - tmp_jiffies;
+ while (remaining_jiffies)
+ remaining_jiffies =
+ schedule_timeout_uninterruptible(remaining_jiffies);
+ }
+}
+
#endif
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 00c836154725..6370a761d137 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -43,12 +43,6 @@ static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *o
static __must_check int
i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
bool readonly);
-static __must_check int
-i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
- struct i915_address_space *vm,
- unsigned alignment,
- bool map_and_fenceable,
- bool nonblocking);
static int i915_gem_phys_pwrite(struct drm_device *dev,
struct drm_i915_gem_object *obj,
struct drm_i915_gem_pwrite *args,
@@ -67,6 +61,7 @@ static unsigned long i915_gem_inactive_scan(struct shrinker *shrinker,
static unsigned long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
+static void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
static bool cpu_cache_is_coherent(struct drm_device *dev,
enum i915_cache_level level)
@@ -204,7 +199,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
pinned = 0;
mutex_lock(&dev->struct_mutex);
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list)
- if (obj->pin_count)
+ if (i915_gem_obj_is_pinned(obj))
pinned += i915_gem_obj_ggtt_size(obj);
mutex_unlock(&dev->struct_mutex);
@@ -332,6 +327,42 @@ __copy_from_user_swizzled(char *gpu_vaddr, int gpu_offset,
return 0;
}
+/*
+ * Pins the specified object's pages and synchronizes the object with
+ * GPU accesses. Sets needs_clflush to non-zero if the caller should
+ * flush the object from the CPU cache.
+ */
+int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
+ int *needs_clflush)
+{
+ int ret;
+
+ *needs_clflush = 0;
+
+ if (!obj->base.filp)
+ return -EINVAL;
+
+ if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) {
+ /* If we're not in the cpu read domain, set ourself into the gtt
+ * read domain and manually flush cachelines (if required). This
+ * optimizes for the case when the gpu will dirty the data
+ * anyway again before the next pread happens. */
+ *needs_clflush = !cpu_cache_is_coherent(obj->base.dev,
+ obj->cache_level);
+ ret = i915_gem_object_wait_rendering(obj, true);
+ if (ret)
+ return ret;
+ }
+
+ ret = i915_gem_object_get_pages(obj);
+ if (ret)
+ return ret;
+
+ i915_gem_object_pin_pages(obj);
+
+ return ret;
+}
+
/* Per-page copy function for the shmem pread fastpath.
* Flushes invalid cachelines before reading the target if
* needs_clflush is set. */
@@ -429,23 +460,10 @@ i915_gem_shmem_pread(struct drm_device *dev,
obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
- if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) {
- /* If we're not in the cpu read domain, set ourself into the gtt
- * read domain and manually flush cachelines (if required). This
- * optimizes for the case when the gpu will dirty the data
- * anyway again before the next pread happens. */
- needs_clflush = !cpu_cache_is_coherent(dev, obj->cache_level);
- ret = i915_gem_object_wait_rendering(obj, true);
- if (ret)
- return ret;
- }
-
- ret = i915_gem_object_get_pages(obj);
+ ret = i915_gem_obj_prepare_shmem_read(obj, &needs_clflush);
if (ret)
return ret;
- i915_gem_object_pin_pages(obj);
-
offset = args->offset;
for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents,
@@ -476,7 +494,7 @@ i915_gem_shmem_pread(struct drm_device *dev,
mutex_unlock(&dev->struct_mutex);
- if (likely(!i915_prefault_disable) && !prefaulted) {
+ if (likely(!i915.prefault_disable) && !prefaulted) {
ret = fault_in_multipages_writeable(user_data, remain);
/* Userspace is tricking us, but we've already clobbered
* its pages with the prefault and promised to write the
@@ -492,12 +510,10 @@ i915_gem_shmem_pread(struct drm_device *dev,
mutex_lock(&dev->struct_mutex);
-next_page:
- mark_page_accessed(page);
-
if (ret)
goto out;
+next_page:
remain -= page_length;
user_data += page_length;
offset += page_length;
@@ -599,13 +615,13 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
struct drm_i915_gem_pwrite *args,
struct drm_file *file)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
ssize_t remain;
loff_t offset, page_base;
char __user *user_data;
int page_offset, page_length, ret;
- ret = i915_gem_obj_ggtt_pin(obj, 0, true, true);
+ ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE | PIN_NONBLOCK);
if (ret)
goto out;
@@ -651,7 +667,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
}
out_unpin:
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
out:
return ret;
}
@@ -677,9 +693,8 @@ shmem_pwrite_fast(struct page *page, int shmem_page_offset, int page_length,
if (needs_clflush_before)
drm_clflush_virt_range(vaddr + shmem_page_offset,
page_length);
- ret = __copy_from_user_inatomic_nocache(vaddr + shmem_page_offset,
- user_data,
- page_length);
+ ret = __copy_from_user_inatomic(vaddr + shmem_page_offset,
+ user_data, page_length);
if (needs_clflush_after)
drm_clflush_virt_range(vaddr + shmem_page_offset,
page_length);
@@ -813,13 +828,10 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
mutex_lock(&dev->struct_mutex);
-next_page:
- set_page_dirty(page);
- mark_page_accessed(page);
-
if (ret)
goto out;
+next_page:
remain -= page_length;
user_data += page_length;
offset += page_length;
@@ -868,7 +880,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
args->size))
return -EFAULT;
- if (likely(!i915_prefault_disable)) {
+ if (likely(!i915.prefault_disable)) {
ret = fault_in_multipages_readable(to_user_ptr(args->data_ptr),
args->size);
if (ret)
@@ -1014,7 +1026,8 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
struct timespec *timeout,
struct drm_i915_file_private *file_priv)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
const bool irq_test_in_progress =
ACCESS_ONCE(dev_priv->gpu_error.test_irq_rings) & intel_ring_flag(ring);
struct timespec before, now;
@@ -1022,14 +1035,14 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
unsigned long timeout_expire;
int ret;
- WARN(dev_priv->pc8.irqs_disabled, "IRQs disabled\n");
+ WARN(dev_priv->pm.irqs_disabled, "IRQs disabled\n");
if (i915_seqno_passed(ring->get_seqno(ring, true), seqno))
return 0;
timeout_expire = timeout ? jiffies + timespec_to_jiffies_timeout(timeout) : 0;
- if (dev_priv->info->gen >= 6 && can_wait_boost(file_priv)) {
+ if (INTEL_INFO(dev)->gen >= 6 && can_wait_boost(file_priv)) {
gen6_rps_boost(dev_priv);
if (file_priv)
mod_delayed_work(dev_priv->wq,
@@ -1184,7 +1197,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
*/
static __must_check int
i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
- struct drm_file *file,
+ struct drm_i915_file_private *file_priv,
bool readonly)
{
struct drm_device *dev = obj->base.dev;
@@ -1211,7 +1224,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
mutex_unlock(&dev->struct_mutex);
- ret = __wait_seqno(ring, seqno, reset_counter, true, NULL, file->driver_priv);
+ ret = __wait_seqno(ring, seqno, reset_counter, true, NULL, file_priv);
mutex_lock(&dev->struct_mutex);
if (ret)
return ret;
@@ -1260,7 +1273,9 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
* We will repeat the flush holding the lock in the normal manner
* to catch cases where we are gazumped.
*/
- ret = i915_gem_object_wait_rendering__nonblocking(obj, file, !write_domain);
+ ret = i915_gem_object_wait_rendering__nonblocking(obj,
+ file->driver_priv,
+ !write_domain);
if (ret)
goto unref;
@@ -1374,7 +1389,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_i915_gem_object *obj = to_intel_bo(vma->vm_private_data);
struct drm_device *dev = obj->base.dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
pgoff_t page_offset;
unsigned long pfn;
int ret = 0;
@@ -1392,6 +1407,15 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
trace_i915_gem_object_fault(obj, page_offset, true, write);
+ /* Try to flush the object off the GPU first without holding the lock.
+ * Upon reacquiring the lock, we will perform our sanity checks and then
+ * repeat the flush holding the lock in the normal manner to catch cases
+ * where we are gazumped.
+ */
+ ret = i915_gem_object_wait_rendering__nonblocking(obj, NULL, !write);
+ if (ret)
+ goto unlock;
+
/* Access to snoopable pages through the GTT is incoherent. */
if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) {
ret = -EINVAL;
@@ -1399,7 +1423,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
}
/* Now bind it into the GTT if needed */
- ret = i915_gem_obj_ggtt_pin(obj, 0, true, false);
+ ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE);
if (ret)
goto unlock;
@@ -1420,7 +1444,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
/* Finally, remap it using the new GTT offset */
ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
unpin:
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
unlock:
mutex_unlock(&dev->struct_mutex);
out:
@@ -1453,6 +1477,7 @@ out:
ret = VM_FAULT_OOM;
break;
case -ENOSPC:
+ case -EFAULT:
ret = VM_FAULT_SIGBUS;
break;
default:
@@ -1501,7 +1526,8 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
if (!obj->fault_mappable)
return;
- drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping);
+ drm_vma_node_unmap(&obj->base.vma_node,
+ obj->base.dev->anon_inode->i_mapping);
obj->fault_mappable = false;
}
@@ -1617,8 +1643,8 @@ i915_gem_mmap_gtt(struct drm_file *file,
}
if (obj->madv != I915_MADV_WILLNEED) {
- DRM_ERROR("Attempting to mmap a purgeable buffer\n");
- ret = -EINVAL;
+ DRM_DEBUG("Attempting to mmap a purgeable buffer\n");
+ ret = -EFAULT;
goto out;
}
@@ -1971,8 +1997,8 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
return 0;
if (obj->madv != I915_MADV_WILLNEED) {
- DRM_ERROR("Attempting to obtain a purgeable object\n");
- return -EINVAL;
+ DRM_DEBUG("Attempting to obtain a purgeable object\n");
+ return -EFAULT;
}
BUG_ON(obj->pages_pin_count);
@@ -2035,13 +2061,17 @@ static void
i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
- struct i915_address_space *ggtt_vm = &dev_priv->gtt.base;
- struct i915_vma *vma = i915_gem_obj_to_vma(obj, ggtt_vm);
+ struct i915_address_space *vm;
+ struct i915_vma *vma;
BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS);
BUG_ON(!obj->active);
- list_move_tail(&vma->mm_list, &ggtt_vm->inactive_list);
+ list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
+ vma = i915_gem_obj_to_vma(obj, vm);
+ if (vma && !list_empty(&vma->mm_list))
+ list_move_tail(&vma->mm_list, &vm->inactive_list);
+ }
list_del_init(&obj->ring_list);
obj->ring = NULL;
@@ -2134,10 +2164,9 @@ int __i915_add_request(struct intel_ring_buffer *ring,
struct drm_i915_gem_object *obj,
u32 *out_seqno)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
struct drm_i915_gem_request *request;
u32 request_ring_position, request_start;
- int was_empty;
int ret;
request_start = intel_ring_get_tail(ring);
@@ -2188,7 +2217,6 @@ int __i915_add_request(struct intel_ring_buffer *ring,
i915_gem_context_reference(request->ctx);
request->emitted_jiffies = jiffies;
- was_empty = list_empty(&ring->request_list);
list_add_tail(&request->list, &ring->request_list);
request->file_priv = NULL;
@@ -2209,13 +2237,11 @@ int __i915_add_request(struct intel_ring_buffer *ring,
if (!dev_priv->ums.mm_suspended) {
i915_queue_hangcheck(ring->dev);
- if (was_empty) {
- cancel_delayed_work_sync(&dev_priv->mm.idle_work);
- queue_delayed_work(dev_priv->wq,
- &dev_priv->mm.retire_work,
- round_jiffies_up_relative(HZ));
- intel_mark_busy(dev_priv->dev);
- }
+ cancel_delayed_work_sync(&dev_priv->mm.idle_work);
+ queue_delayed_work(dev_priv->wq,
+ &dev_priv->mm.retire_work,
+ round_jiffies_up_relative(HZ));
+ intel_mark_busy(dev_priv->dev);
}
if (out_seqno)
@@ -2237,125 +2263,46 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
spin_unlock(&file_priv->mm.lock);
}
-static bool i915_head_inside_object(u32 acthd, struct drm_i915_gem_object *obj,
- struct i915_address_space *vm)
+static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
+ const struct i915_hw_context *ctx)
{
- if (acthd >= i915_gem_obj_offset(obj, vm) &&
- acthd < i915_gem_obj_offset(obj, vm) + obj->base.size)
- return true;
+ unsigned long elapsed;
- return false;
-}
+ elapsed = get_seconds() - ctx->hang_stats.guilty_ts;
-static bool i915_head_inside_request(const u32 acthd_unmasked,
- const u32 request_start,
- const u32 request_end)
-{
- const u32 acthd = acthd_unmasked & HEAD_ADDR;
+ if (ctx->hang_stats.banned)
+ return true;
- if (request_start < request_end) {
- if (acthd >= request_start && acthd < request_end)
- return true;
- } else if (request_start > request_end) {
- if (acthd >= request_start || acthd < request_end)
+ if (elapsed <= DRM_I915_CTX_BAN_PERIOD) {
+ if (!i915_gem_context_is_default(ctx)) {
+ DRM_DEBUG("context hanging too fast, banning!\n");
return true;
- }
-
- return false;
-}
-
-static struct i915_address_space *
-request_to_vm(struct drm_i915_gem_request *request)
-{
- struct drm_i915_private *dev_priv = request->ring->dev->dev_private;
- struct i915_address_space *vm;
-
- vm = &dev_priv->gtt.base;
-
- return vm;
-}
-
-static bool i915_request_guilty(struct drm_i915_gem_request *request,
- const u32 acthd, bool *inside)
-{
- /* There is a possibility that unmasked head address
- * pointing inside the ring, matches the batch_obj address range.
- * However this is extremely unlikely.
- */
- if (request->batch_obj) {
- if (i915_head_inside_object(acthd, request->batch_obj,
- request_to_vm(request))) {
- *inside = true;
+ } else if (dev_priv->gpu_error.stop_rings == 0) {
+ DRM_ERROR("gpu hanging too fast, banning!\n");
return true;
}
}
- if (i915_head_inside_request(acthd, request->head, request->tail)) {
- *inside = false;
- return true;
- }
-
return false;
}
-static bool i915_context_is_banned(const struct i915_ctx_hang_stats *hs)
+static void i915_set_reset_status(struct drm_i915_private *dev_priv,
+ struct i915_hw_context *ctx,
+ const bool guilty)
{
- const unsigned long elapsed = get_seconds() - hs->guilty_ts;
-
- if (hs->banned)
- return true;
-
- if (elapsed <= DRM_I915_CTX_BAN_PERIOD) {
- DRM_ERROR("context hanging too fast, declaring banned!\n");
- return true;
- }
-
- return false;
-}
+ struct i915_ctx_hang_stats *hs;
-static void i915_set_reset_status(struct intel_ring_buffer *ring,
- struct drm_i915_gem_request *request,
- u32 acthd)
-{
- struct i915_ctx_hang_stats *hs = NULL;
- bool inside, guilty;
- unsigned long offset = 0;
-
- /* Innocent until proven guilty */
- guilty = false;
-
- if (request->batch_obj)
- offset = i915_gem_obj_offset(request->batch_obj,
- request_to_vm(request));
+ if (WARN_ON(!ctx))
+ return;
- if (ring->hangcheck.action != HANGCHECK_WAIT &&
- i915_request_guilty(request, acthd, &inside)) {
- DRM_DEBUG("%s hung %s bo (0x%lx ctx %d) at 0x%x\n",
- ring->name,
- inside ? "inside" : "flushing",
- offset,
- request->ctx ? request->ctx->id : 0,
- acthd);
+ hs = &ctx->hang_stats;
- guilty = true;
- }
-
- /* If contexts are disabled or this is the default context, use
- * file_priv->reset_state
- */
- if (request->ctx && request->ctx->id != DEFAULT_CONTEXT_ID)
- hs = &request->ctx->hang_stats;
- else if (request->file_priv)
- hs = &request->file_priv->hang_stats;
-
- if (hs) {
- if (guilty) {
- hs->banned = i915_context_is_banned(hs);
- hs->batch_active++;
- hs->guilty_ts = get_seconds();
- } else {
- hs->batch_pending++;
- }
+ if (guilty) {
+ hs->banned = i915_context_is_banned(dev_priv, ctx);
+ hs->batch_active++;
+ hs->guilty_ts = get_seconds();
+ } else {
+ hs->batch_pending++;
}
}
@@ -2370,19 +2317,41 @@ static void i915_gem_free_request(struct drm_i915_gem_request *request)
kfree(request);
}
-static void i915_gem_reset_ring_status(struct drm_i915_private *dev_priv,
- struct intel_ring_buffer *ring)
+struct drm_i915_gem_request *
+i915_gem_find_active_request(struct intel_ring_buffer *ring)
{
- u32 completed_seqno = ring->get_seqno(ring, false);
- u32 acthd = intel_ring_get_active_head(ring);
struct drm_i915_gem_request *request;
+ u32 completed_seqno;
+
+ completed_seqno = ring->get_seqno(ring, false);
list_for_each_entry(request, &ring->request_list, list) {
if (i915_seqno_passed(completed_seqno, request->seqno))
continue;
- i915_set_reset_status(ring, request, acthd);
+ return request;
}
+
+ return NULL;
+}
+
+static void i915_gem_reset_ring_status(struct drm_i915_private *dev_priv,
+ struct intel_ring_buffer *ring)
+{
+ struct drm_i915_gem_request *request;
+ bool ring_hung;
+
+ request = i915_gem_find_active_request(ring);
+
+ if (request == NULL)
+ return;
+
+ ring_hung = ring->hangcheck.score >= HANGCHECK_SCORE_RING_HUNG;
+
+ i915_set_reset_status(dev_priv, request->ctx, ring_hung);
+
+ list_for_each_entry_continue(request, &ring->request_list, list)
+ i915_set_reset_status(dev_priv, request->ctx, false);
}
static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
@@ -2456,13 +2425,15 @@ void i915_gem_reset(struct drm_device *dev)
i915_gem_cleanup_ringbuffer(dev);
+ i915_gem_context_reset(dev);
+
i915_gem_restore_fences(dev);
}
/**
* This function clears the request list as sequence numbers are passed.
*/
-void
+static void
i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
{
uint32_t seqno;
@@ -2474,6 +2445,24 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
seqno = ring->get_seqno(ring, true);
+ /* Move any buffers on the active list that are no longer referenced
+ * by the ringbuffer to the flushing/inactive lists as appropriate,
+ * before we free the context associated with the requests.
+ */
+ while (!list_empty(&ring->active_list)) {
+ struct drm_i915_gem_object *obj;
+
+ obj = list_first_entry(&ring->active_list,
+ struct drm_i915_gem_object,
+ ring_list);
+
+ if (!i915_seqno_passed(seqno, obj->last_read_seqno))
+ break;
+
+ i915_gem_object_move_to_inactive(obj);
+ }
+
+
while (!list_empty(&ring->request_list)) {
struct drm_i915_gem_request *request;
@@ -2495,22 +2484,6 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
i915_gem_free_request(request);
}
- /* Move any buffers on the active list that are no longer referenced
- * by the ringbuffer to the flushing/inactive lists as appropriate.
- */
- while (!list_empty(&ring->active_list)) {
- struct drm_i915_gem_object *obj;
-
- obj = list_first_entry(&ring->active_list,
- struct drm_i915_gem_object,
- ring_list);
-
- if (!i915_seqno_passed(seqno, obj->last_read_seqno))
- break;
-
- i915_gem_object_move_to_inactive(obj);
- }
-
if (unlikely(ring->trace_irq_seqno &&
i915_seqno_passed(seqno, ring->trace_irq_seqno))) {
ring->irq_put(ring);
@@ -2523,7 +2496,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
bool
i915_gem_retire_requests(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
bool idle = true;
int i;
@@ -2615,7 +2588,7 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj)
int
i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_wait *args = data;
struct drm_i915_gem_object *obj;
struct intel_ring_buffer *ring = NULL;
@@ -2750,22 +2723,18 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
int i915_vma_unbind(struct i915_vma *vma)
{
struct drm_i915_gem_object *obj = vma->obj;
- drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
int ret;
- /* For now we only ever use 1 vma per object */
- WARN_ON(!list_is_singular(&obj->vma_list));
-
if (list_empty(&vma->vma_link))
return 0;
if (!drm_mm_node_allocated(&vma->node)) {
i915_gem_vma_destroy(vma);
-
return 0;
}
- if (obj->pin_count)
+ if (vma->pin_count)
return -EBUSY;
BUG_ON(obj->pages == NULL);
@@ -2787,15 +2756,11 @@ int i915_vma_unbind(struct i915_vma *vma)
trace_i915_vma_unbind(vma);
- if (obj->has_global_gtt_mapping)
- i915_gem_gtt_unbind_object(obj);
- if (obj->has_aliasing_ppgtt_mapping) {
- i915_ppgtt_unbind_object(dev_priv->mm.aliasing_ppgtt, obj);
- obj->has_aliasing_ppgtt_mapping = 0;
- }
+ vma->unbind_vma(vma);
+
i915_gem_gtt_finish_object(obj);
- list_del(&vma->mm_list);
+ list_del_init(&vma->mm_list);
/* Avoid an unnecessary call to unbind on rebind. */
if (i915_is_ggtt(vma->vm))
obj->map_and_fenceable = true;
@@ -2817,35 +2782,15 @@ int i915_vma_unbind(struct i915_vma *vma)
return 0;
}
-/**
- * Unbinds an object from the global GTT aperture.
- */
-int
-i915_gem_object_ggtt_unbind(struct drm_i915_gem_object *obj)
-{
- struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
- struct i915_address_space *ggtt = &dev_priv->gtt.base;
-
- if (!i915_gem_obj_ggtt_bound(obj))
- return 0;
-
- if (obj->pin_count)
- return -EBUSY;
-
- BUG_ON(obj->pages == NULL);
-
- return i915_vma_unbind(i915_gem_obj_to_vma(obj, ggtt));
-}
-
int i915_gpu_idle(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
int ret, i;
/* Flush everything onto the inactive list. */
for_each_ring(ring, dev_priv, i) {
- ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID);
+ ret = i915_switch_context(ring, NULL, ring->default_context);
if (ret)
return ret;
@@ -2860,7 +2805,7 @@ int i915_gpu_idle(struct drm_device *dev)
static void i965_write_fence_reg(struct drm_device *dev, int reg,
struct drm_i915_gem_object *obj)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int fence_reg;
int fence_pitch_shift;
@@ -2912,7 +2857,7 @@ static void i965_write_fence_reg(struct drm_device *dev, int reg,
static void i915_write_fence_reg(struct drm_device *dev, int reg,
struct drm_i915_gem_object *obj)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 val;
if (obj) {
@@ -2956,7 +2901,7 @@ static void i915_write_fence_reg(struct drm_device *dev, int reg,
static void i830_write_fence_reg(struct drm_device *dev, int reg,
struct drm_i915_gem_object *obj)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t val;
if (obj) {
@@ -3259,18 +3204,17 @@ static void i915_gem_verify_gtt(struct drm_device *dev)
/**
* Finds free space in the GTT aperture and binds the object there.
*/
-static int
+static struct i915_vma *
i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
struct i915_address_space *vm,
unsigned alignment,
- bool map_and_fenceable,
- bool nonblocking)
+ unsigned flags)
{
struct drm_device *dev = obj->base.dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 size, fence_size, fence_alignment, unfenced_alignment;
size_t gtt_max =
- map_and_fenceable ? dev_priv->gtt.mappable_end : vm->total;
+ flags & PIN_MAPPABLE ? dev_priv->gtt.mappable_end : vm->total;
struct i915_vma *vma;
int ret;
@@ -3282,57 +3226,49 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
obj->tiling_mode, true);
unfenced_alignment =
i915_gem_get_gtt_alignment(dev,
- obj->base.size,
- obj->tiling_mode, false);
+ obj->base.size,
+ obj->tiling_mode, false);
if (alignment == 0)
- alignment = map_and_fenceable ? fence_alignment :
+ alignment = flags & PIN_MAPPABLE ? fence_alignment :
unfenced_alignment;
- if (map_and_fenceable && alignment & (fence_alignment - 1)) {
- DRM_ERROR("Invalid object alignment requested %u\n", alignment);
- return -EINVAL;
+ if (flags & PIN_MAPPABLE && alignment & (fence_alignment - 1)) {
+ DRM_DEBUG("Invalid object alignment requested %u\n", alignment);
+ return ERR_PTR(-EINVAL);
}
- size = map_and_fenceable ? fence_size : obj->base.size;
+ size = flags & PIN_MAPPABLE ? fence_size : obj->base.size;
/* If the object is bigger than the entire aperture, reject it early
* before evicting everything in a vain attempt to find space.
*/
if (obj->base.size > gtt_max) {
- DRM_ERROR("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%zu\n",
+ DRM_DEBUG("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%zu\n",
obj->base.size,
- map_and_fenceable ? "mappable" : "total",
+ flags & PIN_MAPPABLE ? "mappable" : "total",
gtt_max);
- return -E2BIG;
+ return ERR_PTR(-E2BIG);
}
ret = i915_gem_object_get_pages(obj);
if (ret)
- return ret;
+ return ERR_PTR(ret);
i915_gem_object_pin_pages(obj);
- BUG_ON(!i915_is_ggtt(vm));
-
vma = i915_gem_obj_lookup_or_create_vma(obj, vm);
- if (IS_ERR(vma)) {
- ret = PTR_ERR(vma);
+ if (IS_ERR(vma))
goto err_unpin;
- }
-
- /* For now we only ever use 1 vma per object */
- WARN_ON(!list_is_singular(&obj->vma_list));
search_free:
ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node,
size, alignment,
obj->cache_level, 0, gtt_max,
- DRM_MM_SEARCH_DEFAULT);
+ DRM_MM_SEARCH_DEFAULT,
+ DRM_MM_CREATE_DEFAULT);
if (ret) {
ret = i915_gem_evict_something(dev, vm, size, alignment,
- obj->cache_level,
- map_and_fenceable,
- nonblocking);
+ obj->cache_level, flags);
if (ret == 0)
goto search_free;
@@ -3363,19 +3299,23 @@ search_free:
obj->map_and_fenceable = mappable && fenceable;
}
- WARN_ON(map_and_fenceable && !obj->map_and_fenceable);
+ WARN_ON(flags & PIN_MAPPABLE && !obj->map_and_fenceable);
+
+ trace_i915_vma_bind(vma, flags);
+ vma->bind_vma(vma, obj->cache_level,
+ flags & (PIN_MAPPABLE | PIN_GLOBAL) ? GLOBAL_BIND : 0);
- trace_i915_vma_bind(vma, map_and_fenceable);
i915_gem_verify_gtt(dev);
- return 0;
+ return vma;
err_remove_node:
drm_mm_remove_node(&vma->node);
err_free_vma:
i915_gem_vma_destroy(vma);
+ vma = ERR_PTR(ret);
err_unpin:
i915_gem_object_unpin_pages(obj);
- return ret;
+ return vma;
}
bool
@@ -3470,7 +3410,7 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj,
int
i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
{
- drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
uint32_t old_write_domain, old_read_domains;
int ret;
@@ -3528,25 +3468,22 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
enum i915_cache_level cache_level)
{
struct drm_device *dev = obj->base.dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct i915_vma *vma;
+ struct i915_vma *vma, *next;
int ret;
if (obj->cache_level == cache_level)
return 0;
- if (obj->pin_count) {
+ if (i915_gem_obj_is_pinned(obj)) {
DRM_DEBUG("can not change the cache level of pinned objects\n");
return -EBUSY;
}
- list_for_each_entry(vma, &obj->vma_list, vma_link) {
+ list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) {
if (!i915_gem_valid_gtt_space(dev, &vma->node, cache_level)) {
ret = i915_vma_unbind(vma);
if (ret)
return ret;
-
- break;
}
}
@@ -3567,11 +3504,10 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
return ret;
}
- if (obj->has_global_gtt_mapping)
- i915_gem_gtt_bind_object(obj, cache_level);
- if (obj->has_aliasing_ppgtt_mapping)
- i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
- obj, cache_level);
+ list_for_each_entry(vma, &obj->vma_list, vma_link)
+ if (drm_mm_node_allocated(&vma->node))
+ vma->bind_vma(vma, cache_level,
+ obj->has_global_gtt_mapping ? GLOBAL_BIND : 0);
}
list_for_each_entry(vma, &obj->vma_list, vma_link)
@@ -3695,7 +3631,7 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
* subtracting the potential reference by the user, any pin_count
* remains, it must be due to another use by the display engine.
*/
- return obj->pin_count - !!obj->user_pin_count;
+ return i915_gem_obj_to_ggtt(obj)->pin_count - !!obj->user_pin_count;
}
/*
@@ -3740,7 +3676,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
* (e.g. libkms for the bootup splash), we have to ensure that we
* always use map_and_fenceable for all scanout buffers.
*/
- ret = i915_gem_obj_ggtt_pin(obj, alignment, true, false);
+ ret = i915_gem_obj_ggtt_pin(obj, alignment, PIN_MAPPABLE);
if (ret)
goto err_unpin_display;
@@ -3769,7 +3705,7 @@ err_unpin_display:
void
i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj)
{
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
obj->pin_display = is_pin_display(obj);
}
@@ -3896,65 +3832,63 @@ int
i915_gem_object_pin(struct drm_i915_gem_object *obj,
struct i915_address_space *vm,
uint32_t alignment,
- bool map_and_fenceable,
- bool nonblocking)
+ unsigned flags)
{
struct i915_vma *vma;
int ret;
- if (WARN_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
- return -EBUSY;
-
- WARN_ON(map_and_fenceable && !i915_is_ggtt(vm));
+ if (WARN_ON(flags & (PIN_GLOBAL | PIN_MAPPABLE) && !i915_is_ggtt(vm)))
+ return -EINVAL;
vma = i915_gem_obj_to_vma(obj, vm);
-
if (vma) {
+ if (WARN_ON(vma->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
+ return -EBUSY;
+
if ((alignment &&
vma->node.start & (alignment - 1)) ||
- (map_and_fenceable && !obj->map_and_fenceable)) {
- WARN(obj->pin_count,
+ (flags & PIN_MAPPABLE && !obj->map_and_fenceable)) {
+ WARN(vma->pin_count,
"bo is already pinned with incorrect alignment:"
" offset=%lx, req.alignment=%x, req.map_and_fenceable=%d,"
" obj->map_and_fenceable=%d\n",
i915_gem_obj_offset(obj, vm), alignment,
- map_and_fenceable,
+ flags & PIN_MAPPABLE,
obj->map_and_fenceable);
ret = i915_vma_unbind(vma);
if (ret)
return ret;
+
+ vma = NULL;
}
}
- if (!i915_gem_obj_bound(obj, vm)) {
- struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
-
- ret = i915_gem_object_bind_to_vm(obj, vm, alignment,
- map_and_fenceable,
- nonblocking);
- if (ret)
- return ret;
-
- if (!dev_priv->mm.aliasing_ppgtt)
- i915_gem_gtt_bind_object(obj, obj->cache_level);
+ if (vma == NULL || !drm_mm_node_allocated(&vma->node)) {
+ vma = i915_gem_object_bind_to_vm(obj, vm, alignment, flags);
+ if (IS_ERR(vma))
+ return PTR_ERR(vma);
}
- if (!obj->has_global_gtt_mapping && map_and_fenceable)
- i915_gem_gtt_bind_object(obj, obj->cache_level);
+ if (flags & PIN_GLOBAL && !obj->has_global_gtt_mapping)
+ vma->bind_vma(vma, obj->cache_level, GLOBAL_BIND);
- obj->pin_count++;
- obj->pin_mappable |= map_and_fenceable;
+ vma->pin_count++;
+ if (flags & PIN_MAPPABLE)
+ obj->pin_mappable |= true;
return 0;
}
void
-i915_gem_object_unpin(struct drm_i915_gem_object *obj)
+i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
{
- BUG_ON(obj->pin_count == 0);
- BUG_ON(!i915_gem_obj_bound_any(obj));
+ struct i915_vma *vma = i915_gem_obj_to_ggtt(obj);
- if (--obj->pin_count == 0)
+ BUG_ON(!vma);
+ BUG_ON(vma->pin_count == 0);
+ BUG_ON(!i915_gem_obj_ggtt_bound(obj));
+
+ if (--vma->pin_count == 0)
obj->pin_mappable = false;
}
@@ -3966,6 +3900,9 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_object *obj;
int ret;
+ if (INTEL_INFO(dev)->gen >= 6)
+ return -ENODEV;
+
ret = i915_mutex_lock_interruptible(dev);
if (ret)
return ret;
@@ -3977,13 +3914,13 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
}
if (obj->madv != I915_MADV_WILLNEED) {
- DRM_ERROR("Attempting to pin a purgeable buffer\n");
- ret = -EINVAL;
+ DRM_DEBUG("Attempting to pin a purgeable buffer\n");
+ ret = -EFAULT;
goto out;
}
if (obj->pin_filp != NULL && obj->pin_filp != file) {
- DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
+ DRM_DEBUG("Already pinned in i915_gem_pin_ioctl(): %d\n",
args->handle);
ret = -EINVAL;
goto out;
@@ -3995,7 +3932,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
}
if (obj->user_pin_count == 0) {
- ret = i915_gem_obj_ggtt_pin(obj, args->alignment, true, false);
+ ret = i915_gem_obj_ggtt_pin(obj, args->alignment, PIN_MAPPABLE);
if (ret)
goto out;
}
@@ -4030,7 +3967,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
}
if (obj->pin_filp != file) {
- DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n",
+ DRM_DEBUG("Not pinned by caller in i915_gem_pin_ioctl(): %d\n",
args->handle);
ret = -EINVAL;
goto out;
@@ -4038,7 +3975,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
obj->user_pin_count--;
if (obj->user_pin_count == 0) {
obj->pin_filp = NULL;
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
}
out:
@@ -4118,7 +4055,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
goto unlock;
}
- if (obj->pin_count) {
+ if (i915_gem_obj_is_pinned(obj)) {
ret = -EINVAL;
goto out;
}
@@ -4219,7 +4156,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
{
struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
struct drm_device *dev = obj->base.dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_vma *vma, *next;
intel_runtime_pm_get(dev_priv);
@@ -4229,12 +4166,11 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
if (obj->phys_obj)
i915_gem_detach_phys_object(dev, obj);
- obj->pin_count = 0;
- /* NB: 0 or 1 elements */
- WARN_ON(!list_empty(&obj->vma_list) &&
- !list_is_singular(&obj->vma_list));
list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) {
- int ret = i915_vma_unbind(vma);
+ int ret;
+
+ vma->pin_count = 0;
+ ret = i915_vma_unbind(vma);
if (WARN_ON(ret == -ERESTARTSYS)) {
bool was_interruptible;
@@ -4283,41 +4219,6 @@ struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
return NULL;
}
-static struct i915_vma *__i915_gem_vma_create(struct drm_i915_gem_object *obj,
- struct i915_address_space *vm)
-{
- struct i915_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL);
- if (vma == NULL)
- return ERR_PTR(-ENOMEM);
-
- INIT_LIST_HEAD(&vma->vma_link);
- INIT_LIST_HEAD(&vma->mm_list);
- INIT_LIST_HEAD(&vma->exec_list);
- vma->vm = vm;
- vma->obj = obj;
-
- /* Keep GGTT vmas first to make debug easier */
- if (i915_is_ggtt(vm))
- list_add(&vma->vma_link, &obj->vma_list);
- else
- list_add_tail(&vma->vma_link, &obj->vma_list);
-
- return vma;
-}
-
-struct i915_vma *
-i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
- struct i915_address_space *vm)
-{
- struct i915_vma *vma;
-
- vma = i915_gem_obj_to_vma(obj, vm);
- if (!vma)
- vma = __i915_gem_vma_create(obj, vm);
-
- return vma;
-}
-
void i915_gem_vma_destroy(struct i915_vma *vma)
{
WARN_ON(vma->node.allocated);
@@ -4334,7 +4235,7 @@ void i915_gem_vma_destroy(struct i915_vma *vma)
int
i915_gem_suspend(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
mutex_lock(&dev->struct_mutex);
@@ -4376,7 +4277,7 @@ err:
int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 reg_base = GEN7_L3LOG_BASE + (slice * 0x200);
u32 *remap_info = dev_priv->l3_parity.remap_info[slice];
int i, ret;
@@ -4406,7 +4307,7 @@ int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice)
void i915_gem_init_swizzling(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (INTEL_INFO(dev)->gen < 5 ||
dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_NONE)
@@ -4494,7 +4395,7 @@ cleanup_render_ring:
int
i915_gem_init_hw(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret, i;
if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
@@ -4508,9 +4409,15 @@ i915_gem_init_hw(struct drm_device *dev)
LOWER_SLICE_ENABLED : LOWER_SLICE_DISABLED);
if (HAS_PCH_NOP(dev)) {
- u32 temp = I915_READ(GEN7_MSG_CTL);
- temp &= ~(WAIT_FOR_PCH_FLR_ACK | WAIT_FOR_PCH_RESET_ACK);
- I915_WRITE(GEN7_MSG_CTL, temp);
+ if (IS_IVYBRIDGE(dev)) {
+ u32 temp = I915_READ(GEN7_MSG_CTL);
+ temp &= ~(WAIT_FOR_PCH_FLR_ACK | WAIT_FOR_PCH_RESET_ACK);
+ I915_WRITE(GEN7_MSG_CTL, temp);
+ } else if (INTEL_INFO(dev)->gen >= 7) {
+ u32 temp = I915_READ(HSW_NDE_RSTWRN_OPT);
+ temp &= ~RESET_PCH_HANDSHAKE_ENABLE;
+ I915_WRITE(HSW_NDE_RSTWRN_OPT, temp);
+ }
}
i915_gem_init_swizzling(dev);
@@ -4523,25 +4430,23 @@ i915_gem_init_hw(struct drm_device *dev)
i915_gem_l3_remap(&dev_priv->ring[RCS], i);
/*
- * XXX: There was some w/a described somewhere suggesting loading
- * contexts before PPGTT.
+ * XXX: Contexts should only be initialized once. Doing a switch to the
+ * default context switch however is something we'd like to do after
+ * reset or thaw (the latter may not actually be necessary for HW, but
+ * goes with our code better). Context switching requires rings (for
+ * the do_switch), but before enabling PPGTT. So don't move this.
*/
- ret = i915_gem_context_init(dev);
+ ret = i915_gem_context_enable(dev_priv);
if (ret) {
- i915_gem_cleanup_ringbuffer(dev);
- DRM_ERROR("Context initialization failed %d\n", ret);
- return ret;
- }
-
- if (dev_priv->mm.aliasing_ppgtt) {
- ret = dev_priv->mm.aliasing_ppgtt->enable(dev);
- if (ret) {
- i915_gem_cleanup_aliasing_ppgtt(dev);
- DRM_INFO("PPGTT enable failed. This is not fatal, but unexpected\n");
- }
+ DRM_ERROR("Context enable failed %d\n", ret);
+ goto err_out;
}
return 0;
+
+err_out:
+ i915_gem_cleanup_ringbuffer(dev);
+ return ret;
}
int i915_gem_init(struct drm_device *dev)
@@ -4560,10 +4465,18 @@ int i915_gem_init(struct drm_device *dev)
i915_gem_init_global_gtt(dev);
+ ret = i915_gem_context_init(dev);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+
ret = i915_gem_init_hw(dev);
mutex_unlock(&dev->struct_mutex);
if (ret) {
- i915_gem_cleanup_aliasing_ppgtt(dev);
+ WARN_ON(dev_priv->mm.aliasing_ppgtt);
+ i915_gem_context_fini(dev);
+ drm_mm_takedown(&dev_priv->gtt.base.mm);
return ret;
}
@@ -4576,7 +4489,7 @@ int i915_gem_init(struct drm_device *dev)
void
i915_gem_cleanup_ringbuffer(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
int i;
@@ -4658,20 +4571,22 @@ init_ring_lists(struct intel_ring_buffer *ring)
INIT_LIST_HEAD(&ring->request_list);
}
-static void i915_init_vm(struct drm_i915_private *dev_priv,
- struct i915_address_space *vm)
+void i915_init_vm(struct drm_i915_private *dev_priv,
+ struct i915_address_space *vm)
{
+ if (!i915_is_ggtt(vm))
+ drm_mm_init(&vm->mm, vm->start, vm->total);
vm->dev = dev_priv->dev;
INIT_LIST_HEAD(&vm->active_list);
INIT_LIST_HEAD(&vm->inactive_list);
INIT_LIST_HEAD(&vm->global_link);
- list_add(&vm->global_link, &dev_priv->vm_list);
+ list_add_tail(&vm->global_link, &dev_priv->vm_list);
}
void
i915_gem_load(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int i;
dev_priv->slab =
@@ -4738,7 +4653,7 @@ i915_gem_load(struct drm_device *dev)
static int i915_gem_init_phys_object(struct drm_device *dev,
int id, int size, int align)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_phys_object *phys_obj;
int ret;
@@ -4770,7 +4685,7 @@ kfree_obj:
static void i915_gem_free_phys_object(struct drm_device *dev, int id)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_phys_object *phys_obj;
if (!dev_priv->mm.phys_objs[id - 1])
@@ -4837,7 +4752,7 @@ i915_gem_attach_phys_object(struct drm_device *dev,
int align)
{
struct address_space *mapping = file_inode(obj->base.filp)->i_mapping;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
int page_count;
int i;
@@ -4950,6 +4865,7 @@ i915_gem_file_idle_work_handler(struct work_struct *work)
int i915_gem_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_i915_file_private *file_priv;
+ int ret;
DRM_DEBUG_DRIVER("\n");
@@ -4959,15 +4875,18 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file)
file->driver_priv = file_priv;
file_priv->dev_priv = dev->dev_private;
+ file_priv->file = file;
spin_lock_init(&file_priv->mm.lock);
INIT_LIST_HEAD(&file_priv->mm.request_list);
INIT_DELAYED_WORK(&file_priv->mm.idle_work,
i915_gem_file_idle_work_handler);
- idr_init(&file_priv->context_idr);
+ ret = i915_gem_context_open(dev, file);
+ if (ret)
+ kfree(file_priv);
- return 0;
+ return ret;
}
static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
@@ -5014,7 +4933,7 @@ i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
if (obj->active)
continue;
- if (obj->pin_count == 0 && obj->pages_pin_count == 0)
+ if (!i915_gem_obj_is_pinned(obj) && obj->pages_pin_count == 0)
count += obj->base.size >> PAGE_SHIFT;
}
@@ -5031,7 +4950,8 @@ unsigned long i915_gem_obj_offset(struct drm_i915_gem_object *o,
struct drm_i915_private *dev_priv = o->base.dev->dev_private;
struct i915_vma *vma;
- if (vm == &dev_priv->mm.aliasing_ppgtt->base)
+ if (!dev_priv->mm.aliasing_ppgtt ||
+ vm == &dev_priv->mm.aliasing_ppgtt->base)
vm = &dev_priv->gtt.base;
BUG_ON(list_empty(&o->vma_list));
@@ -5072,7 +4992,8 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o,
struct drm_i915_private *dev_priv = o->base.dev->dev_private;
struct i915_vma *vma;
- if (vm == &dev_priv->mm.aliasing_ppgtt->base)
+ if (!dev_priv->mm.aliasing_ppgtt ||
+ vm == &dev_priv->mm.aliasing_ppgtt->base)
vm = &dev_priv->gtt.base;
BUG_ON(list_empty(&o->vma_list));
@@ -5127,7 +5048,7 @@ struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
return NULL;
vma = list_first_entry(&obj->vma_list, typeof(*vma), vma_link);
- if (WARN_ON(vma->vm != obj_to_ggtt(obj)))
+ if (vma->vm != obj_to_ggtt(obj))
return NULL;
return vma;
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index e08acaba5402..6043062ffce7 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -93,11 +93,63 @@
* I've seen in a spec to date, and that was a workaround for a non-shipping
* part. It should be safe to decrease this, but it's more future proof as is.
*/
-#define CONTEXT_ALIGN (64<<10)
+#define GEN6_CONTEXT_ALIGN (64<<10)
+#define GEN7_CONTEXT_ALIGN 4096
-static struct i915_hw_context *
-i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
-static int do_switch(struct i915_hw_context *to);
+static int do_switch(struct intel_ring_buffer *ring,
+ struct i915_hw_context *to);
+
+static void do_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_address_space *vm = &ppgtt->base;
+
+ if (ppgtt == dev_priv->mm.aliasing_ppgtt ||
+ (list_empty(&vm->active_list) && list_empty(&vm->inactive_list))) {
+ ppgtt->base.cleanup(&ppgtt->base);
+ return;
+ }
+
+ /*
+ * Make sure vmas are unbound before we take down the drm_mm
+ *
+ * FIXME: Proper refcounting should take care of this, this shouldn't be
+ * needed at all.
+ */
+ if (!list_empty(&vm->active_list)) {
+ struct i915_vma *vma;
+
+ list_for_each_entry(vma, &vm->active_list, mm_list)
+ if (WARN_ON(list_empty(&vma->vma_link) ||
+ list_is_singular(&vma->vma_link)))
+ break;
+
+ i915_gem_evict_vm(&ppgtt->base, true);
+ } else {
+ i915_gem_retire_requests(dev);
+ i915_gem_evict_vm(&ppgtt->base, false);
+ }
+
+ ppgtt->base.cleanup(&ppgtt->base);
+}
+
+static void ppgtt_release(struct kref *kref)
+{
+ struct i915_hw_ppgtt *ppgtt =
+ container_of(kref, struct i915_hw_ppgtt, ref);
+
+ do_ppgtt_cleanup(ppgtt);
+ kfree(ppgtt);
+}
+
+static size_t get_context_alignment(struct drm_device *dev)
+{
+ if (IS_GEN6(dev))
+ return GEN6_CONTEXT_ALIGN;
+
+ return GEN7_CONTEXT_ALIGN;
+}
static int get_context_size(struct drm_device *dev)
{
@@ -131,14 +183,44 @@ void i915_gem_context_free(struct kref *ctx_ref)
{
struct i915_hw_context *ctx = container_of(ctx_ref,
typeof(*ctx), ref);
+ struct i915_hw_ppgtt *ppgtt = NULL;
- list_del(&ctx->link);
+ /* We refcount even the aliasing PPGTT to keep the code symmetric */
+ if (USES_PPGTT(ctx->obj->base.dev))
+ ppgtt = ctx_to_ppgtt(ctx);
+
+ /* XXX: Free up the object before tearing down the address space, in
+ * case we're bound in the PPGTT */
drm_gem_object_unreference(&ctx->obj->base);
+
+ if (ppgtt)
+ kref_put(&ppgtt->ref, ppgtt_release);
+ list_del(&ctx->link);
kfree(ctx);
}
+static struct i915_hw_ppgtt *
+create_vm_for_ctx(struct drm_device *dev, struct i915_hw_context *ctx)
+{
+ struct i915_hw_ppgtt *ppgtt;
+ int ret;
+
+ ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
+ if (!ppgtt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = i915_gem_init_ppgtt(dev, ppgtt);
+ if (ret) {
+ kfree(ppgtt);
+ return ERR_PTR(ret);
+ }
+
+ ppgtt->ctx = ctx;
+ return ppgtt;
+}
+
static struct i915_hw_context *
-create_hw_context(struct drm_device *dev,
+__create_hw_context(struct drm_device *dev,
struct drm_i915_file_private *file_priv)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -166,18 +248,13 @@ create_hw_context(struct drm_device *dev,
goto err_out;
}
- /* The ring associated with the context object is handled by the normal
- * object tracking code. We give an initial ring value simple to pass an
- * assertion in the context switch code.
- */
- ctx->ring = &dev_priv->ring[RCS];
list_add_tail(&ctx->link, &dev_priv->context_list);
/* Default context will never have a file_priv */
if (file_priv == NULL)
return ctx;
- ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0,
+ ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID, 0,
GFP_KERNEL);
if (ret < 0)
goto err_out;
@@ -196,67 +273,136 @@ err_out:
return ERR_PTR(ret);
}
-static inline bool is_default_context(struct i915_hw_context *ctx)
-{
- return (ctx == ctx->ring->default_context);
-}
-
/**
* The default context needs to exist per ring that uses contexts. It stores the
* context state of the GPU for applications that don't utilize HW contexts, as
* well as an idle case.
*/
-static int create_default_context(struct drm_i915_private *dev_priv)
+static struct i915_hw_context *
+i915_gem_create_context(struct drm_device *dev,
+ struct drm_i915_file_private *file_priv,
+ bool create_vm)
{
+ const bool is_global_default_ctx = file_priv == NULL;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_hw_context *ctx;
- int ret;
+ int ret = 0;
- BUG_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
- ctx = create_hw_context(dev_priv->dev, NULL);
+ ctx = __create_hw_context(dev, file_priv);
if (IS_ERR(ctx))
- return PTR_ERR(ctx);
-
- /* We may need to do things with the shrinker which require us to
- * immediately switch back to the default context. This can cause a
- * problem as pinning the default context also requires GTT space which
- * may not be available. To avoid this we always pin the
- * default context.
- */
- ret = i915_gem_obj_ggtt_pin(ctx->obj, CONTEXT_ALIGN, false, false);
- if (ret) {
- DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
- goto err_destroy;
- }
+ return ctx;
- ret = do_switch(ctx);
- if (ret) {
- DRM_DEBUG_DRIVER("Switch failed %d\n", ret);
- goto err_unpin;
+ if (is_global_default_ctx) {
+ /* We may need to do things with the shrinker which
+ * require us to immediately switch back to the default
+ * context. This can cause a problem as pinning the
+ * default context also requires GTT space which may not
+ * be available. To avoid this we always pin the default
+ * context.
+ */
+ ret = i915_gem_obj_ggtt_pin(ctx->obj,
+ get_context_alignment(dev), 0);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
+ goto err_destroy;
+ }
}
- dev_priv->ring[RCS].default_context = ctx;
+ if (create_vm) {
+ struct i915_hw_ppgtt *ppgtt = create_vm_for_ctx(dev, ctx);
+
+ if (IS_ERR_OR_NULL(ppgtt)) {
+ DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n",
+ PTR_ERR(ppgtt));
+ ret = PTR_ERR(ppgtt);
+ goto err_unpin;
+ } else
+ ctx->vm = &ppgtt->base;
+
+ /* This case is reserved for the global default context and
+ * should only happen once. */
+ if (is_global_default_ctx) {
+ if (WARN_ON(dev_priv->mm.aliasing_ppgtt)) {
+ ret = -EEXIST;
+ goto err_unpin;
+ }
+
+ dev_priv->mm.aliasing_ppgtt = ppgtt;
+ }
+ } else if (USES_PPGTT(dev)) {
+ /* For platforms which only have aliasing PPGTT, we fake the
+ * address space and refcounting. */
+ ctx->vm = &dev_priv->mm.aliasing_ppgtt->base;
+ kref_get(&dev_priv->mm.aliasing_ppgtt->ref);
+ } else
+ ctx->vm = &dev_priv->gtt.base;
- DRM_DEBUG_DRIVER("Default HW context loaded\n");
- return 0;
+ return ctx;
err_unpin:
- i915_gem_object_unpin(ctx->obj);
+ if (is_global_default_ctx)
+ i915_gem_object_ggtt_unpin(ctx->obj);
err_destroy:
i915_gem_context_unreference(ctx);
- return ret;
+ return ERR_PTR(ret);
+}
+
+void i915_gem_context_reset(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_ring_buffer *ring;
+ int i;
+
+ if (!HAS_HW_CONTEXTS(dev))
+ return;
+
+ /* Prevent the hardware from restoring the last context (which hung) on
+ * the next switch */
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct i915_hw_context *dctx;
+ if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
+ continue;
+
+ /* Do a fake switch to the default context */
+ ring = &dev_priv->ring[i];
+ dctx = ring->default_context;
+ if (WARN_ON(!dctx))
+ continue;
+
+ if (!ring->last_context)
+ continue;
+
+ if (ring->last_context == dctx)
+ continue;
+
+ if (i == RCS) {
+ WARN_ON(i915_gem_obj_ggtt_pin(dctx->obj,
+ get_context_alignment(dev), 0));
+ /* Fake a finish/inactive */
+ dctx->obj->base.write_domain = 0;
+ dctx->obj->active = 0;
+ }
+
+ i915_gem_context_unreference(ring->last_context);
+ i915_gem_context_reference(dctx);
+ ring->last_context = dctx;
+ }
}
int i915_gem_context_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int ret;
+ struct intel_ring_buffer *ring;
+ int i;
if (!HAS_HW_CONTEXTS(dev))
return 0;
- /* If called from reset, or thaw... we've been here already */
- if (dev_priv->ring[RCS].default_context)
+ /* Init should only be called once per module load. Eventually the
+ * restriction on the context_disabled check can be loosened. */
+ if (WARN_ON(dev_priv->ring[RCS].default_context))
return 0;
dev_priv->hw_context_size = round_up(get_context_size(dev), 4096);
@@ -266,11 +412,23 @@ int i915_gem_context_init(struct drm_device *dev)
return -E2BIG;
}
- ret = create_default_context(dev_priv);
- if (ret) {
- DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed %d\n",
- ret);
- return ret;
+ dev_priv->ring[RCS].default_context =
+ i915_gem_create_context(dev, NULL, USES_PPGTT(dev));
+
+ if (IS_ERR_OR_NULL(dev_priv->ring[RCS].default_context)) {
+ DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed %ld\n",
+ PTR_ERR(dev_priv->ring[RCS].default_context));
+ return PTR_ERR(dev_priv->ring[RCS].default_context);
+ }
+
+ for (i = RCS + 1; i < I915_NUM_RINGS; i++) {
+ if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
+ continue;
+
+ ring = &dev_priv->ring[i];
+
+ /* NB: RCS will hold a ref for all rings */
+ ring->default_context = dev_priv->ring[RCS].default_context;
}
DRM_DEBUG_DRIVER("HW context support initialized\n");
@@ -281,6 +439,7 @@ void i915_gem_context_fini(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context;
+ int i;
if (!HAS_HW_CONTEXTS(dev))
return;
@@ -300,59 +459,129 @@ void i915_gem_context_fini(struct drm_device *dev)
if (dev_priv->ring[RCS].last_context == dctx) {
/* Fake switch to NULL context */
WARN_ON(dctx->obj->active);
- i915_gem_object_unpin(dctx->obj);
+ i915_gem_object_ggtt_unpin(dctx->obj);
i915_gem_context_unreference(dctx);
+ dev_priv->ring[RCS].last_context = NULL;
}
- i915_gem_object_unpin(dctx->obj);
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct intel_ring_buffer *ring = &dev_priv->ring[i];
+ if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
+ continue;
+
+ if (ring->last_context)
+ i915_gem_context_unreference(ring->last_context);
+
+ ring->default_context = NULL;
+ ring->last_context = NULL;
+ }
+
+ i915_gem_object_ggtt_unpin(dctx->obj);
i915_gem_context_unreference(dctx);
- dev_priv->ring[RCS].default_context = NULL;
- dev_priv->ring[RCS].last_context = NULL;
+ dev_priv->mm.aliasing_ppgtt = NULL;
+}
+
+int i915_gem_context_enable(struct drm_i915_private *dev_priv)
+{
+ struct intel_ring_buffer *ring;
+ int ret, i;
+
+ if (!HAS_HW_CONTEXTS(dev_priv->dev))
+ return 0;
+
+ /* This is the only place the aliasing PPGTT gets enabled, which means
+ * it has to happen before we bail on reset */
+ if (dev_priv->mm.aliasing_ppgtt) {
+ struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+ ppgtt->enable(ppgtt);
+ }
+
+ /* FIXME: We should make this work, even in reset */
+ if (i915_reset_in_progress(&dev_priv->gpu_error))
+ return 0;
+
+ BUG_ON(!dev_priv->ring[RCS].default_context);
+
+ for_each_ring(ring, dev_priv, i) {
+ ret = do_switch(ring, ring->default_context);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static int context_idr_cleanup(int id, void *p, void *data)
{
struct i915_hw_context *ctx = p;
- BUG_ON(id == DEFAULT_CONTEXT_ID);
+ /* Ignore the default context because close will handle it */
+ if (i915_gem_context_is_default(ctx))
+ return 0;
i915_gem_context_unreference(ctx);
return 0;
}
-struct i915_ctx_hang_stats *
-i915_gem_context_get_hang_stats(struct drm_device *dev,
- struct drm_file *file,
- u32 id)
+int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_i915_file_private *file_priv = file->driver_priv;
- struct i915_hw_context *ctx;
+ struct drm_i915_private *dev_priv = dev->dev_private;
- if (id == DEFAULT_CONTEXT_ID)
- return &file_priv->hang_stats;
+ if (!HAS_HW_CONTEXTS(dev)) {
+ /* Cheat for hang stats */
+ file_priv->private_default_ctx =
+ kzalloc(sizeof(struct i915_hw_context), GFP_KERNEL);
- if (!HAS_HW_CONTEXTS(dev))
- return ERR_PTR(-ENOENT);
+ if (file_priv->private_default_ctx == NULL)
+ return -ENOMEM;
- ctx = i915_gem_context_get(file->driver_priv, id);
- if (ctx == NULL)
- return ERR_PTR(-ENOENT);
+ file_priv->private_default_ctx->vm = &dev_priv->gtt.base;
+ return 0;
+ }
+
+ idr_init(&file_priv->context_idr);
- return &ctx->hang_stats;
+ mutex_lock(&dev->struct_mutex);
+ file_priv->private_default_ctx =
+ i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
+ mutex_unlock(&dev->struct_mutex);
+
+ if (IS_ERR(file_priv->private_default_ctx)) {
+ idr_destroy(&file_priv->context_idr);
+ return PTR_ERR(file_priv->private_default_ctx);
+ }
+
+ return 0;
}
void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
{
struct drm_i915_file_private *file_priv = file->driver_priv;
+ if (!HAS_HW_CONTEXTS(dev)) {
+ kfree(file_priv->private_default_ctx);
+ return;
+ }
+
idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);
+ i915_gem_context_unreference(file_priv->private_default_ctx);
idr_destroy(&file_priv->context_idr);
}
-static struct i915_hw_context *
+struct i915_hw_context *
i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
{
- return (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
+ struct i915_hw_context *ctx;
+
+ if (!HAS_HW_CONTEXTS(file_priv->dev_priv->dev))
+ return file_priv->private_default_ctx;
+
+ ctx = (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
+ if (!ctx)
+ return ERR_PTR(-ENOENT);
+
+ return ctx;
}
static inline int
@@ -390,7 +619,10 @@ mi_set_context(struct intel_ring_buffer *ring,
MI_SAVE_EXT_STATE_EN |
MI_RESTORE_EXT_STATE_EN |
hw_flags);
- /* w/a: MI_SET_CONTEXT must always be followed by MI_NOOP */
+ /*
+ * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP
+ * WaMiSetContext_Hang:snb,ivb,vlv
+ */
intel_ring_emit(ring, MI_NOOP);
if (IS_GEN7(ring->dev))
@@ -403,21 +635,30 @@ mi_set_context(struct intel_ring_buffer *ring,
return ret;
}
-static int do_switch(struct i915_hw_context *to)
+static int do_switch(struct intel_ring_buffer *ring,
+ struct i915_hw_context *to)
{
- struct intel_ring_buffer *ring = to->ring;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
struct i915_hw_context *from = ring->last_context;
+ struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to);
u32 hw_flags = 0;
int ret, i;
- BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0);
+ if (from != NULL && ring == &dev_priv->ring[RCS]) {
+ BUG_ON(from->obj == NULL);
+ BUG_ON(!i915_gem_obj_is_pinned(from->obj));
+ }
- if (from == to && !to->remap_slice)
+ if (from == to && from->last_ring == ring && !to->remap_slice)
return 0;
- ret = i915_gem_obj_ggtt_pin(to->obj, CONTEXT_ALIGN, false, false);
- if (ret)
- return ret;
+ /* Trying to pin first makes error handling easier. */
+ if (ring == &dev_priv->ring[RCS]) {
+ ret = i915_gem_obj_ggtt_pin(to->obj,
+ get_context_alignment(ring->dev), 0);
+ if (ret)
+ return ret;
+ }
/*
* Pin can switch back to the default context if we end up calling into
@@ -426,6 +667,18 @@ static int do_switch(struct i915_hw_context *to)
*/
from = ring->last_context;
+ if (USES_FULL_PPGTT(ring->dev)) {
+ ret = ppgtt->switch_mm(ppgtt, ring, false);
+ if (ret)
+ goto unpin_out;
+ }
+
+ if (ring != &dev_priv->ring[RCS]) {
+ if (from)
+ i915_gem_context_unreference(from);
+ goto done;
+ }
+
/*
* Clear this page out of any CPU caches for coherent swap-in/out. Note
* that thanks to write = false in this call and us not setting any gpu
@@ -435,22 +688,21 @@ static int do_switch(struct i915_hw_context *to)
* XXX: We need a real interface to do this instead of trickery.
*/
ret = i915_gem_object_set_to_gtt_domain(to->obj, false);
- if (ret) {
- i915_gem_object_unpin(to->obj);
- return ret;
- }
+ if (ret)
+ goto unpin_out;
- if (!to->obj->has_global_gtt_mapping)
- i915_gem_gtt_bind_object(to->obj, to->obj->cache_level);
+ if (!to->obj->has_global_gtt_mapping) {
+ struct i915_vma *vma = i915_gem_obj_to_vma(to->obj,
+ &dev_priv->gtt.base);
+ vma->bind_vma(vma, to->obj->cache_level, GLOBAL_BIND);
+ }
- if (!to->is_initialized || is_default_context(to))
+ if (!to->is_initialized || i915_gem_context_is_default(to))
hw_flags |= MI_RESTORE_INHIBIT;
ret = mi_set_context(ring, to, hw_flags);
- if (ret) {
- i915_gem_object_unpin(to->obj);
- return ret;
- }
+ if (ret)
+ goto unpin_out;
for (i = 0; i < MAX_L3_SLICES; i++) {
if (!(to->remap_slice & (1<<i)))
@@ -484,22 +736,30 @@ static int do_switch(struct i915_hw_context *to)
BUG_ON(from->obj->ring != ring);
/* obj is kept alive until the next request by its active ref */
- i915_gem_object_unpin(from->obj);
+ i915_gem_object_ggtt_unpin(from->obj);
i915_gem_context_unreference(from);
}
+ to->is_initialized = true;
+
+done:
i915_gem_context_reference(to);
ring->last_context = to;
- to->is_initialized = true;
+ to->last_ring = ring;
return 0;
+
+unpin_out:
+ if (ring->id == RCS)
+ i915_gem_object_ggtt_unpin(to->obj);
+ return ret;
}
/**
* i915_switch_context() - perform a GPU context switch.
* @ring: ring for which we'll execute the context switch
* @file_priv: file_priv associated with the context, may be NULL
- * @id: context id number
+ * @to: the context to switch to
*
* The context life cycle is simple. The context refcount is incremented and
* decremented by 1 and create and destroy. If the context is in use by the GPU,
@@ -508,31 +768,21 @@ static int do_switch(struct i915_hw_context *to)
*/
int i915_switch_context(struct intel_ring_buffer *ring,
struct drm_file *file,
- int to_id)
+ struct i915_hw_context *to)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
- struct i915_hw_context *to;
-
- if (!HAS_HW_CONTEXTS(ring->dev))
- return 0;
WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
- if (ring != &dev_priv->ring[RCS])
- return 0;
-
- if (to_id == DEFAULT_CONTEXT_ID) {
- to = ring->default_context;
- } else {
- if (file == NULL)
- return -EINVAL;
+ BUG_ON(file && to == NULL);
- to = i915_gem_context_get(file->driver_priv, to_id);
- if (to == NULL)
- return -ENOENT;
+ /* We have the fake context */
+ if (!HAS_HW_CONTEXTS(ring->dev)) {
+ ring->last_context = to;
+ return 0;
}
- return do_switch(to);
+ return do_switch(ring, to);
}
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
@@ -543,9 +793,6 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
struct i915_hw_context *ctx;
int ret;
- if (!(dev->driver->driver_features & DRIVER_GEM))
- return -ENODEV;
-
if (!HAS_HW_CONTEXTS(dev))
return -ENODEV;
@@ -553,7 +800,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
if (ret)
return ret;
- ctx = create_hw_context(dev, file_priv);
+ ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
mutex_unlock(&dev->struct_mutex);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
@@ -572,17 +819,17 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
struct i915_hw_context *ctx;
int ret;
- if (!(dev->driver->driver_features & DRIVER_GEM))
- return -ENODEV;
+ if (args->ctx_id == DEFAULT_CONTEXT_ID)
+ return -ENOENT;
ret = i915_mutex_lock_interruptible(dev);
if (ret)
return ret;
ctx = i915_gem_context_get(file_priv, args->ctx_id);
- if (!ctx) {
+ if (IS_ERR(ctx)) {
mutex_unlock(&dev->struct_mutex);
- return -ENOENT;
+ return PTR_ERR(ctx);
}
idr_remove(&ctx->file_priv->context_idr, ctx->id);
diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c
index 775d506b3208..f462d1b51d97 100644
--- a/drivers/gpu/drm/i915/i915_gem_debug.c
+++ b/drivers/gpu/drm/i915/i915_gem_debug.c
@@ -34,7 +34,7 @@ int
i915_verify_lists(struct drm_device *dev)
{
static int warned;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
int err = 0;
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 2ca280f9ee53..75fca63dc8c1 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -36,7 +36,7 @@
static bool
mark_free(struct i915_vma *vma, struct list_head *unwind)
{
- if (vma->obj->pin_count)
+ if (vma->pin_count)
return false;
if (WARN_ON(!list_empty(&vma->exec_list)))
@@ -46,18 +46,37 @@ mark_free(struct i915_vma *vma, struct list_head *unwind)
return drm_mm_scan_add_block(&vma->node);
}
+/**
+ * i915_gem_evict_something - Evict vmas to make room for binding a new one
+ * @dev: drm_device
+ * @vm: address space to evict from
+ * @size: size of the desired free space
+ * @alignment: alignment constraint of the desired free space
+ * @cache_level: cache_level for the desired space
+ * @mappable: whether the free space must be mappable
+ * @nonblocking: whether evicting active objects is allowed or not
+ *
+ * This function will try to evict vmas until a free space satisfying the
+ * requirements is found. Callers must check first whether any such hole exists
+ * already before calling this function.
+ *
+ * This function is used by the object/vma binding code.
+ *
+ * To clarify: This is for freeing up virtual address space, not for freeing
+ * memory in e.g. the shrinker.
+ */
int
i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm,
int min_size, unsigned alignment, unsigned cache_level,
- bool mappable, bool nonblocking)
+ unsigned flags)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct list_head eviction_list, unwind_list;
struct i915_vma *vma;
int ret = 0;
int pass = 0;
- trace_i915_gem_evict(dev, min_size, alignment, mappable);
+ trace_i915_gem_evict(dev, min_size, alignment, flags);
/*
* The goal is to evict objects and amalgamate space in LRU order.
@@ -83,7 +102,7 @@ i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm,
*/
INIT_LIST_HEAD(&unwind_list);
- if (mappable) {
+ if (flags & PIN_MAPPABLE) {
BUG_ON(!i915_is_ggtt(vm));
drm_mm_init_scan_with_range(&vm->mm, min_size,
alignment, cache_level, 0,
@@ -98,7 +117,7 @@ search_again:
goto found;
}
- if (nonblocking)
+ if (flags & PIN_NONBLOCK)
goto none;
/* Now merge in the soon-to-be-expired objects... */
@@ -122,7 +141,7 @@ none:
/* Can we unpin some objects such as idle hw contents,
* or pending flips?
*/
- if (nonblocking)
+ if (flags & PIN_NONBLOCK)
return -ENOSPC;
/* Only idle the GPU and repeat the search once */
@@ -177,19 +196,19 @@ found:
}
/**
- * i915_gem_evict_vm - Try to free up VM space
+ * i915_gem_evict_vm - Evict all idle vmas from a vm
*
- * @vm: Address space to evict from
+ * @vm: Address space to cleanse
* @do_idle: Boolean directing whether to idle first.
*
- * VM eviction is about freeing up virtual address space. If one wants fine
- * grained eviction, they should see evict something for more details. In terms
- * of freeing up actual system memory, this function may not accomplish the
- * desired result. An object may be shared in multiple address space, and this
- * function will not assert those objects be freed.
+ * This function evicts all idles vmas from a vm. If all unpinned vmas should be
+ * evicted the @do_idle needs to be set to true.
*
- * Using do_idle will result in a more complete eviction because it retires, and
- * inactivates current BOs.
+ * This is used by the execbuf code as a last-ditch effort to defragment the
+ * address space.
+ *
+ * To clarify: This is for freeing up virtual address space, not for freeing
+ * memory in e.g. the shrinker.
*/
int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle)
{
@@ -207,16 +226,24 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle)
}
list_for_each_entry_safe(vma, next, &vm->inactive_list, mm_list)
- if (vma->obj->pin_count == 0)
+ if (vma->pin_count == 0)
WARN_ON(i915_vma_unbind(vma));
return 0;
}
+/**
+ * i915_gem_evict_everything - Try to evict all objects
+ * @dev: Device to evict objects for
+ *
+ * This functions tries to evict all gem objects from all address spaces. Used
+ * by the shrinker as a last-ditch effort and for suspend, before releasing the
+ * backing storage of all unbound objects.
+ */
int
i915_gem_evict_everything(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_address_space *vm;
bool lists_empty = true;
int ret;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index d269ecf46e26..7447160155a3 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -91,6 +91,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
struct i915_address_space *vm,
struct drm_file *file)
{
+ struct drm_i915_private *dev_priv = vm->dev->dev_private;
struct drm_i915_gem_object *obj;
struct list_head objects;
int i, ret;
@@ -125,6 +126,20 @@ eb_lookup_vmas(struct eb_vmas *eb,
i = 0;
while (!list_empty(&objects)) {
struct i915_vma *vma;
+ struct i915_address_space *bind_vm = vm;
+
+ if (exec[i].flags & EXEC_OBJECT_NEEDS_GTT &&
+ USES_FULL_PPGTT(vm->dev)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* If we have secure dispatch, or the userspace assures us that
+ * they know what they're doing, use the GGTT VM.
+ */
+ if (((args->flags & I915_EXEC_SECURE) &&
+ (i == (args->buffer_count - 1))))
+ bind_vm = &dev_priv->gtt.base;
obj = list_first_entry(&objects,
struct drm_i915_gem_object,
@@ -138,7 +153,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
* from the (obj, vm) we don't run the risk of creating
* duplicated vmas for the same vm.
*/
- vma = i915_gem_obj_lookup_or_create_vma(obj, vm);
+ vma = i915_gem_obj_lookup_or_create_vma(obj, bind_vm);
if (IS_ERR(vma)) {
DRM_DEBUG("Failed to lookup VMA\n");
ret = PTR_ERR(vma);
@@ -217,7 +232,7 @@ i915_gem_execbuffer_unreserve_vma(struct i915_vma *vma)
i915_gem_object_unpin_fence(obj);
if (entry->flags & __EXEC_OBJECT_HAS_PIN)
- i915_gem_object_unpin(obj);
+ vma->pin_count--;
entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN);
}
@@ -327,8 +342,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
static int
i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
struct eb_vmas *eb,
- struct drm_i915_gem_relocation_entry *reloc,
- struct i915_address_space *vm)
+ struct drm_i915_gem_relocation_entry *reloc)
{
struct drm_device *dev = obj->base.dev;
struct drm_gem_object *target_obj;
@@ -352,8 +366,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
if (unlikely(IS_GEN6(dev) &&
reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
!target_i915_obj->has_global_gtt_mapping)) {
- i915_gem_gtt_bind_object(target_i915_obj,
- target_i915_obj->cache_level);
+ struct i915_vma *vma =
+ list_first_entry(&target_i915_obj->vma_list,
+ typeof(*vma), vma_link);
+ vma->bind_vma(vma, target_i915_obj->cache_level, GLOBAL_BIND);
}
/* Validate that the target is in a valid r/w GPU domain */
@@ -451,8 +467,7 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma,
do {
u64 offset = r->presumed_offset;
- ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r,
- vma->vm);
+ ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r);
if (ret)
return ret;
@@ -481,8 +496,7 @@ i915_gem_execbuffer_relocate_vma_slow(struct i915_vma *vma,
int i, ret;
for (i = 0; i < entry->relocation_count; i++) {
- ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i],
- vma->vm);
+ ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i]);
if (ret)
return ret;
}
@@ -527,21 +541,26 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
struct intel_ring_buffer *ring,
bool *need_reloc)
{
- struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ struct drm_i915_gem_object *obj = vma->obj;
struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
- bool need_fence, need_mappable;
- struct drm_i915_gem_object *obj = vma->obj;
+ bool need_fence;
+ unsigned flags;
int ret;
+ flags = 0;
+
need_fence =
has_fenced_gpu_access &&
entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
obj->tiling_mode != I915_TILING_NONE;
- need_mappable = need_fence || need_reloc_mappable(vma);
+ if (need_fence || need_reloc_mappable(vma))
+ flags |= PIN_MAPPABLE;
- ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, need_mappable,
- false);
+ if (entry->flags & EXEC_OBJECT_NEEDS_GTT)
+ flags |= PIN_GLOBAL;
+
+ ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags);
if (ret)
return ret;
@@ -560,14 +579,6 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
}
}
- /* Ensure ppgtt mapping exists if needed */
- if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) {
- i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
- obj, obj->cache_level);
-
- obj->has_aliasing_ppgtt_mapping = 1;
- }
-
if (entry->offset != vma->node.start) {
entry->offset = vma->node.start;
*need_reloc = true;
@@ -578,10 +589,6 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
obj->base.pending_write_domain = I915_GEM_DOMAIN_RENDER;
}
- if (entry->flags & EXEC_OBJECT_NEEDS_GTT &&
- !obj->has_global_gtt_mapping)
- i915_gem_gtt_bind_object(obj, obj->cache_level);
-
return 0;
}
@@ -891,7 +898,7 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
if (!access_ok(VERIFY_WRITE, ptr, length))
return -EFAULT;
- if (likely(!i915_prefault_disable)) {
+ if (likely(!i915.prefault_disable)) {
if (fault_in_multipages_readable(ptr, length))
return -EFAULT;
}
@@ -900,22 +907,27 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
return 0;
}
-static int
+static struct i915_hw_context *
i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
- const u32 ctx_id)
+ struct intel_ring_buffer *ring, const u32 ctx_id)
{
+ struct i915_hw_context *ctx = NULL;
struct i915_ctx_hang_stats *hs;
- hs = i915_gem_context_get_hang_stats(dev, file, ctx_id);
- if (IS_ERR(hs))
- return PTR_ERR(hs);
+ if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_ID)
+ return ERR_PTR(-EINVAL);
+
+ ctx = i915_gem_context_get(file->driver_priv, ctx_id);
+ if (IS_ERR(ctx))
+ return ctx;
+ hs = &ctx->hang_stats;
if (hs->banned) {
DRM_DEBUG("Context %u tried to submit while banned\n", ctx_id);
- return -EIO;
+ return ERR_PTR(-EIO);
}
- return 0;
+ return ctx;
}
static void
@@ -939,7 +951,9 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
if (obj->base.write_domain) {
obj->dirty = 1;
obj->last_write_seqno = intel_ring_get_seqno(ring);
- if (obj->pin_count) /* check for potential scanout */
+ /* check for potential scanout */
+ if (i915_gem_obj_ggtt_bound(obj) &&
+ i915_gem_obj_to_ggtt(obj)->pin_count)
intel_mark_fb_busy(obj, ring);
}
@@ -964,7 +978,7 @@ static int
i915_reset_gen7_sol_offsets(struct drm_device *dev,
struct intel_ring_buffer *ring)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int ret, i;
if (!IS_GEN7(dev) || ring != &dev_priv->ring[RCS])
@@ -989,16 +1003,17 @@ static int
i915_gem_do_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file,
struct drm_i915_gem_execbuffer2 *args,
- struct drm_i915_gem_exec_object2 *exec,
- struct i915_address_space *vm)
+ struct drm_i915_gem_exec_object2 *exec)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct eb_vmas *eb;
struct drm_i915_gem_object *batch_obj;
struct drm_clip_rect *cliprects = NULL;
struct intel_ring_buffer *ring;
+ struct i915_hw_context *ctx;
+ struct i915_address_space *vm;
const u32 ctx_id = i915_execbuffer2_get_context_id(*args);
- u32 exec_start, exec_len;
+ u32 exec_start = args->batch_start_offset, exec_len;
u32 mask, flags;
int ret, mode, i;
bool need_relocs;
@@ -1020,41 +1035,17 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
if (args->flags & I915_EXEC_IS_PINNED)
flags |= I915_DISPATCH_PINNED;
- switch (args->flags & I915_EXEC_RING_MASK) {
- case I915_EXEC_DEFAULT:
- case I915_EXEC_RENDER:
- ring = &dev_priv->ring[RCS];
- break;
- case I915_EXEC_BSD:
- ring = &dev_priv->ring[VCS];
- if (ctx_id != DEFAULT_CONTEXT_ID) {
- DRM_DEBUG("Ring %s doesn't support contexts\n",
- ring->name);
- return -EPERM;
- }
- break;
- case I915_EXEC_BLT:
- ring = &dev_priv->ring[BCS];
- if (ctx_id != DEFAULT_CONTEXT_ID) {
- DRM_DEBUG("Ring %s doesn't support contexts\n",
- ring->name);
- return -EPERM;
- }
- break;
- case I915_EXEC_VEBOX:
- ring = &dev_priv->ring[VECS];
- if (ctx_id != DEFAULT_CONTEXT_ID) {
- DRM_DEBUG("Ring %s doesn't support contexts\n",
- ring->name);
- return -EPERM;
- }
- break;
-
- default:
+ if ((args->flags & I915_EXEC_RING_MASK) > I915_NUM_RINGS) {
DRM_DEBUG("execbuf with unknown ring: %d\n",
(int)(args->flags & I915_EXEC_RING_MASK));
return -EINVAL;
}
+
+ if ((args->flags & I915_EXEC_RING_MASK) == I915_EXEC_DEFAULT)
+ ring = &dev_priv->ring[RCS];
+ else
+ ring = &dev_priv->ring[(args->flags & I915_EXEC_RING_MASK) - 1];
+
if (!intel_ring_initialized(ring)) {
DRM_DEBUG("execbuf with invalid ring: %d\n",
(int)(args->flags & I915_EXEC_RING_MASK));
@@ -1136,11 +1127,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
goto pre_mutex_err;
}
- ret = i915_gem_validate_context(dev, file, ctx_id);
- if (ret) {
+ ctx = i915_gem_validate_context(dev, file, ring, ctx_id);
+ if (IS_ERR(ctx)) {
mutex_unlock(&dev->struct_mutex);
+ ret = PTR_ERR(ctx);
goto pre_mutex_err;
- }
+ }
+
+ i915_gem_context_reference(ctx);
+
+ vm = ctx->vm;
+ if (!USES_FULL_PPGTT(dev))
+ vm = &dev_priv->gtt.base;
eb = eb_create(args);
if (eb == NULL) {
@@ -1184,17 +1182,46 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
}
batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
+ if (i915_needs_cmd_parser(ring)) {
+ ret = i915_parse_cmds(ring,
+ batch_obj,
+ args->batch_start_offset,
+ file->is_master);
+ if (ret)
+ goto err;
+
+ /*
+ * XXX: Actually do this when enabling batch copy...
+ *
+ * Set the DISPATCH_SECURE bit to remove the NON_SECURE bit
+ * from MI_BATCH_BUFFER_START commands issued in the
+ * dispatch_execbuffer implementations. We specifically don't
+ * want that set when the command parser is enabled.
+ */
+ }
+
/* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
* batch" bit. Hence we need to pin secure batches into the global gtt.
* hsw should have this fixed, but bdw mucks it up again. */
- if (flags & I915_DISPATCH_SECURE && !batch_obj->has_global_gtt_mapping)
- i915_gem_gtt_bind_object(batch_obj, batch_obj->cache_level);
+ if (flags & I915_DISPATCH_SECURE &&
+ !batch_obj->has_global_gtt_mapping) {
+ /* When we have multiple VMs, we'll need to make sure that we
+ * allocate space first */
+ struct i915_vma *vma = i915_gem_obj_to_ggtt(batch_obj);
+ BUG_ON(!vma);
+ vma->bind_vma(vma, batch_obj->cache_level, GLOBAL_BIND);
+ }
+
+ if (flags & I915_DISPATCH_SECURE)
+ exec_start += i915_gem_obj_ggtt_offset(batch_obj);
+ else
+ exec_start += i915_gem_obj_offset(batch_obj, vm);
ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->vmas);
if (ret)
goto err;
- ret = i915_switch_context(ring, file, ctx_id);
+ ret = i915_switch_context(ring, file, ctx);
if (ret)
goto err;
@@ -1219,8 +1246,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
goto err;
}
- exec_start = i915_gem_obj_offset(batch_obj, vm) +
- args->batch_start_offset;
+
exec_len = args->batch_len;
if (cliprects) {
for (i = 0; i < args->num_cliprects; i++) {
@@ -1249,6 +1275,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
err:
+ /* the request owns the ref now */
+ i915_gem_context_unreference(ctx);
eb_destroy(eb);
mutex_unlock(&dev->struct_mutex);
@@ -1270,7 +1298,6 @@ int
i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_execbuffer *args = data;
struct drm_i915_gem_execbuffer2 exec2;
struct drm_i915_gem_exec_object *exec_list = NULL;
@@ -1326,8 +1353,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
exec2.flags = I915_EXEC_RENDER;
i915_execbuffer2_set_context_id(exec2, 0);
- ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list,
- &dev_priv->gtt.base);
+ ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list);
if (!ret) {
/* Copy the new buffer offsets back to the user's exec list. */
for (i = 0; i < args->buffer_count; i++)
@@ -1353,7 +1379,6 @@ int
i915_gem_execbuffer2(struct drm_device *dev, void *data,
struct drm_file *file)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_execbuffer2 *args = data;
struct drm_i915_gem_exec_object2 *exec2_list = NULL;
int ret;
@@ -1384,8 +1409,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
return -EFAULT;
}
- ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list,
- &dev_priv->gtt.base);
+ ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list);
if (!ret) {
/* Copy the new buffer offsets back to the user's exec list. */
ret = copy_to_user(to_user_ptr(args->buffers_ptr),
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index d278be110805..ab5e93c30aa2 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -1,5 +1,6 @@
/*
* Copyright © 2010 Daniel Vetter
+ * Copyright © 2011-2014 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -22,12 +23,38 @@
*
*/
+#include <linux/seq_file.h>
#include <drm/drmP.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include "i915_trace.h"
#include "intel_drv.h"
+static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv);
+
+bool intel_enable_ppgtt(struct drm_device *dev, bool full)
+{
+ if (i915.enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev))
+ return false;
+
+ if (i915.enable_ppgtt == 1 && full)
+ return false;
+
+#ifdef CONFIG_INTEL_IOMMU
+ /* Disable ppgtt on SNB if VT-d is on. */
+ if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) {
+ DRM_INFO("Disabling PPGTT because VT-d is on\n");
+ return false;
+ }
+#endif
+
+ /* Full ppgtt disabled by default for now due to issues. */
+ if (full)
+ return false; /* HAS_PPGTT(dev) */
+ else
+ return HAS_ALIASING_PPGTT(dev);
+}
+
#define GEN6_PPGTT_PD_ENTRIES 512
#define I915_PPGTT_PT_ENTRIES (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
typedef uint64_t gen8_gtt_pte_t;
@@ -63,13 +90,31 @@ typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
#define GEN8_PTES_PER_PAGE (PAGE_SIZE / sizeof(gen8_gtt_pte_t))
#define GEN8_PDES_PER_PAGE (PAGE_SIZE / sizeof(gen8_ppgtt_pde_t))
-#define GEN8_LEGACY_PDPS 4
+
+/* GEN8 legacy style addressis defined as a 3 level page table:
+ * 31:30 | 29:21 | 20:12 | 11:0
+ * PDPE | PDE | PTE | offset
+ * The difference as compared to normal x86 3 level page table is the PDPEs are
+ * programmed via register.
+ */
+#define GEN8_PDPE_SHIFT 30
+#define GEN8_PDPE_MASK 0x3
+#define GEN8_PDE_SHIFT 21
+#define GEN8_PDE_MASK 0x1ff
+#define GEN8_PTE_SHIFT 12
+#define GEN8_PTE_MASK 0x1ff
#define PPAT_UNCACHED_INDEX (_PAGE_PWT | _PAGE_PCD)
#define PPAT_CACHED_PDE_INDEX 0 /* WB LLC */
#define PPAT_CACHED_INDEX _PAGE_PAT /* WB LLCeLLC */
#define PPAT_DISPLAY_ELLC_INDEX _PAGE_PCD /* WT eLLC */
+static void ppgtt_bind_vma(struct i915_vma *vma,
+ enum i915_cache_level cache_level,
+ u32 flags);
+static void ppgtt_unbind_vma(struct i915_vma *vma);
+static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt);
+
static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
bool valid)
@@ -199,12 +244,19 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
/* Broadwell Page Directory Pointer Descriptors */
static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
- uint64_t val)
+ uint64_t val, bool synchronous)
{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
int ret;
BUG_ON(entry >= 4);
+ if (synchronous) {
+ I915_WRITE(GEN8_RING_PDP_UDW(ring, entry), val >> 32);
+ I915_WRITE(GEN8_RING_PDP_LDW(ring, entry), (u32)val);
+ return 0;
+ }
+
ret = intel_ring_begin(ring, 6);
if (ret)
return ret;
@@ -220,216 +272,357 @@ static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
return 0;
}
-static int gen8_ppgtt_enable(struct drm_device *dev)
+static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
+ struct intel_ring_buffer *ring,
+ bool synchronous)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
- struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
- int i, j, ret;
+ int i, ret;
/* bit of a hack to find the actual last used pd */
int used_pd = ppgtt->num_pd_entries / GEN8_PDES_PER_PAGE;
- for_each_ring(ring, dev_priv, j) {
- I915_WRITE(RING_MODE_GEN7(ring),
- _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
- }
-
for (i = used_pd - 1; i >= 0; i--) {
dma_addr_t addr = ppgtt->pd_dma_addr[i];
- for_each_ring(ring, dev_priv, j) {
- ret = gen8_write_pdp(ring, i, addr);
- if (ret)
- goto err_out;
- }
+ ret = gen8_write_pdp(ring, i, addr, synchronous);
+ if (ret)
+ return ret;
}
- return 0;
-err_out:
- for_each_ring(ring, dev_priv, j)
- I915_WRITE(RING_MODE_GEN7(ring),
- _MASKED_BIT_DISABLE(GFX_PPGTT_ENABLE));
- return ret;
+ return 0;
}
static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
- unsigned first_entry,
- unsigned num_entries,
+ uint64_t start,
+ uint64_t length,
bool use_scratch)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
gen8_gtt_pte_t *pt_vaddr, scratch_pte;
- unsigned act_pt = first_entry / GEN8_PTES_PER_PAGE;
- unsigned first_pte = first_entry % GEN8_PTES_PER_PAGE;
+ unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK;
+ unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK;
+ unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK;
+ unsigned num_entries = length >> PAGE_SHIFT;
unsigned last_pte, i;
scratch_pte = gen8_pte_encode(ppgtt->base.scratch.addr,
I915_CACHE_LLC, use_scratch);
while (num_entries) {
- struct page *page_table = &ppgtt->gen8_pt_pages[act_pt];
+ struct page *page_table = ppgtt->gen8_pt_pages[pdpe][pde];
- last_pte = first_pte + num_entries;
+ last_pte = pte + num_entries;
if (last_pte > GEN8_PTES_PER_PAGE)
last_pte = GEN8_PTES_PER_PAGE;
pt_vaddr = kmap_atomic(page_table);
- for (i = first_pte; i < last_pte; i++)
+ for (i = pte; i < last_pte; i++) {
pt_vaddr[i] = scratch_pte;
+ num_entries--;
+ }
kunmap_atomic(pt_vaddr);
- num_entries -= last_pte - first_pte;
- first_pte = 0;
- act_pt++;
+ pte = 0;
+ if (++pde == GEN8_PDES_PER_PAGE) {
+ pdpe++;
+ pde = 0;
+ }
}
}
static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
struct sg_table *pages,
- unsigned first_entry,
+ uint64_t start,
enum i915_cache_level cache_level)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
gen8_gtt_pte_t *pt_vaddr;
- unsigned act_pt = first_entry / GEN8_PTES_PER_PAGE;
- unsigned act_pte = first_entry % GEN8_PTES_PER_PAGE;
+ unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK;
+ unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK;
+ unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK;
struct sg_page_iter sg_iter;
pt_vaddr = NULL;
+
for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) {
+ if (WARN_ON(pdpe >= GEN8_LEGACY_PDPS))
+ break;
+
if (pt_vaddr == NULL)
- pt_vaddr = kmap_atomic(&ppgtt->gen8_pt_pages[act_pt]);
+ pt_vaddr = kmap_atomic(ppgtt->gen8_pt_pages[pdpe][pde]);
- pt_vaddr[act_pte] =
+ pt_vaddr[pte] =
gen8_pte_encode(sg_page_iter_dma_address(&sg_iter),
cache_level, true);
- if (++act_pte == GEN8_PTES_PER_PAGE) {
+ if (++pte == GEN8_PTES_PER_PAGE) {
kunmap_atomic(pt_vaddr);
pt_vaddr = NULL;
- act_pt++;
- act_pte = 0;
+ if (++pde == GEN8_PDES_PER_PAGE) {
+ pdpe++;
+ pde = 0;
+ }
+ pte = 0;
}
}
if (pt_vaddr)
kunmap_atomic(pt_vaddr);
}
+static void gen8_free_page_tables(struct page **pt_pages)
+{
+ int i;
+
+ if (pt_pages == NULL)
+ return;
+
+ for (i = 0; i < GEN8_PDES_PER_PAGE; i++)
+ if (pt_pages[i])
+ __free_pages(pt_pages[i], 0);
+}
+
+static void gen8_ppgtt_free(const struct i915_hw_ppgtt *ppgtt)
+{
+ int i;
+
+ for (i = 0; i < ppgtt->num_pd_pages; i++) {
+ gen8_free_page_tables(ppgtt->gen8_pt_pages[i]);
+ kfree(ppgtt->gen8_pt_pages[i]);
+ kfree(ppgtt->gen8_pt_dma_addr[i]);
+ }
+
+ __free_pages(ppgtt->pd_pages, get_order(ppgtt->num_pd_pages << PAGE_SHIFT));
+}
+
+static void gen8_ppgtt_unmap_pages(struct i915_hw_ppgtt *ppgtt)
+{
+ struct pci_dev *hwdev = ppgtt->base.dev->pdev;
+ int i, j;
+
+ for (i = 0; i < ppgtt->num_pd_pages; i++) {
+ /* TODO: In the future we'll support sparse mappings, so this
+ * will have to change. */
+ if (!ppgtt->pd_dma_addr[i])
+ continue;
+
+ pci_unmap_page(hwdev, ppgtt->pd_dma_addr[i], PAGE_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+
+ for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
+ dma_addr_t addr = ppgtt->gen8_pt_dma_addr[i][j];
+ if (addr)
+ pci_unmap_page(hwdev, addr, PAGE_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+ }
+ }
+}
+
static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
- int i, j;
+ list_del(&vm->global_link);
drm_mm_takedown(&vm->mm);
- for (i = 0; i < ppgtt->num_pd_pages ; i++) {
- if (ppgtt->pd_dma_addr[i]) {
- pci_unmap_page(ppgtt->base.dev->pdev,
- ppgtt->pd_dma_addr[i],
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ gen8_ppgtt_unmap_pages(ppgtt);
+ gen8_ppgtt_free(ppgtt);
+}
- for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
- dma_addr_t addr = ppgtt->gen8_pt_dma_addr[i][j];
- if (addr)
- pci_unmap_page(ppgtt->base.dev->pdev,
- addr,
- PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
+static struct page **__gen8_alloc_page_tables(void)
+{
+ struct page **pt_pages;
+ int i;
- }
- }
- kfree(ppgtt->gen8_pt_dma_addr[i]);
+ pt_pages = kcalloc(GEN8_PDES_PER_PAGE, sizeof(struct page *), GFP_KERNEL);
+ if (!pt_pages)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < GEN8_PDES_PER_PAGE; i++) {
+ pt_pages[i] = alloc_page(GFP_KERNEL);
+ if (!pt_pages[i])
+ goto bail;
}
- __free_pages(ppgtt->gen8_pt_pages, get_order(ppgtt->num_pt_pages << PAGE_SHIFT));
- __free_pages(ppgtt->pd_pages, get_order(ppgtt->num_pd_pages << PAGE_SHIFT));
+ return pt_pages;
+
+bail:
+ gen8_free_page_tables(pt_pages);
+ kfree(pt_pages);
+ return ERR_PTR(-ENOMEM);
}
-/**
- * GEN8 legacy ppgtt programming is accomplished through 4 PDP registers with a
- * net effect resembling a 2-level page table in normal x86 terms. Each PDP
- * represents 1GB of memory
- * 4 * 512 * 512 * 4096 = 4GB legacy 32b address space.
- *
- * TODO: Do something with the size parameter
- **/
-static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
+static int gen8_ppgtt_allocate_page_tables(struct i915_hw_ppgtt *ppgtt,
+ const int max_pdp)
{
- struct page *pt_pages;
- int i, j, ret = -ENOMEM;
- const int max_pdp = DIV_ROUND_UP(size, 1 << 30);
- const int num_pt_pages = GEN8_PDES_PER_PAGE * max_pdp;
+ struct page **pt_pages[GEN8_LEGACY_PDPS];
+ int i, ret;
- if (size % (1<<30))
- DRM_INFO("Pages will be wasted unless GTT size (%llu) is divisible by 1GB\n", size);
+ for (i = 0; i < max_pdp; i++) {
+ pt_pages[i] = __gen8_alloc_page_tables();
+ if (IS_ERR(pt_pages[i])) {
+ ret = PTR_ERR(pt_pages[i]);
+ goto unwind_out;
+ }
+ }
- /* FIXME: split allocation into smaller pieces. For now we only ever do
- * this once, but with full PPGTT, the multiple contiguous allocations
- * will be bad.
+ /* NB: Avoid touching gen8_pt_pages until last to keep the allocation,
+ * "atomic" - for cleanup purposes.
*/
+ for (i = 0; i < max_pdp; i++)
+ ppgtt->gen8_pt_pages[i] = pt_pages[i];
+
+ return 0;
+
+unwind_out:
+ while (i--) {
+ gen8_free_page_tables(pt_pages[i]);
+ kfree(pt_pages[i]);
+ }
+
+ return ret;
+}
+
+static int gen8_ppgtt_allocate_dma(struct i915_hw_ppgtt *ppgtt)
+{
+ int i;
+
+ for (i = 0; i < ppgtt->num_pd_pages; i++) {
+ ppgtt->gen8_pt_dma_addr[i] = kcalloc(GEN8_PDES_PER_PAGE,
+ sizeof(dma_addr_t),
+ GFP_KERNEL);
+ if (!ppgtt->gen8_pt_dma_addr[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int gen8_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt,
+ const int max_pdp)
+{
ppgtt->pd_pages = alloc_pages(GFP_KERNEL, get_order(max_pdp << PAGE_SHIFT));
if (!ppgtt->pd_pages)
return -ENOMEM;
- pt_pages = alloc_pages(GFP_KERNEL, get_order(num_pt_pages << PAGE_SHIFT));
- if (!pt_pages) {
+ ppgtt->num_pd_pages = 1 << get_order(max_pdp << PAGE_SHIFT);
+ BUG_ON(ppgtt->num_pd_pages > GEN8_LEGACY_PDPS);
+
+ return 0;
+}
+
+static int gen8_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt,
+ const int max_pdp)
+{
+ int ret;
+
+ ret = gen8_ppgtt_allocate_page_directories(ppgtt, max_pdp);
+ if (ret)
+ return ret;
+
+ ret = gen8_ppgtt_allocate_page_tables(ppgtt, max_pdp);
+ if (ret) {
__free_pages(ppgtt->pd_pages, get_order(max_pdp << PAGE_SHIFT));
- return -ENOMEM;
+ return ret;
}
- ppgtt->gen8_pt_pages = pt_pages;
- ppgtt->num_pd_pages = 1 << get_order(max_pdp << PAGE_SHIFT);
- ppgtt->num_pt_pages = 1 << get_order(num_pt_pages << PAGE_SHIFT);
ppgtt->num_pd_entries = max_pdp * GEN8_PDES_PER_PAGE;
- ppgtt->enable = gen8_ppgtt_enable;
- ppgtt->base.clear_range = gen8_ppgtt_clear_range;
- ppgtt->base.insert_entries = gen8_ppgtt_insert_entries;
- ppgtt->base.cleanup = gen8_ppgtt_cleanup;
- ppgtt->base.start = 0;
- ppgtt->base.total = ppgtt->num_pt_pages * GEN8_PTES_PER_PAGE * PAGE_SIZE;
- BUG_ON(ppgtt->num_pd_pages > GEN8_LEGACY_PDPS);
+ ret = gen8_ppgtt_allocate_dma(ppgtt);
+ if (ret)
+ gen8_ppgtt_free(ppgtt);
- /*
- * - Create a mapping for the page directories.
- * - For each page directory:
- * allocate space for page table mappings.
- * map each page table
- */
- for (i = 0; i < max_pdp; i++) {
- dma_addr_t temp;
- temp = pci_map_page(ppgtt->base.dev->pdev,
- &ppgtt->pd_pages[i], 0,
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(ppgtt->base.dev->pdev, temp))
- goto err_out;
+ return ret;
+}
- ppgtt->pd_dma_addr[i] = temp;
+static int gen8_ppgtt_setup_page_directories(struct i915_hw_ppgtt *ppgtt,
+ const int pd)
+{
+ dma_addr_t pd_addr;
+ int ret;
- ppgtt->gen8_pt_dma_addr[i] = kmalloc(sizeof(dma_addr_t) * GEN8_PDES_PER_PAGE, GFP_KERNEL);
- if (!ppgtt->gen8_pt_dma_addr[i])
- goto err_out;
+ pd_addr = pci_map_page(ppgtt->base.dev->pdev,
+ &ppgtt->pd_pages[pd], 0,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
- struct page *p = &pt_pages[i * GEN8_PDES_PER_PAGE + j];
- temp = pci_map_page(ppgtt->base.dev->pdev,
- p, 0, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
+ ret = pci_dma_mapping_error(ppgtt->base.dev->pdev, pd_addr);
+ if (ret)
+ return ret;
- if (pci_dma_mapping_error(ppgtt->base.dev->pdev, temp))
- goto err_out;
+ ppgtt->pd_dma_addr[pd] = pd_addr;
- ppgtt->gen8_pt_dma_addr[i][j] = temp;
+ return 0;
+}
+
+static int gen8_ppgtt_setup_page_tables(struct i915_hw_ppgtt *ppgtt,
+ const int pd,
+ const int pt)
+{
+ dma_addr_t pt_addr;
+ struct page *p;
+ int ret;
+
+ p = ppgtt->gen8_pt_pages[pd][pt];
+ pt_addr = pci_map_page(ppgtt->base.dev->pdev,
+ p, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ ret = pci_dma_mapping_error(ppgtt->base.dev->pdev, pt_addr);
+ if (ret)
+ return ret;
+
+ ppgtt->gen8_pt_dma_addr[pd][pt] = pt_addr;
+
+ return 0;
+}
+
+/**
+ * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers
+ * with a net effect resembling a 2-level page table in normal x86 terms. Each
+ * PDP represents 1GB of memory 4 * 512 * 512 * 4096 = 4GB legacy 32b address
+ * space.
+ *
+ * FIXME: split allocation into smaller pieces. For now we only ever do this
+ * once, but with full PPGTT, the multiple contiguous allocations will be bad.
+ * TODO: Do something with the size parameter
+ */
+static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
+{
+ const int max_pdp = DIV_ROUND_UP(size, 1 << 30);
+ const int min_pt_pages = GEN8_PDES_PER_PAGE * max_pdp;
+ int i, j, ret;
+
+ if (size % (1<<30))
+ DRM_INFO("Pages will be wasted unless GTT size (%llu) is divisible by 1GB\n", size);
+
+ /* 1. Do all our allocations for page directories and page tables. */
+ ret = gen8_ppgtt_alloc(ppgtt, max_pdp);
+ if (ret)
+ return ret;
+
+ /*
+ * 2. Create DMA mappings for the page directories and page tables.
+ */
+ for (i = 0; i < max_pdp; i++) {
+ ret = gen8_ppgtt_setup_page_directories(ppgtt, i);
+ if (ret)
+ goto bail;
+
+ for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
+ ret = gen8_ppgtt_setup_page_tables(ppgtt, i, j);
+ if (ret)
+ goto bail;
}
}
- /* For now, the PPGTT helper functions all require that the PDEs are
+ /*
+ * 3. Map all the page directory entires to point to the page tables
+ * we've allocated.
+ *
+ * For now, the PPGTT helper functions all require that the PDEs are
* plugged in correctly. So we do that now/here. For aliasing PPGTT, we
- * will never need to touch the PDEs again */
+ * will never need to touch the PDEs again.
+ */
for (i = 0; i < max_pdp; i++) {
gen8_ppgtt_pde_t *pd_vaddr;
pd_vaddr = kmap_atomic(&ppgtt->pd_pages[i]);
@@ -441,23 +634,85 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
kunmap_atomic(pd_vaddr);
}
- ppgtt->base.clear_range(&ppgtt->base, 0,
- ppgtt->num_pd_entries * GEN8_PTES_PER_PAGE,
- true);
+ ppgtt->enable = gen8_ppgtt_enable;
+ ppgtt->switch_mm = gen8_mm_switch;
+ ppgtt->base.clear_range = gen8_ppgtt_clear_range;
+ ppgtt->base.insert_entries = gen8_ppgtt_insert_entries;
+ ppgtt->base.cleanup = gen8_ppgtt_cleanup;
+ ppgtt->base.start = 0;
+ ppgtt->base.total = ppgtt->num_pd_entries * GEN8_PTES_PER_PAGE * PAGE_SIZE;
+
+ ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
DRM_DEBUG_DRIVER("Allocated %d pages for page directories (%d wasted)\n",
ppgtt->num_pd_pages, ppgtt->num_pd_pages - max_pdp);
DRM_DEBUG_DRIVER("Allocated %d pages for page tables (%lld wasted)\n",
- ppgtt->num_pt_pages,
- (ppgtt->num_pt_pages - num_pt_pages) +
- size % (1<<30));
+ ppgtt->num_pd_entries,
+ (ppgtt->num_pd_entries - min_pt_pages) + size % (1<<30));
return 0;
-err_out:
- ppgtt->base.cleanup(&ppgtt->base);
+bail:
+ gen8_ppgtt_unmap_pages(ppgtt);
+ gen8_ppgtt_free(ppgtt);
return ret;
}
+static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
+{
+ struct drm_i915_private *dev_priv = ppgtt->base.dev->dev_private;
+ struct i915_address_space *vm = &ppgtt->base;
+ gen6_gtt_pte_t __iomem *pd_addr;
+ gen6_gtt_pte_t scratch_pte;
+ uint32_t pd_entry;
+ int pte, pde;
+
+ scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true);
+
+ pd_addr = (gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm +
+ ppgtt->pd_offset / sizeof(gen6_gtt_pte_t);
+
+ seq_printf(m, " VM %p (pd_offset %x-%x):\n", vm,
+ ppgtt->pd_offset, ppgtt->pd_offset + ppgtt->num_pd_entries);
+ for (pde = 0; pde < ppgtt->num_pd_entries; pde++) {
+ u32 expected;
+ gen6_gtt_pte_t *pt_vaddr;
+ dma_addr_t pt_addr = ppgtt->pt_dma_addr[pde];
+ pd_entry = readl(pd_addr + pde);
+ expected = (GEN6_PDE_ADDR_ENCODE(pt_addr) | GEN6_PDE_VALID);
+
+ if (pd_entry != expected)
+ seq_printf(m, "\tPDE #%d mismatch: Actual PDE: %x Expected PDE: %x\n",
+ pde,
+ pd_entry,
+ expected);
+ seq_printf(m, "\tPDE: %x\n", pd_entry);
+
+ pt_vaddr = kmap_atomic(ppgtt->pt_pages[pde]);
+ for (pte = 0; pte < I915_PPGTT_PT_ENTRIES; pte+=4) {
+ unsigned long va =
+ (pde * PAGE_SIZE * I915_PPGTT_PT_ENTRIES) +
+ (pte * PAGE_SIZE);
+ int i;
+ bool found = false;
+ for (i = 0; i < 4; i++)
+ if (pt_vaddr[pte + i] != scratch_pte)
+ found = true;
+ if (!found)
+ continue;
+
+ seq_printf(m, "\t\t0x%lx [%03d,%04d]: =", va, pde, pte);
+ for (i = 0; i < 4; i++) {
+ if (pt_vaddr[pte + i] != scratch_pte)
+ seq_printf(m, " %08x", pt_vaddr[pte + i]);
+ else
+ seq_puts(m, " SCRATCH ");
+ }
+ seq_puts(m, "\n");
+ }
+ kunmap_atomic(pt_vaddr);
+ }
+}
+
static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
{
struct drm_i915_private *dev_priv = ppgtt->base.dev->dev_private;
@@ -480,73 +735,235 @@ static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
readl(pd_addr);
}
-static int gen6_ppgtt_enable(struct drm_device *dev)
+static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt)
+{
+ BUG_ON(ppgtt->pd_offset & 0x3f);
+
+ return (ppgtt->pd_offset / 64) << 16;
+}
+
+static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
+ struct intel_ring_buffer *ring,
+ bool synchronous)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ /* If we're in reset, we can assume the GPU is sufficiently idle to
+ * manually frob these bits. Ideally we could use the ring functions,
+ * except our error handling makes it quite difficult (can't use
+ * intel_ring_begin, ring->flush, or intel_ring_advance)
+ *
+ * FIXME: We should try not to special case reset
+ */
+ if (synchronous ||
+ i915_reset_in_progress(&dev_priv->gpu_error)) {
+ WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt);
+ I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
+ I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt));
+ POSTING_READ(RING_PP_DIR_BASE(ring));
+ return 0;
+ }
+
+ /* NB: TLBs must be flushed and invalidated before a switch */
+ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+ if (ret)
+ return ret;
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(2));
+ intel_ring_emit(ring, RING_PP_DIR_DCLV(ring));
+ intel_ring_emit(ring, PP_DIR_DCLV_2G);
+ intel_ring_emit(ring, RING_PP_DIR_BASE(ring));
+ intel_ring_emit(ring, get_pd_offset(ppgtt));
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+
+ return 0;
+}
+
+static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
+ struct intel_ring_buffer *ring,
+ bool synchronous)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ /* If we're in reset, we can assume the GPU is sufficiently idle to
+ * manually frob these bits. Ideally we could use the ring functions,
+ * except our error handling makes it quite difficult (can't use
+ * intel_ring_begin, ring->flush, or intel_ring_advance)
+ *
+ * FIXME: We should try not to special case reset
+ */
+ if (synchronous ||
+ i915_reset_in_progress(&dev_priv->gpu_error)) {
+ WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt);
+ I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
+ I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt));
+ POSTING_READ(RING_PP_DIR_BASE(ring));
+ return 0;
+ }
+
+ /* NB: TLBs must be flushed and invalidated before a switch */
+ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+ if (ret)
+ return ret;
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(2));
+ intel_ring_emit(ring, RING_PP_DIR_DCLV(ring));
+ intel_ring_emit(ring, PP_DIR_DCLV_2G);
+ intel_ring_emit(ring, RING_PP_DIR_BASE(ring));
+ intel_ring_emit(ring, get_pd_offset(ppgtt));
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+
+ /* XXX: RCS is the only one to auto invalidate the TLBs? */
+ if (ring->id != RCS) {
+ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt,
+ struct intel_ring_buffer *ring,
+ bool synchronous)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!synchronous)
+ return 0;
+
+ I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
+ I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt));
+
+ POSTING_READ(RING_PP_DIR_DCLV(ring));
+
+ return 0;
+}
+
+static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
- uint32_t pd_offset;
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
- struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
- int i;
+ int j, ret;
- BUG_ON(ppgtt->pd_offset & 0x3f);
+ for_each_ring(ring, dev_priv, j) {
+ I915_WRITE(RING_MODE_GEN7(ring),
+ _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
- gen6_write_pdes(ppgtt);
+ /* We promise to do a switch later with FULL PPGTT. If this is
+ * aliasing, this is the one and only switch we'll do */
+ if (USES_FULL_PPGTT(dev))
+ continue;
- pd_offset = ppgtt->pd_offset;
- pd_offset /= 64; /* in cachelines, */
- pd_offset <<= 16;
+ ret = ppgtt->switch_mm(ppgtt, ring, true);
+ if (ret)
+ goto err_out;
+ }
- if (INTEL_INFO(dev)->gen == 6) {
- uint32_t ecochk, gab_ctl, ecobits;
+ return 0;
- ecobits = I915_READ(GAC_ECO_BITS);
- I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_SNB_BIT |
- ECOBITS_PPGTT_CACHE64B);
+err_out:
+ for_each_ring(ring, dev_priv, j)
+ I915_WRITE(RING_MODE_GEN7(ring),
+ _MASKED_BIT_DISABLE(GFX_PPGTT_ENABLE));
+ return ret;
+}
- gab_ctl = I915_READ(GAB_CTL);
- I915_WRITE(GAB_CTL, gab_ctl | GAB_CTL_CONT_AFTER_PAGEFAULT);
+static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_ring_buffer *ring;
+ uint32_t ecochk, ecobits;
+ int i;
- ecochk = I915_READ(GAM_ECOCHK);
- I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT |
- ECOCHK_PPGTT_CACHE64B);
- I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
- } else if (INTEL_INFO(dev)->gen >= 7) {
- uint32_t ecochk, ecobits;
+ ecobits = I915_READ(GAC_ECO_BITS);
+ I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_PPGTT_CACHE64B);
- ecobits = I915_READ(GAC_ECO_BITS);
- I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_PPGTT_CACHE64B);
+ ecochk = I915_READ(GAM_ECOCHK);
+ if (IS_HASWELL(dev)) {
+ ecochk |= ECOCHK_PPGTT_WB_HSW;
+ } else {
+ ecochk |= ECOCHK_PPGTT_LLC_IVB;
+ ecochk &= ~ECOCHK_PPGTT_GFDT_IVB;
+ }
+ I915_WRITE(GAM_ECOCHK, ecochk);
- ecochk = I915_READ(GAM_ECOCHK);
- if (IS_HASWELL(dev)) {
- ecochk |= ECOCHK_PPGTT_WB_HSW;
- } else {
- ecochk |= ECOCHK_PPGTT_LLC_IVB;
- ecochk &= ~ECOCHK_PPGTT_GFDT_IVB;
- }
- I915_WRITE(GAM_ECOCHK, ecochk);
+ for_each_ring(ring, dev_priv, i) {
+ int ret;
/* GFX_MODE is per-ring on gen7+ */
+ I915_WRITE(RING_MODE_GEN7(ring),
+ _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
+
+ /* We promise to do a switch later with FULL PPGTT. If this is
+ * aliasing, this is the one and only switch we'll do */
+ if (USES_FULL_PPGTT(dev))
+ continue;
+
+ ret = ppgtt->switch_mm(ppgtt, ring, true);
+ if (ret)
+ return ret;
}
- for_each_ring(ring, dev_priv, i) {
- if (INTEL_INFO(dev)->gen >= 7)
- I915_WRITE(RING_MODE_GEN7(ring),
- _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
+ return 0;
+}
- I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
- I915_WRITE(RING_PP_DIR_BASE(ring), pd_offset);
+static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_ring_buffer *ring;
+ uint32_t ecochk, gab_ctl, ecobits;
+ int i;
+
+ ecobits = I915_READ(GAC_ECO_BITS);
+ I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_SNB_BIT |
+ ECOBITS_PPGTT_CACHE64B);
+
+ gab_ctl = I915_READ(GAB_CTL);
+ I915_WRITE(GAB_CTL, gab_ctl | GAB_CTL_CONT_AFTER_PAGEFAULT);
+
+ ecochk = I915_READ(GAM_ECOCHK);
+ I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT | ECOCHK_PPGTT_CACHE64B);
+
+ I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
+
+ for_each_ring(ring, dev_priv, i) {
+ int ret = ppgtt->switch_mm(ppgtt, ring, true);
+ if (ret)
+ return ret;
}
+
return 0;
}
/* PPGTT support for Sandybdrige/Gen6 and later */
static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
- unsigned first_entry,
- unsigned num_entries,
+ uint64_t start,
+ uint64_t length,
bool use_scratch)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
gen6_gtt_pte_t *pt_vaddr, scratch_pte;
+ unsigned first_entry = start >> PAGE_SHIFT;
+ unsigned num_entries = length >> PAGE_SHIFT;
unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
unsigned last_pte, i;
@@ -573,12 +990,13 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
struct sg_table *pages,
- unsigned first_entry,
+ uint64_t start,
enum i915_cache_level cache_level)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
gen6_gtt_pte_t *pt_vaddr;
+ unsigned first_entry = start >> PAGE_SHIFT;
unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
unsigned act_pte = first_entry % I915_PPGTT_PT_ENTRIES;
struct sg_page_iter sg_iter;
@@ -602,65 +1020,130 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
kunmap_atomic(pt_vaddr);
}
-static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
+static void gen6_ppgtt_unmap_pages(struct i915_hw_ppgtt *ppgtt)
{
- struct i915_hw_ppgtt *ppgtt =
- container_of(vm, struct i915_hw_ppgtt, base);
int i;
- drm_mm_takedown(&ppgtt->base.mm);
-
if (ppgtt->pt_dma_addr) {
for (i = 0; i < ppgtt->num_pd_entries; i++)
pci_unmap_page(ppgtt->base.dev->pdev,
ppgtt->pt_dma_addr[i],
4096, PCI_DMA_BIDIRECTIONAL);
}
+}
+
+static void gen6_ppgtt_free(struct i915_hw_ppgtt *ppgtt)
+{
+ int i;
kfree(ppgtt->pt_dma_addr);
for (i = 0; i < ppgtt->num_pd_entries; i++)
__free_page(ppgtt->pt_pages[i]);
kfree(ppgtt->pt_pages);
- kfree(ppgtt);
}
-static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
+static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
{
+ struct i915_hw_ppgtt *ppgtt =
+ container_of(vm, struct i915_hw_ppgtt, base);
+
+ list_del(&vm->global_link);
+ drm_mm_takedown(&ppgtt->base.mm);
+ drm_mm_remove_node(&ppgtt->node);
+
+ gen6_ppgtt_unmap_pages(ppgtt);
+ gen6_ppgtt_free(ppgtt);
+}
+
+static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt)
+{
+#define GEN6_PD_ALIGN (PAGE_SIZE * 16)
+#define GEN6_PD_SIZE (GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE)
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- unsigned first_pd_entry_in_global_pt;
- int i;
- int ret = -ENOMEM;
+ bool retried = false;
+ int ret;
- /* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
- * entries. For aliasing ppgtt support we just steal them at the end for
- * now. */
- first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt);
+ /* PPGTT PDEs reside in the GGTT and consists of 512 entries. The
+ * allocator works in address space sizes, so it's multiplied by page
+ * size. We allocate at the top of the GTT to avoid fragmentation.
+ */
+ BUG_ON(!drm_mm_initialized(&dev_priv->gtt.base.mm));
+alloc:
+ ret = drm_mm_insert_node_in_range_generic(&dev_priv->gtt.base.mm,
+ &ppgtt->node, GEN6_PD_SIZE,
+ GEN6_PD_ALIGN, 0,
+ 0, dev_priv->gtt.base.total,
+ DRM_MM_SEARCH_DEFAULT,
+ DRM_MM_CREATE_DEFAULT);
+ if (ret == -ENOSPC && !retried) {
+ ret = i915_gem_evict_something(dev, &dev_priv->gtt.base,
+ GEN6_PD_SIZE, GEN6_PD_ALIGN,
+ I915_CACHE_NONE, 0);
+ if (ret)
+ return ret;
+
+ retried = true;
+ goto alloc;
+ }
+
+ if (ppgtt->node.start < dev_priv->gtt.mappable_end)
+ DRM_DEBUG("Forced to use aperture for PDEs\n");
- ppgtt->base.pte_encode = dev_priv->gtt.base.pte_encode;
ppgtt->num_pd_entries = GEN6_PPGTT_PD_ENTRIES;
- ppgtt->enable = gen6_ppgtt_enable;
- ppgtt->base.clear_range = gen6_ppgtt_clear_range;
- ppgtt->base.insert_entries = gen6_ppgtt_insert_entries;
- ppgtt->base.cleanup = gen6_ppgtt_cleanup;
- ppgtt->base.scratch = dev_priv->gtt.base.scratch;
- ppgtt->base.start = 0;
- ppgtt->base.total = GEN6_PPGTT_PD_ENTRIES * I915_PPGTT_PT_ENTRIES * PAGE_SIZE;
+ return ret;
+}
+
+static int gen6_ppgtt_allocate_page_tables(struct i915_hw_ppgtt *ppgtt)
+{
+ int i;
+
ppgtt->pt_pages = kcalloc(ppgtt->num_pd_entries, sizeof(struct page *),
GFP_KERNEL);
+
if (!ppgtt->pt_pages)
return -ENOMEM;
for (i = 0; i < ppgtt->num_pd_entries; i++) {
ppgtt->pt_pages[i] = alloc_page(GFP_KERNEL);
- if (!ppgtt->pt_pages[i])
- goto err_pt_alloc;
+ if (!ppgtt->pt_pages[i]) {
+ gen6_ppgtt_free(ppgtt);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt)
+{
+ int ret;
+
+ ret = gen6_ppgtt_allocate_page_directories(ppgtt);
+ if (ret)
+ return ret;
+
+ ret = gen6_ppgtt_allocate_page_tables(ppgtt);
+ if (ret) {
+ drm_mm_remove_node(&ppgtt->node);
+ return ret;
}
ppgtt->pt_dma_addr = kcalloc(ppgtt->num_pd_entries, sizeof(dma_addr_t),
GFP_KERNEL);
- if (!ppgtt->pt_dma_addr)
- goto err_pt_alloc;
+ if (!ppgtt->pt_dma_addr) {
+ drm_mm_remove_node(&ppgtt->node);
+ gen6_ppgtt_free(ppgtt);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int gen6_ppgtt_setup_page_tables(struct i915_hw_ppgtt *ppgtt)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ int i;
for (i = 0; i < ppgtt->num_pd_entries; i++) {
dma_addr_t pt_addr;
@@ -669,48 +1152,71 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(dev->pdev, pt_addr)) {
- ret = -EIO;
- goto err_pd_pin;
-
+ gen6_ppgtt_unmap_pages(ppgtt);
+ return -EIO;
}
+
ppgtt->pt_dma_addr[i] = pt_addr;
}
- ppgtt->base.clear_range(&ppgtt->base, 0,
- ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES, true);
+ return 0;
+}
- ppgtt->pd_offset = first_pd_entry_in_global_pt * sizeof(gen6_gtt_pte_t);
+static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
+{
+ struct drm_device *dev = ppgtt->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
- return 0;
+ ppgtt->base.pte_encode = dev_priv->gtt.base.pte_encode;
+ if (IS_GEN6(dev)) {
+ ppgtt->enable = gen6_ppgtt_enable;
+ ppgtt->switch_mm = gen6_mm_switch;
+ } else if (IS_HASWELL(dev)) {
+ ppgtt->enable = gen7_ppgtt_enable;
+ ppgtt->switch_mm = hsw_mm_switch;
+ } else if (IS_GEN7(dev)) {
+ ppgtt->enable = gen7_ppgtt_enable;
+ ppgtt->switch_mm = gen7_mm_switch;
+ } else
+ BUG();
-err_pd_pin:
- if (ppgtt->pt_dma_addr) {
- for (i--; i >= 0; i--)
- pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
- 4096, PCI_DMA_BIDIRECTIONAL);
- }
-err_pt_alloc:
- kfree(ppgtt->pt_dma_addr);
- for (i = 0; i < ppgtt->num_pd_entries; i++) {
- if (ppgtt->pt_pages[i])
- __free_page(ppgtt->pt_pages[i]);
+ ret = gen6_ppgtt_alloc(ppgtt);
+ if (ret)
+ return ret;
+
+ ret = gen6_ppgtt_setup_page_tables(ppgtt);
+ if (ret) {
+ gen6_ppgtt_free(ppgtt);
+ return ret;
}
- kfree(ppgtt->pt_pages);
- return ret;
+ ppgtt->base.clear_range = gen6_ppgtt_clear_range;
+ ppgtt->base.insert_entries = gen6_ppgtt_insert_entries;
+ ppgtt->base.cleanup = gen6_ppgtt_cleanup;
+ ppgtt->base.start = 0;
+ ppgtt->base.total = ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES * PAGE_SIZE;
+ ppgtt->debug_dump = gen6_dump_ppgtt;
+
+ ppgtt->pd_offset =
+ ppgtt->node.start / PAGE_SIZE * sizeof(gen6_gtt_pte_t);
+
+ ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
+
+ DRM_DEBUG_DRIVER("Allocated pde space (%ldM) at GTT entry: %lx\n",
+ ppgtt->node.size >> 20,
+ ppgtt->node.start / PAGE_SIZE);
+
+ return 0;
}
-static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
+int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_hw_ppgtt *ppgtt;
- int ret;
-
- ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
- if (!ppgtt)
- return -ENOMEM;
+ int ret = 0;
ppgtt->base.dev = dev;
+ ppgtt->base.scratch = dev_priv->gtt.base.scratch;
if (INTEL_INFO(dev)->gen < 8)
ret = gen6_ppgtt_init(ppgtt);
@@ -719,45 +1225,37 @@ static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
else
BUG();
- if (ret)
- kfree(ppgtt);
- else {
- dev_priv->mm.aliasing_ppgtt = ppgtt;
+ if (!ret) {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ kref_init(&ppgtt->ref);
drm_mm_init(&ppgtt->base.mm, ppgtt->base.start,
ppgtt->base.total);
+ i915_init_vm(dev_priv, &ppgtt->base);
+ if (INTEL_INFO(dev)->gen < 8) {
+ gen6_write_pdes(ppgtt);
+ DRM_DEBUG("Adding PPGTT at offset %x\n",
+ ppgtt->pd_offset << 10);
+ }
}
return ret;
}
-void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
+static void
+ppgtt_bind_vma(struct i915_vma *vma,
+ enum i915_cache_level cache_level,
+ u32 flags)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
-
- if (!ppgtt)
- return;
-
- ppgtt->base.cleanup(&ppgtt->base);
- dev_priv->mm.aliasing_ppgtt = NULL;
+ vma->vm->insert_entries(vma->vm, vma->obj->pages, vma->node.start,
+ cache_level);
}
-void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
- struct drm_i915_gem_object *obj,
- enum i915_cache_level cache_level)
+static void ppgtt_unbind_vma(struct i915_vma *vma)
{
- ppgtt->base.insert_entries(&ppgtt->base, obj->pages,
- i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
- cache_level);
-}
-
-void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
- struct drm_i915_gem_object *obj)
-{
- ppgtt->base.clear_range(&ppgtt->base,
- i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
- obj->base.size >> PAGE_SHIFT,
- true);
+ vma->vm->clear_range(vma->vm,
+ vma->node.start,
+ vma->obj->base.size,
+ true);
}
extern int intel_iommu_gfx_mapped;
@@ -840,8 +1338,8 @@ void i915_gem_suspend_gtt_mappings(struct drm_device *dev)
i915_check_and_clear_faults(dev);
dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
- dev_priv->gtt.base.start / PAGE_SIZE,
- dev_priv->gtt.base.total / PAGE_SIZE,
+ dev_priv->gtt.base.start,
+ dev_priv->gtt.base.total,
true);
}
@@ -849,18 +1347,46 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
+ struct i915_address_space *vm;
i915_check_and_clear_faults(dev);
/* First fill our portion of the GTT with scratch pages */
dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
- dev_priv->gtt.base.start / PAGE_SIZE,
- dev_priv->gtt.base.total / PAGE_SIZE,
+ dev_priv->gtt.base.start,
+ dev_priv->gtt.base.total,
true);
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
+ struct i915_vma *vma = i915_gem_obj_to_vma(obj,
+ &dev_priv->gtt.base);
+ if (!vma)
+ continue;
+
i915_gem_clflush_object(obj, obj->pin_display);
- i915_gem_gtt_bind_object(obj, obj->cache_level);
+ /* The bind_vma code tries to be smart about tracking mappings.
+ * Unfortunately above, we've just wiped out the mappings
+ * without telling our object about it. So we need to fake it.
+ */
+ obj->has_global_gtt_mapping = 0;
+ vma->bind_vma(vma, obj->cache_level, GLOBAL_BIND);
+ }
+
+
+ if (INTEL_INFO(dev)->gen >= 8) {
+ gen8_setup_private_ppat(dev_priv);
+ return;
+ }
+
+ list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
+ /* TODO: Perhaps it shouldn't be gen6 specific */
+ if (i915_is_ggtt(vm)) {
+ if (dev_priv->mm.aliasing_ppgtt)
+ gen6_write_pdes(dev_priv->mm.aliasing_ppgtt);
+ continue;
+ }
+
+ gen6_write_pdes(container_of(vm, struct i915_hw_ppgtt, base));
}
i915_gem_chipset_flush(dev);
@@ -891,10 +1417,11 @@ static inline void gen8_set_pte(void __iomem *addr, gen8_gtt_pte_t pte)
static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
struct sg_table *st,
- unsigned int first_entry,
+ uint64_t start,
enum i915_cache_level level)
{
struct drm_i915_private *dev_priv = vm->dev->dev_private;
+ unsigned first_entry = start >> PAGE_SHIFT;
gen8_gtt_pte_t __iomem *gtt_entries =
(gen8_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
int i = 0;
@@ -936,10 +1463,11 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
*/
static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
struct sg_table *st,
- unsigned int first_entry,
+ uint64_t start,
enum i915_cache_level level)
{
struct drm_i915_private *dev_priv = vm->dev->dev_private;
+ unsigned first_entry = start >> PAGE_SHIFT;
gen6_gtt_pte_t __iomem *gtt_entries =
(gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
int i = 0;
@@ -971,11 +1499,13 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
}
static void gen8_ggtt_clear_range(struct i915_address_space *vm,
- unsigned int first_entry,
- unsigned int num_entries,
+ uint64_t start,
+ uint64_t length,
bool use_scratch)
{
struct drm_i915_private *dev_priv = vm->dev->dev_private;
+ unsigned first_entry = start >> PAGE_SHIFT;
+ unsigned num_entries = length >> PAGE_SHIFT;
gen8_gtt_pte_t scratch_pte, __iomem *gtt_base =
(gen8_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
@@ -995,11 +1525,13 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm,
}
static void gen6_ggtt_clear_range(struct i915_address_space *vm,
- unsigned int first_entry,
- unsigned int num_entries,
+ uint64_t start,
+ uint64_t length,
bool use_scratch)
{
struct drm_i915_private *dev_priv = vm->dev->dev_private;
+ unsigned first_entry = start >> PAGE_SHIFT;
+ unsigned num_entries = length >> PAGE_SHIFT;
gen6_gtt_pte_t scratch_pte, __iomem *gtt_base =
(gen6_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
@@ -1017,53 +1549,103 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
readl(gtt_base);
}
-static void i915_ggtt_insert_entries(struct i915_address_space *vm,
- struct sg_table *st,
- unsigned int pg_start,
- enum i915_cache_level cache_level)
+
+static void i915_ggtt_bind_vma(struct i915_vma *vma,
+ enum i915_cache_level cache_level,
+ u32 unused)
{
+ const unsigned long entry = vma->node.start >> PAGE_SHIFT;
unsigned int flags = (cache_level == I915_CACHE_NONE) ?
AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY;
- intel_gtt_insert_sg_entries(st, pg_start, flags);
-
+ BUG_ON(!i915_is_ggtt(vma->vm));
+ intel_gtt_insert_sg_entries(vma->obj->pages, entry, flags);
+ vma->obj->has_global_gtt_mapping = 1;
}
static void i915_ggtt_clear_range(struct i915_address_space *vm,
- unsigned int first_entry,
- unsigned int num_entries,
+ uint64_t start,
+ uint64_t length,
bool unused)
{
+ unsigned first_entry = start >> PAGE_SHIFT;
+ unsigned num_entries = length >> PAGE_SHIFT;
intel_gtt_clear_range(first_entry, num_entries);
}
+static void i915_ggtt_unbind_vma(struct i915_vma *vma)
+{
+ const unsigned int first = vma->node.start >> PAGE_SHIFT;
+ const unsigned int size = vma->obj->base.size >> PAGE_SHIFT;
+
+ BUG_ON(!i915_is_ggtt(vma->vm));
+ vma->obj->has_global_gtt_mapping = 0;
+ intel_gtt_clear_range(first, size);
+}
-void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
- enum i915_cache_level cache_level)
+static void ggtt_bind_vma(struct i915_vma *vma,
+ enum i915_cache_level cache_level,
+ u32 flags)
{
- struct drm_device *dev = obj->base.dev;
+ struct drm_device *dev = vma->vm->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- const unsigned long entry = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT;
+ struct drm_i915_gem_object *obj = vma->obj;
- dev_priv->gtt.base.insert_entries(&dev_priv->gtt.base, obj->pages,
- entry,
- cache_level);
+ /* If there is no aliasing PPGTT, or the caller needs a global mapping,
+ * or we have a global mapping already but the cacheability flags have
+ * changed, set the global PTEs.
+ *
+ * If there is an aliasing PPGTT it is anecdotally faster, so use that
+ * instead if none of the above hold true.
+ *
+ * NB: A global mapping should only be needed for special regions like
+ * "gtt mappable", SNB errata, or if specified via special execbuf
+ * flags. At all other times, the GPU will use the aliasing PPGTT.
+ */
+ if (!dev_priv->mm.aliasing_ppgtt || flags & GLOBAL_BIND) {
+ if (!obj->has_global_gtt_mapping ||
+ (cache_level != obj->cache_level)) {
+ vma->vm->insert_entries(vma->vm, obj->pages,
+ vma->node.start,
+ cache_level);
+ obj->has_global_gtt_mapping = 1;
+ }
+ }
- obj->has_global_gtt_mapping = 1;
+ if (dev_priv->mm.aliasing_ppgtt &&
+ (!obj->has_aliasing_ppgtt_mapping ||
+ (cache_level != obj->cache_level))) {
+ struct i915_hw_ppgtt *appgtt = dev_priv->mm.aliasing_ppgtt;
+ appgtt->base.insert_entries(&appgtt->base,
+ vma->obj->pages,
+ vma->node.start,
+ cache_level);
+ vma->obj->has_aliasing_ppgtt_mapping = 1;
+ }
}
-void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
+static void ggtt_unbind_vma(struct i915_vma *vma)
{
- struct drm_device *dev = obj->base.dev;
+ struct drm_device *dev = vma->vm->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- const unsigned long entry = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT;
-
- dev_priv->gtt.base.clear_range(&dev_priv->gtt.base,
- entry,
- obj->base.size >> PAGE_SHIFT,
- true);
+ struct drm_i915_gem_object *obj = vma->obj;
+
+ if (obj->has_global_gtt_mapping) {
+ vma->vm->clear_range(vma->vm,
+ vma->node.start,
+ obj->base.size,
+ true);
+ obj->has_global_gtt_mapping = 0;
+ }
- obj->has_global_gtt_mapping = 0;
+ if (obj->has_aliasing_ppgtt_mapping) {
+ struct i915_hw_ppgtt *appgtt = dev_priv->mm.aliasing_ppgtt;
+ appgtt->base.clear_range(&appgtt->base,
+ vma->node.start,
+ obj->base.size,
+ true);
+ obj->has_aliasing_ppgtt_mapping = 0;
+ }
}
void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj)
@@ -1145,29 +1727,14 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
/* Clear any non-preallocated blocks */
drm_mm_for_each_hole(entry, &ggtt_vm->mm, hole_start, hole_end) {
- const unsigned long count = (hole_end - hole_start) / PAGE_SIZE;
DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
hole_start, hole_end);
- ggtt_vm->clear_range(ggtt_vm, hole_start / PAGE_SIZE, count, true);
+ ggtt_vm->clear_range(ggtt_vm, hole_start,
+ hole_end - hole_start, true);
}
/* And finally clear the reserved guard page */
- ggtt_vm->clear_range(ggtt_vm, end / PAGE_SIZE - 1, 1, true);
-}
-
-static bool
-intel_enable_ppgtt(struct drm_device *dev)
-{
- if (i915_enable_ppgtt >= 0)
- return i915_enable_ppgtt;
-
-#ifdef CONFIG_INTEL_IOMMU
- /* Disable ppgtt on SNB if VT-d is on. */
- if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped)
- return false;
-#endif
-
- return true;
+ ggtt_vm->clear_range(ggtt_vm, end - PAGE_SIZE, PAGE_SIZE, true);
}
void i915_gem_init_global_gtt(struct drm_device *dev)
@@ -1178,26 +1745,6 @@ void i915_gem_init_global_gtt(struct drm_device *dev)
gtt_size = dev_priv->gtt.base.total;
mappable_size = dev_priv->gtt.mappable_end;
- if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) {
- int ret;
-
- if (INTEL_INFO(dev)->gen <= 7) {
- /* PPGTT pdes are stolen from global gtt ptes, so shrink the
- * aperture accordingly when using aliasing ppgtt. */
- gtt_size -= GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE;
- }
-
- i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
-
- ret = i915_gem_init_aliasing_ppgtt(dev);
- if (!ret)
- return;
-
- DRM_ERROR("Aliased PPGTT setup failed %d\n", ret);
- drm_mm_takedown(&dev_priv->gtt.base.mm);
- if (INTEL_INFO(dev)->gen < 8)
- gtt_size += GEN6_PPGTT_PD_ENTRIES*PAGE_SIZE;
- }
i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
}
@@ -1252,11 +1799,6 @@ static inline unsigned int gen8_get_total_gtt_size(u16 bdw_gmch_ctl)
bdw_gmch_ctl &= BDW_GMCH_GGMS_MASK;
if (bdw_gmch_ctl)
bdw_gmch_ctl = 1 << bdw_gmch_ctl;
- if (bdw_gmch_ctl > 4) {
- WARN_ON(!i915_preliminary_hw_support);
- return 4<<20;
- }
-
return bdw_gmch_ctl << 20;
}
@@ -1438,7 +1980,6 @@ static int i915_gmch_probe(struct drm_device *dev,
dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev);
dev_priv->gtt.base.clear_range = i915_ggtt_clear_range;
- dev_priv->gtt.base.insert_entries = i915_ggtt_insert_entries;
if (unlikely(dev_priv->gtt.do_idle_maps))
DRM_INFO("applying Ironlake quirks for intel_iommu\n");
@@ -1493,3 +2034,62 @@ int i915_gem_gtt_init(struct drm_device *dev)
return 0;
}
+
+static struct i915_vma *__i915_gem_vma_create(struct drm_i915_gem_object *obj,
+ struct i915_address_space *vm)
+{
+ struct i915_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL);
+ if (vma == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&vma->vma_link);
+ INIT_LIST_HEAD(&vma->mm_list);
+ INIT_LIST_HEAD(&vma->exec_list);
+ vma->vm = vm;
+ vma->obj = obj;
+
+ switch (INTEL_INFO(vm->dev)->gen) {
+ case 8:
+ case 7:
+ case 6:
+ if (i915_is_ggtt(vm)) {
+ vma->unbind_vma = ggtt_unbind_vma;
+ vma->bind_vma = ggtt_bind_vma;
+ } else {
+ vma->unbind_vma = ppgtt_unbind_vma;
+ vma->bind_vma = ppgtt_bind_vma;
+ }
+ break;
+ case 5:
+ case 4:
+ case 3:
+ case 2:
+ BUG_ON(!i915_is_ggtt(vm));
+ vma->unbind_vma = i915_ggtt_unbind_vma;
+ vma->bind_vma = i915_ggtt_bind_vma;
+ break;
+ default:
+ BUG();
+ }
+
+ /* Keep GGTT vmas first to make debug easier */
+ if (i915_is_ggtt(vm))
+ list_add(&vma->vma_link, &obj->vma_list);
+ else
+ list_add_tail(&vma->vma_link, &obj->vma_list);
+
+ return vma;
+}
+
+struct i915_vma *
+i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
+ struct i915_address_space *vm)
+{
+ struct i915_vma *vma;
+
+ vma = i915_gem_obj_to_vma(obj, vm);
+ if (!vma)
+ vma = __i915_gem_vma_create(obj, vm);
+
+ return vma;
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 28d24caa49f3..62ef55ba061c 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -215,7 +215,7 @@ int i915_gem_init_stolen(struct drm_device *dev)
int bios_reserved = 0;
#ifdef CONFIG_INTEL_IOMMU
- if (intel_iommu_gfx_mapped) {
+ if (intel_iommu_gfx_mapped && INTEL_INFO(dev)->gen < 8) {
DRM_INFO("DMAR active, disabling use of stolen memory\n");
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index b13905348048..cb150e8b4336 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -87,7 +87,7 @@
void
i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
@@ -294,7 +294,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_set_tiling *args = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
int ret = 0;
@@ -308,7 +308,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
return -EINVAL;
}
- if (obj->pin_count || obj->framebuffer_references) {
+ if (i915_gem_obj_is_pinned(obj) || obj->framebuffer_references) {
drm_gem_object_unreference_unlocked(&obj->base);
return -EBUSY;
}
@@ -415,7 +415,7 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_get_tiling *args = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 990cf8f43efd..12f1d43b2d68 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -238,50 +238,61 @@ static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a)
static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
struct drm_device *dev,
- struct drm_i915_error_state *error,
- unsigned ring)
+ struct drm_i915_error_ring *ring)
{
- BUG_ON(ring >= I915_NUM_RINGS); /* shut up confused gcc */
- if (!error->ring[ring].valid)
+ if (!ring->valid)
return;
- err_printf(m, "%s command stream:\n", ring_str(ring));
- err_printf(m, " HEAD: 0x%08x\n", error->head[ring]);
- err_printf(m, " TAIL: 0x%08x\n", error->tail[ring]);
- err_printf(m, " CTL: 0x%08x\n", error->ctl[ring]);
- err_printf(m, " ACTHD: 0x%08x\n", error->acthd[ring]);
- err_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]);
- err_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]);
- err_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]);
+ err_printf(m, " HEAD: 0x%08x\n", ring->head);
+ err_printf(m, " TAIL: 0x%08x\n", ring->tail);
+ err_printf(m, " CTL: 0x%08x\n", ring->ctl);
+ err_printf(m, " HWS: 0x%08x\n", ring->hws);
+ err_printf(m, " ACTHD: 0x%08x %08x\n", (u32)(ring->acthd>>32), (u32)ring->acthd);
+ err_printf(m, " IPEIR: 0x%08x\n", ring->ipeir);
+ err_printf(m, " IPEHR: 0x%08x\n", ring->ipehr);
+ err_printf(m, " INSTDONE: 0x%08x\n", ring->instdone);
if (INTEL_INFO(dev)->gen >= 4) {
- err_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr[ring]);
- err_printf(m, " BB_STATE: 0x%08x\n", error->bbstate[ring]);
- err_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]);
+ err_printf(m, " BBADDR: 0x%08x %08x\n", (u32)(ring->bbaddr>>32), (u32)ring->bbaddr);
+ err_printf(m, " BB_STATE: 0x%08x\n", ring->bbstate);
+ err_printf(m, " INSTPS: 0x%08x\n", ring->instps);
}
- err_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]);
- err_printf(m, " FADDR: 0x%08x\n", error->faddr[ring]);
+ err_printf(m, " INSTPM: 0x%08x\n", ring->instpm);
+ err_printf(m, " FADDR: 0x%08x\n", ring->faddr);
if (INTEL_INFO(dev)->gen >= 6) {
- err_printf(m, " RC PSMI: 0x%08x\n", error->rc_psmi[ring]);
- err_printf(m, " FAULT_REG: 0x%08x\n", error->fault_reg[ring]);
+ err_printf(m, " RC PSMI: 0x%08x\n", ring->rc_psmi);
+ err_printf(m, " FAULT_REG: 0x%08x\n", ring->fault_reg);
err_printf(m, " SYNC_0: 0x%08x [last synced 0x%08x]\n",
- error->semaphore_mboxes[ring][0],
- error->semaphore_seqno[ring][0]);
+ ring->semaphore_mboxes[0],
+ ring->semaphore_seqno[0]);
err_printf(m, " SYNC_1: 0x%08x [last synced 0x%08x]\n",
- error->semaphore_mboxes[ring][1],
- error->semaphore_seqno[ring][1]);
+ ring->semaphore_mboxes[1],
+ ring->semaphore_seqno[1]);
if (HAS_VEBOX(dev)) {
err_printf(m, " SYNC_2: 0x%08x [last synced 0x%08x]\n",
- error->semaphore_mboxes[ring][2],
- error->semaphore_seqno[ring][2]);
+ ring->semaphore_mboxes[2],
+ ring->semaphore_seqno[2]);
}
}
- err_printf(m, " seqno: 0x%08x\n", error->seqno[ring]);
- err_printf(m, " waiting: %s\n", yesno(error->waiting[ring]));
- err_printf(m, " ring->head: 0x%08x\n", error->cpu_ring_head[ring]);
- err_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]);
+ if (USES_PPGTT(dev)) {
+ err_printf(m, " GFX_MODE: 0x%08x\n", ring->vm_info.gfx_mode);
+
+ if (INTEL_INFO(dev)->gen >= 8) {
+ int i;
+ for (i = 0; i < 4; i++)
+ err_printf(m, " PDP%d: 0x%016llx\n",
+ i, ring->vm_info.pdp[i]);
+ } else {
+ err_printf(m, " PP_DIR_BASE: 0x%08x\n",
+ ring->vm_info.pp_dir_base);
+ }
+ }
+ err_printf(m, " seqno: 0x%08x\n", ring->seqno);
+ err_printf(m, " waiting: %s\n", yesno(ring->waiting));
+ err_printf(m, " ring->head: 0x%08x\n", ring->cpu_ring_head);
+ err_printf(m, " ring->tail: 0x%08x\n", ring->cpu_ring_tail);
err_printf(m, " hangcheck: %s [%d]\n",
- hangcheck_action_to_str(error->hangcheck_action[ring]),
- error->hangcheck_score[ring]);
+ hangcheck_action_to_str(ring->hangcheck_action),
+ ring->hangcheck_score);
}
void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
@@ -293,22 +304,54 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
va_end(args);
}
+static void print_error_obj(struct drm_i915_error_state_buf *m,
+ struct drm_i915_error_object *obj)
+{
+ int page, offset, elt;
+
+ for (page = offset = 0; page < obj->page_count; page++) {
+ for (elt = 0; elt < PAGE_SIZE/4; elt++) {
+ err_printf(m, "%08x : %08x\n", offset,
+ obj->pages[page][elt]);
+ offset += 4;
+ }
+ }
+}
+
int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
const struct i915_error_state_file_priv *error_priv)
{
struct drm_device *dev = error_priv->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_error_state *error = error_priv->error;
- int i, j, page, offset, elt;
+ int i, j, offset, elt;
+ int max_hangcheck_score;
if (!error) {
err_printf(m, "no error state collected\n");
goto out;
}
+ err_printf(m, "%s\n", error->error_msg);
err_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
error->time.tv_usec);
err_printf(m, "Kernel: " UTS_RELEASE "\n");
+ max_hangcheck_score = 0;
+ for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
+ if (error->ring[i].hangcheck_score > max_hangcheck_score)
+ max_hangcheck_score = error->ring[i].hangcheck_score;
+ }
+ for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
+ if (error->ring[i].hangcheck_score == max_hangcheck_score &&
+ error->ring[i].pid != -1) {
+ err_printf(m, "Active process (on ring %s): %s [%d]\n",
+ ring_str(i),
+ error->ring[i].comm,
+ error->ring[i].pid);
+ }
+ }
+ err_printf(m, "Reset count: %u\n", error->reset_count);
+ err_printf(m, "Suspend count: %u\n", error->suspend_count);
err_printf(m, "PCI ID: 0x%04x\n", dev->pdev->device);
err_printf(m, "EIR: 0x%08x\n", error->eir);
err_printf(m, "IER: 0x%08x\n", error->ier);
@@ -333,8 +376,10 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
if (INTEL_INFO(dev)->gen == 7)
err_printf(m, "ERR_INT: 0x%08x\n", error->err_int);
- for (i = 0; i < ARRAY_SIZE(error->ring); i++)
- i915_ring_error_state(m, dev, error, i);
+ for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
+ err_printf(m, "%s command stream:\n", ring_str(i));
+ i915_ring_error_state(m, dev, &error->ring[i]);
+ }
if (error->active_bo)
print_error_buffers(m, "Active",
@@ -349,18 +394,23 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
struct drm_i915_error_object *obj;
- if ((obj = error->ring[i].batchbuffer)) {
- err_printf(m, "%s --- gtt_offset = 0x%08x\n",
- dev_priv->ring[i].name,
+ obj = error->ring[i].batchbuffer;
+ if (obj) {
+ err_puts(m, dev_priv->ring[i].name);
+ if (error->ring[i].pid != -1)
+ err_printf(m, " (submitted by %s [%d])",
+ error->ring[i].comm,
+ error->ring[i].pid);
+ err_printf(m, " --- gtt_offset = 0x%08x\n",
obj->gtt_offset);
- offset = 0;
- for (page = 0; page < obj->page_count; page++) {
- for (elt = 0; elt < PAGE_SIZE/4; elt++) {
- err_printf(m, "%08x : %08x\n", offset,
- obj->pages[page][elt]);
- offset += 4;
- }
- }
+ print_error_obj(m, obj);
+ }
+
+ obj = error->ring[i].wa_batchbuffer;
+ if (obj) {
+ err_printf(m, "%s (w/a) --- gtt_offset = 0x%08x\n",
+ dev_priv->ring[i].name, obj->gtt_offset);
+ print_error_obj(m, obj);
}
if (error->ring[i].num_requests) {
@@ -379,14 +429,22 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
err_printf(m, "%s --- ringbuffer = 0x%08x\n",
dev_priv->ring[i].name,
obj->gtt_offset);
+ print_error_obj(m, obj);
+ }
+
+ if ((obj = error->ring[i].hws_page)) {
+ err_printf(m, "%s --- HW Status = 0x%08x\n",
+ dev_priv->ring[i].name,
+ obj->gtt_offset);
offset = 0;
- for (page = 0; page < obj->page_count; page++) {
- for (elt = 0; elt < PAGE_SIZE/4; elt++) {
- err_printf(m, "%08x : %08x\n",
- offset,
- obj->pages[page][elt]);
- offset += 4;
- }
+ for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
+ err_printf(m, "[%04x] %08x %08x %08x %08x\n",
+ offset,
+ obj->pages[0][elt],
+ obj->pages[0][elt+1],
+ obj->pages[0][elt+2],
+ obj->pages[0][elt+3]);
+ offset += 16;
}
}
@@ -472,6 +530,7 @@ static void i915_error_state_free(struct kref *error_ref)
for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
i915_error_object_free(error->ring[i].batchbuffer);
i915_error_object_free(error->ring[i].ringbuffer);
+ i915_error_object_free(error->ring[i].hws_page);
i915_error_object_free(error->ring[i].ctx);
kfree(error->ring[i].requests);
}
@@ -485,6 +544,7 @@ static void i915_error_state_free(struct kref *error_ref)
static struct drm_i915_error_object *
i915_error_object_create_sized(struct drm_i915_private *dev_priv,
struct drm_i915_gem_object *src,
+ struct i915_address_space *vm,
const int num_pages)
{
struct drm_i915_error_object *dst;
@@ -498,7 +558,7 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv,
if (dst == NULL)
return NULL;
- reloc_offset = dst->gtt_offset = i915_gem_obj_ggtt_offset(src);
+ reloc_offset = dst->gtt_offset = i915_gem_obj_offset(src, vm);
for (i = 0; i < num_pages; i++) {
unsigned long flags;
void *d;
@@ -508,8 +568,10 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv,
goto unwind;
local_irq_save(flags);
- if (reloc_offset < dev_priv->gtt.mappable_end &&
- src->has_global_gtt_mapping) {
+ if (src->cache_level == I915_CACHE_NONE &&
+ reloc_offset < dev_priv->gtt.mappable_end &&
+ src->has_global_gtt_mapping &&
+ i915_is_ggtt(vm)) {
void __iomem *s;
/* Simply ignore tiling or any overlapping fence.
@@ -559,8 +621,12 @@ unwind:
kfree(dst);
return NULL;
}
-#define i915_error_object_create(dev_priv, src) \
- i915_error_object_create_sized((dev_priv), (src), \
+#define i915_error_object_create(dev_priv, src, vm) \
+ i915_error_object_create_sized((dev_priv), (src), (vm), \
+ (src)->base.size>>PAGE_SHIFT)
+
+#define i915_error_ggtt_object_create(dev_priv, src) \
+ i915_error_object_create_sized((dev_priv), (src), &(dev_priv)->gtt.base, \
(src)->base.size>>PAGE_SHIFT)
static void capture_bo(struct drm_i915_error_buffer *err,
@@ -575,7 +641,7 @@ static void capture_bo(struct drm_i915_error_buffer *err,
err->write_domain = obj->base.write_domain;
err->fence_reg = obj->fence_reg;
err->pinned = 0;
- if (obj->pin_count > 0)
+ if (i915_gem_obj_is_pinned(obj))
err->pinned = 1;
if (obj->user_pin_count > 0)
err->pinned = -1;
@@ -608,7 +674,7 @@ static u32 capture_pinned_bo(struct drm_i915_error_buffer *err,
int i = 0;
list_for_each_entry(obj, head, global_list) {
- if (obj->pin_count == 0)
+ if (!i915_gem_obj_is_pinned(obj))
continue;
capture_bo(err++, obj);
@@ -619,6 +685,39 @@ static u32 capture_pinned_bo(struct drm_i915_error_buffer *err,
return i;
}
+/* Generate a semi-unique error code. The code is not meant to have meaning, The
+ * code's only purpose is to try to prevent false duplicated bug reports by
+ * grossly estimating a GPU error state.
+ *
+ * TODO Ideally, hashing the batchbuffer would be a very nice way to determine
+ * the hang if we could strip the GTT offset information from it.
+ *
+ * It's only a small step better than a random number in its current form.
+ */
+static uint32_t i915_error_generate_code(struct drm_i915_private *dev_priv,
+ struct drm_i915_error_state *error,
+ int *ring_id)
+{
+ uint32_t error_code = 0;
+ int i;
+
+ /* IPEHR would be an ideal way to detect errors, as it's the gross
+ * measure of "the command that hung." However, has some very common
+ * synchronization commands which almost always appear in the case
+ * strictly a client bug. Use instdone to differentiate those some.
+ */
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ if (error->ring[i].hangcheck_action == HANGCHECK_HUNG) {
+ if (ring_id)
+ *ring_id = i;
+
+ return error->ring[i].ipehr ^ error->ring[i].instdone;
+ }
+ }
+
+ return error_code;
+}
+
static void i915_gem_record_fences(struct drm_device *dev,
struct drm_i915_error_state *error)
{
@@ -652,107 +751,114 @@ static void i915_gem_record_fences(struct drm_device *dev,
}
}
-static struct drm_i915_error_object *
-i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
- struct intel_ring_buffer *ring)
-{
- struct i915_address_space *vm;
- struct i915_vma *vma;
- struct drm_i915_gem_object *obj;
- u32 seqno;
-
- if (!ring->get_seqno)
- return NULL;
-
- if (HAS_BROKEN_CS_TLB(dev_priv->dev)) {
- u32 acthd = I915_READ(ACTHD);
-
- if (WARN_ON(ring->id != RCS))
- return NULL;
-
- obj = ring->scratch.obj;
- if (obj != NULL &&
- acthd >= i915_gem_obj_ggtt_offset(obj) &&
- acthd < i915_gem_obj_ggtt_offset(obj) + obj->base.size)
- return i915_error_object_create(dev_priv, obj);
- }
-
- seqno = ring->get_seqno(ring, false);
- list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
- list_for_each_entry(vma, &vm->active_list, mm_list) {
- obj = vma->obj;
- if (obj->ring != ring)
- continue;
-
- if (i915_seqno_passed(seqno, obj->last_read_seqno))
- continue;
-
- if ((obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) == 0)
- continue;
-
- /* We need to copy these to an anonymous buffer as the simplest
- * method to avoid being overwritten by userspace.
- */
- return i915_error_object_create(dev_priv, obj);
- }
- }
-
- return NULL;
-}
-
static void i915_record_ring_state(struct drm_device *dev,
- struct drm_i915_error_state *error,
- struct intel_ring_buffer *ring)
+ struct intel_ring_buffer *ring,
+ struct drm_i915_error_ring *ering)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (INTEL_INFO(dev)->gen >= 6) {
- error->rc_psmi[ring->id] = I915_READ(ring->mmio_base + 0x50);
- error->fault_reg[ring->id] = I915_READ(RING_FAULT_REG(ring));
- error->semaphore_mboxes[ring->id][0]
+ ering->rc_psmi = I915_READ(ring->mmio_base + 0x50);
+ ering->fault_reg = I915_READ(RING_FAULT_REG(ring));
+ ering->semaphore_mboxes[0]
= I915_READ(RING_SYNC_0(ring->mmio_base));
- error->semaphore_mboxes[ring->id][1]
+ ering->semaphore_mboxes[1]
= I915_READ(RING_SYNC_1(ring->mmio_base));
- error->semaphore_seqno[ring->id][0] = ring->sync_seqno[0];
- error->semaphore_seqno[ring->id][1] = ring->sync_seqno[1];
+ ering->semaphore_seqno[0] = ring->sync_seqno[0];
+ ering->semaphore_seqno[1] = ring->sync_seqno[1];
}
if (HAS_VEBOX(dev)) {
- error->semaphore_mboxes[ring->id][2] =
+ ering->semaphore_mboxes[2] =
I915_READ(RING_SYNC_2(ring->mmio_base));
- error->semaphore_seqno[ring->id][2] = ring->sync_seqno[2];
+ ering->semaphore_seqno[2] = ring->sync_seqno[2];
}
if (INTEL_INFO(dev)->gen >= 4) {
- error->faddr[ring->id] = I915_READ(RING_DMA_FADD(ring->mmio_base));
- error->ipeir[ring->id] = I915_READ(RING_IPEIR(ring->mmio_base));
- error->ipehr[ring->id] = I915_READ(RING_IPEHR(ring->mmio_base));
- error->instdone[ring->id] = I915_READ(RING_INSTDONE(ring->mmio_base));
- error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base));
- error->bbaddr[ring->id] = I915_READ(RING_BBADDR(ring->mmio_base));
+ ering->faddr = I915_READ(RING_DMA_FADD(ring->mmio_base));
+ ering->ipeir = I915_READ(RING_IPEIR(ring->mmio_base));
+ ering->ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
+ ering->instdone = I915_READ(RING_INSTDONE(ring->mmio_base));
+ ering->instps = I915_READ(RING_INSTPS(ring->mmio_base));
+ ering->bbaddr = I915_READ(RING_BBADDR(ring->mmio_base));
if (INTEL_INFO(dev)->gen >= 8)
- error->bbaddr[ring->id] |= (u64) I915_READ(RING_BBADDR_UDW(ring->mmio_base)) << 32;
- error->bbstate[ring->id] = I915_READ(RING_BBSTATE(ring->mmio_base));
+ ering->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(ring->mmio_base)) << 32;
+ ering->bbstate = I915_READ(RING_BBSTATE(ring->mmio_base));
} else {
- error->faddr[ring->id] = I915_READ(DMA_FADD_I8XX);
- error->ipeir[ring->id] = I915_READ(IPEIR);
- error->ipehr[ring->id] = I915_READ(IPEHR);
- error->instdone[ring->id] = I915_READ(INSTDONE);
+ ering->faddr = I915_READ(DMA_FADD_I8XX);
+ ering->ipeir = I915_READ(IPEIR);
+ ering->ipehr = I915_READ(IPEHR);
+ ering->instdone = I915_READ(INSTDONE);
+ }
+
+ ering->waiting = waitqueue_active(&ring->irq_queue);
+ ering->instpm = I915_READ(RING_INSTPM(ring->mmio_base));
+ ering->seqno = ring->get_seqno(ring, false);
+ ering->acthd = intel_ring_get_active_head(ring);
+ ering->head = I915_READ_HEAD(ring);
+ ering->tail = I915_READ_TAIL(ring);
+ ering->ctl = I915_READ_CTL(ring);
+
+ if (I915_NEED_GFX_HWS(dev)) {
+ int mmio;
+
+ if (IS_GEN7(dev)) {
+ switch (ring->id) {
+ default:
+ case RCS:
+ mmio = RENDER_HWS_PGA_GEN7;
+ break;
+ case BCS:
+ mmio = BLT_HWS_PGA_GEN7;
+ break;
+ case VCS:
+ mmio = BSD_HWS_PGA_GEN7;
+ break;
+ case VECS:
+ mmio = VEBOX_HWS_PGA_GEN7;
+ break;
+ }
+ } else if (IS_GEN6(ring->dev)) {
+ mmio = RING_HWS_PGA_GEN6(ring->mmio_base);
+ } else {
+ /* XXX: gen8 returns to sanity */
+ mmio = RING_HWS_PGA(ring->mmio_base);
+ }
+
+ ering->hws = I915_READ(mmio);
}
- error->waiting[ring->id] = waitqueue_active(&ring->irq_queue);
- error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base));
- error->seqno[ring->id] = ring->get_seqno(ring, false);
- error->acthd[ring->id] = intel_ring_get_active_head(ring);
- error->head[ring->id] = I915_READ_HEAD(ring);
- error->tail[ring->id] = I915_READ_TAIL(ring);
- error->ctl[ring->id] = I915_READ_CTL(ring);
+ ering->cpu_ring_head = ring->head;
+ ering->cpu_ring_tail = ring->tail;
+
+ ering->hangcheck_score = ring->hangcheck.score;
+ ering->hangcheck_action = ring->hangcheck.action;
+
+ if (USES_PPGTT(dev)) {
+ int i;
- error->cpu_ring_head[ring->id] = ring->head;
- error->cpu_ring_tail[ring->id] = ring->tail;
+ ering->vm_info.gfx_mode = I915_READ(RING_MODE_GEN7(ring));
- error->hangcheck_score[ring->id] = ring->hangcheck.score;
- error->hangcheck_action[ring->id] = ring->hangcheck.action;
+ switch (INTEL_INFO(dev)->gen) {
+ case 8:
+ for (i = 0; i < 4; i++) {
+ ering->vm_info.pdp[i] =
+ I915_READ(GEN8_RING_PDP_UDW(ring, i));
+ ering->vm_info.pdp[i] <<= 32;
+ ering->vm_info.pdp[i] |=
+ I915_READ(GEN8_RING_PDP_LDW(ring, i));
+ }
+ break;
+ case 7:
+ ering->vm_info.pp_dir_base =
+ I915_READ(RING_PP_DIR_BASE(ring));
+ break;
+ case 6:
+ ering->vm_info.pp_dir_base =
+ I915_READ(RING_PP_DIR_BASE_READ(ring));
+ break;
+ }
+ }
}
@@ -770,7 +876,9 @@ static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) {
ering->ctx = i915_error_object_create_sized(dev_priv,
- obj, 1);
+ obj,
+ &dev_priv->gtt.base,
+ 1);
break;
}
}
@@ -791,14 +899,48 @@ static void i915_gem_record_rings(struct drm_device *dev,
error->ring[i].valid = true;
- i915_record_ring_state(dev, error, ring);
+ i915_record_ring_state(dev, ring, &error->ring[i]);
- error->ring[i].batchbuffer =
- i915_error_first_batchbuffer(dev_priv, ring);
+ error->ring[i].pid = -1;
+ request = i915_gem_find_active_request(ring);
+ if (request) {
+ /* We need to copy these to an anonymous buffer
+ * as the simplest method to avoid being overwritten
+ * by userspace.
+ */
+ error->ring[i].batchbuffer =
+ i915_error_object_create(dev_priv,
+ request->batch_obj,
+ request->ctx ?
+ request->ctx->vm :
+ &dev_priv->gtt.base);
+
+ if (HAS_BROKEN_CS_TLB(dev_priv->dev) &&
+ ring->scratch.obj)
+ error->ring[i].wa_batchbuffer =
+ i915_error_ggtt_object_create(dev_priv,
+ ring->scratch.obj);
+
+ if (request->file_priv) {
+ struct task_struct *task;
+
+ rcu_read_lock();
+ task = pid_task(request->file_priv->file->pid,
+ PIDTYPE_PID);
+ if (task) {
+ strcpy(error->ring[i].comm, task->comm);
+ error->ring[i].pid = task->pid;
+ }
+ rcu_read_unlock();
+ }
+ }
error->ring[i].ringbuffer =
- i915_error_object_create(dev_priv, ring->obj);
+ i915_error_ggtt_object_create(dev_priv, ring->obj);
+ if (ring->status_page.obj)
+ error->ring[i].hws_page =
+ i915_error_ggtt_object_create(dev_priv, ring->status_page.obj);
i915_gem_record_active_context(ring, error, &error->ring[i]);
@@ -845,7 +987,7 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv,
i++;
error->active_bo_count[ndx] = i;
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list)
- if (obj->pin_count)
+ if (i915_gem_obj_is_pinned(obj))
i++;
error->pinned_bo_count[ndx] = i - error->active_bo_count[ndx];
@@ -879,11 +1021,6 @@ static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
list_for_each_entry(vm, &dev_priv->vm_list, global_link)
cnt++;
- if (WARN(cnt > 1, "Multiple VMs not yet supported\n"))
- cnt = 1;
-
- vm = &dev_priv->gtt.base;
-
error->active_bo = kcalloc(cnt, sizeof(*error->active_bo), GFP_ATOMIC);
error->pinned_bo = kcalloc(cnt, sizeof(*error->pinned_bo), GFP_ATOMIC);
error->active_bo_count = kcalloc(cnt, sizeof(*error->active_bo_count),
@@ -895,6 +1032,108 @@ static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
i915_gem_capture_vm(dev_priv, error, vm, i++);
}
+/* Capture all registers which don't fit into another category. */
+static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
+ struct drm_i915_error_state *error)
+{
+ struct drm_device *dev = dev_priv->dev;
+ int pipe;
+
+ /* General organization
+ * 1. Registers specific to a single generation
+ * 2. Registers which belong to multiple generations
+ * 3. Feature specific registers.
+ * 4. Everything else
+ * Please try to follow the order.
+ */
+
+ /* 1: Registers specific to a single generation */
+ if (IS_VALLEYVIEW(dev)) {
+ error->ier = I915_READ(GTIER) | I915_READ(VLV_IER);
+ error->forcewake = I915_READ(FORCEWAKE_VLV);
+ }
+
+ if (IS_GEN7(dev))
+ error->err_int = I915_READ(GEN7_ERR_INT);
+
+ if (IS_GEN6(dev)) {
+ error->forcewake = I915_READ(FORCEWAKE);
+ error->gab_ctl = I915_READ(GAB_CTL);
+ error->gfx_mode = I915_READ(GFX_MODE);
+ }
+
+ if (IS_GEN2(dev))
+ error->ier = I915_READ16(IER);
+
+ /* 2: Registers which belong to multiple generations */
+ if (INTEL_INFO(dev)->gen >= 7)
+ error->forcewake = I915_READ(FORCEWAKE_MT);
+
+ if (INTEL_INFO(dev)->gen >= 6) {
+ error->derrmr = I915_READ(DERRMR);
+ error->error = I915_READ(ERROR_GEN6);
+ error->done_reg = I915_READ(DONE_REG);
+ }
+
+ /* 3: Feature specific registers */
+ if (IS_GEN6(dev) || IS_GEN7(dev)) {
+ error->gam_ecochk = I915_READ(GAM_ECOCHK);
+ error->gac_eco = I915_READ(GAC_ECO_BITS);
+ }
+
+ /* 4: Everything else */
+ if (HAS_HW_CONTEXTS(dev))
+ error->ccid = I915_READ(CCID);
+
+ if (HAS_PCH_SPLIT(dev))
+ error->ier = I915_READ(DEIER) | I915_READ(GTIER);
+ else {
+ error->ier = I915_READ(IER);
+ for_each_pipe(pipe)
+ error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
+ }
+
+ /* 4: Everything else */
+ error->eir = I915_READ(EIR);
+ error->pgtbl_er = I915_READ(PGTBL_ER);
+
+ i915_get_extra_instdone(dev, error->extra_instdone);
+}
+
+static void i915_error_capture_msg(struct drm_device *dev,
+ struct drm_i915_error_state *error,
+ bool wedged,
+ const char *error_msg)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 ecode;
+ int ring_id = -1, len;
+
+ ecode = i915_error_generate_code(dev_priv, error, &ring_id);
+
+ len = scnprintf(error->error_msg, sizeof(error->error_msg),
+ "GPU HANG: ecode %d:0x%08x", ring_id, ecode);
+
+ if (ring_id != -1 && error->ring[ring_id].pid != -1)
+ len += scnprintf(error->error_msg + len,
+ sizeof(error->error_msg) - len,
+ ", in %s [%d]",
+ error->ring[ring_id].comm,
+ error->ring[ring_id].pid);
+
+ scnprintf(error->error_msg + len, sizeof(error->error_msg) - len,
+ ", reason: %s, action: %s",
+ error_msg,
+ wedged ? "reset" : "continue");
+}
+
+static void i915_capture_gen_state(struct drm_i915_private *dev_priv,
+ struct drm_i915_error_state *error)
+{
+ error->reset_count = i915_reset_count(&dev_priv->gpu_error);
+ error->suspend_count = dev_priv->suspend_count;
+}
+
/**
* i915_capture_error_state - capture an error record for later analysis
* @dev: drm device
@@ -904,18 +1143,13 @@ static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
* out a structure which becomes available in debugfs for user level tools
* to pick up.
*/
-void i915_capture_error_state(struct drm_device *dev)
+void i915_capture_error_state(struct drm_device *dev, bool wedged,
+ const char *error_msg)
{
+ static bool warned;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_error_state *error;
unsigned long flags;
- int pipe;
-
- spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
- error = dev_priv->gpu_error.first_error;
- spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
- if (error)
- return;
/* Account for pipe specific data like PIPE*STAT */
error = kzalloc(sizeof(*error), GFP_ATOMIC);
@@ -924,52 +1158,10 @@ void i915_capture_error_state(struct drm_device *dev)
return;
}
- DRM_INFO("GPU crash dump saved to /sys/class/drm/card%d/error\n",
- dev->primary->index);
- DRM_INFO("GPU hangs can indicate a bug anywhere in the entire gfx stack, including userspace.\n");
- DRM_INFO("Please file a _new_ bug report on bugs.freedesktop.org against DRI -> DRM/Intel\n");
- DRM_INFO("drm/i915 developers can then reassign to the right component if it's not a kernel issue.\n");
- DRM_INFO("The gpu crash dump is required to analyze gpu hangs, so please always attach it.\n");
-
kref_init(&error->ref);
- error->eir = I915_READ(EIR);
- error->pgtbl_er = I915_READ(PGTBL_ER);
- if (HAS_HW_CONTEXTS(dev))
- error->ccid = I915_READ(CCID);
-
- if (HAS_PCH_SPLIT(dev))
- error->ier = I915_READ(DEIER) | I915_READ(GTIER);
- else if (IS_VALLEYVIEW(dev))
- error->ier = I915_READ(GTIER) | I915_READ(VLV_IER);
- else if (IS_GEN2(dev))
- error->ier = I915_READ16(IER);
- else
- error->ier = I915_READ(IER);
-
- if (INTEL_INFO(dev)->gen >= 6)
- error->derrmr = I915_READ(DERRMR);
-
- if (IS_VALLEYVIEW(dev))
- error->forcewake = I915_READ(FORCEWAKE_VLV);
- else if (INTEL_INFO(dev)->gen >= 7)
- error->forcewake = I915_READ(FORCEWAKE_MT);
- else if (INTEL_INFO(dev)->gen == 6)
- error->forcewake = I915_READ(FORCEWAKE);
-
- if (!HAS_PCH_SPLIT(dev))
- for_each_pipe(pipe)
- error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
-
- if (INTEL_INFO(dev)->gen >= 6) {
- error->error = I915_READ(ERROR_GEN6);
- error->done_reg = I915_READ(DONE_REG);
- }
-
- if (INTEL_INFO(dev)->gen == 7)
- error->err_int = I915_READ(GEN7_ERR_INT);
-
- i915_get_extra_instdone(dev, error->extra_instdone);
+ i915_capture_gen_state(dev_priv, error);
+ i915_capture_reg_state(dev_priv, error);
i915_gem_capture_buffers(dev_priv, error);
i915_gem_record_fences(dev, error);
i915_gem_record_rings(dev, error);
@@ -979,6 +1171,9 @@ void i915_capture_error_state(struct drm_device *dev)
error->overlay = intel_overlay_capture_error_state(dev);
error->display = intel_display_capture_error_state(dev);
+ i915_error_capture_msg(dev, error, wedged, error_msg);
+ DRM_INFO("%s\n", error->error_msg);
+
spin_lock_irqsave(&dev_priv->gpu_error.lock, flags);
if (dev_priv->gpu_error.first_error == NULL) {
dev_priv->gpu_error.first_error = error;
@@ -986,8 +1181,19 @@ void i915_capture_error_state(struct drm_device *dev)
}
spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
- if (error)
+ if (error) {
i915_error_state_free(&error->ref);
+ return;
+ }
+
+ if (!warned) {
+ DRM_INFO("GPU hangs can indicate a bug anywhere in the entire gfx stack, including userspace.\n");
+ DRM_INFO("Please file a _new_ bug report on bugs.freedesktop.org against DRI -> DRM/Intel\n");
+ DRM_INFO("drm/i915 developers can then reassign to the right component if it's not a kernel issue.\n");
+ DRM_INFO("The gpu crash dump is required to analyze gpu hangs, so please always attach it.\n");
+ DRM_INFO("GPU crash dump saved to /sys/class/drm/card%d/error\n", dev->primary->index);
+ warned = true;
+ }
}
void i915_error_state_get(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index d554169ac592..7753249b3a95 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -82,13 +82,13 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */
/* For display hotplug interrupt */
static void
-ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
{
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pc8.irqs_disabled) {
+ if (dev_priv->pm.irqs_disabled) {
WARN(1, "IRQs disabled\n");
- dev_priv->pc8.regsave.deimr &= ~mask;
+ dev_priv->pm.regsave.deimr &= ~mask;
return;
}
@@ -100,13 +100,13 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
}
static void
-ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
{
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pc8.irqs_disabled) {
+ if (dev_priv->pm.irqs_disabled) {
WARN(1, "IRQs disabled\n");
- dev_priv->pc8.regsave.deimr |= mask;
+ dev_priv->pm.regsave.deimr |= mask;
return;
}
@@ -129,10 +129,10 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
{
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pc8.irqs_disabled) {
+ if (dev_priv->pm.irqs_disabled) {
WARN(1, "IRQs disabled\n");
- dev_priv->pc8.regsave.gtimr &= ~interrupt_mask;
- dev_priv->pc8.regsave.gtimr |= (~enabled_irq_mask &
+ dev_priv->pm.regsave.gtimr &= ~interrupt_mask;
+ dev_priv->pm.regsave.gtimr |= (~enabled_irq_mask &
interrupt_mask);
return;
}
@@ -167,10 +167,10 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pc8.irqs_disabled) {
+ if (dev_priv->pm.irqs_disabled) {
WARN(1, "IRQs disabled\n");
- dev_priv->pc8.regsave.gen6_pmimr &= ~interrupt_mask;
- dev_priv->pc8.regsave.gen6_pmimr |= (~enabled_irq_mask &
+ dev_priv->pm.regsave.gen6_pmimr &= ~interrupt_mask;
+ dev_priv->pm.regsave.gen6_pmimr |= (~enabled_irq_mask &
interrupt_mask);
return;
}
@@ -232,6 +232,18 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev)
return true;
}
+static void i9xx_clear_fifo_underrun(struct drm_device *dev, enum pipe pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg = PIPESTAT(pipe);
+ u32 pipestat = I915_READ(reg) & 0x7fff0000;
+
+ assert_spin_locked(&dev_priv->irq_lock);
+
+ I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
+ POSTING_READ(reg);
+}
+
static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
enum pipe pipe, bool enable)
{
@@ -301,11 +313,11 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pc8.irqs_disabled &&
+ if (dev_priv->pm.irqs_disabled &&
(interrupt_mask & SDE_HOTPLUG_MASK_CPT)) {
WARN(1, "IRQs disabled\n");
- dev_priv->pc8.regsave.sdeimr &= ~interrupt_mask;
- dev_priv->pc8.regsave.sdeimr |= (~enabled_irq_mask &
+ dev_priv->pm.regsave.sdeimr &= ~interrupt_mask;
+ dev_priv->pm.regsave.sdeimr |= (~enabled_irq_mask &
interrupt_mask);
return;
}
@@ -375,16 +387,15 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
*
* Returns the previous state of underrun reporting.
*/
-bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
- enum pipe pipe, bool enable)
+bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe, bool enable)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- unsigned long flags;
bool ret;
- spin_lock_irqsave(&dev_priv->irq_lock, flags);
+ assert_spin_locked(&dev_priv->irq_lock);
ret = !intel_crtc->cpu_fifo_underrun_disabled;
@@ -393,7 +404,9 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
intel_crtc->cpu_fifo_underrun_disabled = !enable;
- if (IS_GEN5(dev) || IS_GEN6(dev))
+ if (enable && (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)))
+ i9xx_clear_fifo_underrun(dev, pipe);
+ else if (IS_GEN5(dev) || IS_GEN6(dev))
ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
else if (IS_GEN7(dev))
ivybridge_set_fifo_underrun_reporting(dev, pipe, enable);
@@ -401,10 +414,33 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
broadwell_set_fifo_underrun_reporting(dev, pipe, enable);
done:
+ return ret;
+}
+
+bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe, bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
+ ret = __intel_set_cpu_fifo_underrun_reporting(dev, pipe, enable);
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+
return ret;
}
+static bool __cpu_fifo_underrun_reporting_enabled(struct drm_device *dev,
+ enum pipe pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ return !intel_crtc->cpu_fifo_underrun_disabled;
+}
+
/**
* intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages
* @dev: drm device
@@ -458,45 +494,109 @@ done:
}
-void
-i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
+static void
+__i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
+ u32 enable_mask, u32 status_mask)
{
u32 reg = PIPESTAT(pipe);
- u32 pipestat = I915_READ(reg) & 0x7fff0000;
+ u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK;
assert_spin_locked(&dev_priv->irq_lock);
- if ((pipestat & mask) == mask)
+ if (WARN_ON_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+ status_mask & ~PIPESTAT_INT_STATUS_MASK))
return;
+ if ((pipestat & enable_mask) == enable_mask)
+ return;
+
+ dev_priv->pipestat_irq_mask[pipe] |= status_mask;
+
/* Enable the interrupt, clear any pending status */
- pipestat |= mask | (mask >> 16);
+ pipestat |= enable_mask | status_mask;
I915_WRITE(reg, pipestat);
POSTING_READ(reg);
}
-void
-i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
+static void
+__i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
+ u32 enable_mask, u32 status_mask)
{
u32 reg = PIPESTAT(pipe);
- u32 pipestat = I915_READ(reg) & 0x7fff0000;
+ u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK;
assert_spin_locked(&dev_priv->irq_lock);
- if ((pipestat & mask) == 0)
+ if (WARN_ON_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+ status_mask & ~PIPESTAT_INT_STATUS_MASK))
+ return;
+
+ if ((pipestat & enable_mask) == 0)
return;
- pipestat &= ~mask;
+ dev_priv->pipestat_irq_mask[pipe] &= ~status_mask;
+
+ pipestat &= ~enable_mask;
I915_WRITE(reg, pipestat);
POSTING_READ(reg);
}
+static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask)
+{
+ u32 enable_mask = status_mask << 16;
+
+ /*
+ * On pipe A we don't support the PSR interrupt yet, on pipe B the
+ * same bit MBZ.
+ */
+ if (WARN_ON_ONCE(status_mask & PIPE_A_PSR_STATUS_VLV))
+ return 0;
+
+ enable_mask &= ~(PIPE_FIFO_UNDERRUN_STATUS |
+ SPRITE0_FLIP_DONE_INT_EN_VLV |
+ SPRITE1_FLIP_DONE_INT_EN_VLV);
+ if (status_mask & SPRITE0_FLIP_DONE_INT_STATUS_VLV)
+ enable_mask |= SPRITE0_FLIP_DONE_INT_EN_VLV;
+ if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV)
+ enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV;
+
+ return enable_mask;
+}
+
+void
+i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
+ u32 status_mask)
+{
+ u32 enable_mask;
+
+ if (IS_VALLEYVIEW(dev_priv->dev))
+ enable_mask = vlv_get_pipestat_enable_mask(dev_priv->dev,
+ status_mask);
+ else
+ enable_mask = status_mask << 16;
+ __i915_enable_pipestat(dev_priv, pipe, enable_mask, status_mask);
+}
+
+void
+i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
+ u32 status_mask)
+{
+ u32 enable_mask;
+
+ if (IS_VALLEYVIEW(dev_priv->dev))
+ enable_mask = vlv_get_pipestat_enable_mask(dev_priv->dev,
+ status_mask);
+ else
+ enable_mask = status_mask << 16;
+ __i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask);
+}
+
/**
* i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
*/
static void i915_enable_asle_pipestat(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
if (!dev_priv->opregion.asle || !IS_MOBILE(dev))
@@ -504,10 +604,10 @@ static void i915_enable_asle_pipestat(struct drm_device *dev)
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_ENABLE);
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_STATUS);
if (INTEL_INFO(dev)->gen >= 4)
i915_enable_pipestat(dev_priv, PIPE_A,
- PIPE_LEGACY_BLC_EVENT_ENABLE);
+ PIPE_LEGACY_BLC_EVENT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -524,7 +624,7 @@ static void i915_enable_asle_pipestat(struct drm_device *dev)
static int
i915_pipe_enabled(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
/* Locking is horribly broken here, but whatever. */
@@ -548,7 +648,7 @@ static u32 i8xx_get_vblank_counter(struct drm_device *dev, int pipe)
*/
static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long high_frame;
unsigned long low_frame;
u32 high1, high2, low, pixel, vbl_start;
@@ -604,7 +704,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int reg = PIPE_FRMCOUNT_GM45(pipe);
if (!i915_pipe_enabled(dev, pipe)) {
@@ -859,8 +959,8 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
static void i915_hotplug_work_func(struct work_struct *work)
{
- drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
- hotplug_work);
+ struct drm_i915_private *dev_priv =
+ container_of(work, struct drm_i915_private, hotplug_work);
struct drm_device *dev = dev_priv->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_connector *intel_connector;
@@ -928,9 +1028,14 @@ static void i915_hotplug_work_func(struct work_struct *work)
drm_kms_helper_hotplug_event(dev);
}
+static void intel_hpd_irq_uninstall(struct drm_i915_private *dev_priv)
+{
+ del_timer_sync(&dev_priv->hotplug_reenable_timer);
+}
+
static void ironlake_rps_change_irq_handler(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 busy_up, busy_down, max_avg, min_avg;
u8 new_delay;
@@ -981,8 +1086,8 @@ static void notify_ring(struct drm_device *dev,
static void gen6_pm_rps_work(struct work_struct *work)
{
- drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
- rps.work);
+ struct drm_i915_private *dev_priv =
+ container_of(work, struct drm_i915_private, rps.work);
u32 pm_iir;
int new_delay, adj;
@@ -990,13 +1095,13 @@ static void gen6_pm_rps_work(struct work_struct *work)
pm_iir = dev_priv->rps.pm_iir;
dev_priv->rps.pm_iir = 0;
/* Make sure not to corrupt PMIMR state used by ringbuffer code */
- snb_enable_pm_irq(dev_priv, GEN6_PM_RPS_EVENTS);
+ snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
spin_unlock_irq(&dev_priv->irq_lock);
/* Make sure we didn't queue anything we're not going to process. */
- WARN_ON(pm_iir & ~GEN6_PM_RPS_EVENTS);
+ WARN_ON(pm_iir & ~dev_priv->pm_rps_events);
- if ((pm_iir & GEN6_PM_RPS_EVENTS) == 0)
+ if ((pm_iir & dev_priv->pm_rps_events) == 0)
return;
mutex_lock(&dev_priv->rps.hw_lock);
@@ -1007,36 +1112,38 @@ static void gen6_pm_rps_work(struct work_struct *work)
adj *= 2;
else
adj = 1;
- new_delay = dev_priv->rps.cur_delay + adj;
+ new_delay = dev_priv->rps.cur_freq + adj;
/*
* For better performance, jump directly
* to RPe if we're below it.
*/
- if (new_delay < dev_priv->rps.rpe_delay)
- new_delay = dev_priv->rps.rpe_delay;
+ if (new_delay < dev_priv->rps.efficient_freq)
+ new_delay = dev_priv->rps.efficient_freq;
} else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
- if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay)
- new_delay = dev_priv->rps.rpe_delay;
+ if (dev_priv->rps.cur_freq > dev_priv->rps.efficient_freq)
+ new_delay = dev_priv->rps.efficient_freq;
else
- new_delay = dev_priv->rps.min_delay;
+ new_delay = dev_priv->rps.min_freq_softlimit;
adj = 0;
} else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
if (adj < 0)
adj *= 2;
else
adj = -1;
- new_delay = dev_priv->rps.cur_delay + adj;
+ new_delay = dev_priv->rps.cur_freq + adj;
} else { /* unknown event */
- new_delay = dev_priv->rps.cur_delay;
+ new_delay = dev_priv->rps.cur_freq;
}
/* sysfs frequency interfaces may have snuck in while servicing the
* interrupt
*/
new_delay = clamp_t(int, new_delay,
- dev_priv->rps.min_delay, dev_priv->rps.max_delay);
- dev_priv->rps.last_adj = new_delay - dev_priv->rps.cur_delay;
+ dev_priv->rps.min_freq_softlimit,
+ dev_priv->rps.max_freq_softlimit);
+
+ dev_priv->rps.last_adj = new_delay - dev_priv->rps.cur_freq;
if (IS_VALLEYVIEW(dev_priv->dev))
valleyview_set_rps(dev_priv->dev, new_delay);
@@ -1058,8 +1165,8 @@ static void gen6_pm_rps_work(struct work_struct *work)
*/
static void ivybridge_parity_work(struct work_struct *work)
{
- drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
- l3_parity.error_work);
+ struct drm_i915_private *dev_priv =
+ container_of(work, struct drm_i915_private, l3_parity.error_work);
u32 error_status, row, bank, subbank;
char *parity_event[6];
uint32_t misccpctl;
@@ -1131,7 +1238,7 @@ out:
static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (!HAS_L3_DPF(dev))
return;
@@ -1177,8 +1284,8 @@ static void snb_gt_irq_handler(struct drm_device *dev,
if (gt_iir & (GT_BLT_CS_ERROR_INTERRUPT |
GT_BSD_CS_ERROR_INTERRUPT |
GT_RENDER_CS_MASTER_ERROR_INTERRUPT)) {
- DRM_ERROR("GT error interrupt 0x%08x\n", gt_iir);
- i915_handle_error(dev, false);
+ i915_handle_error(dev, false, "GT error interrupt 0x%08x",
+ gt_iir);
}
if (gt_iir & GT_PARITY_ERROR(dev))
@@ -1242,13 +1349,16 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
u32 hotplug_trigger,
const u32 *hpd)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int i;
bool storm_detected = false;
if (!hotplug_trigger)
return;
+ DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
+ hotplug_trigger);
+
spin_lock(&dev_priv->irq_lock);
for (i = 1; i < HPD_NUM_PINS; i++) {
@@ -1295,14 +1405,14 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
static void gmbus_irq_handler(struct drm_device *dev)
{
- struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
wake_up_all(&dev_priv->gmbus_wait_queue);
}
static void dp_aux_irq_handler(struct drm_device *dev)
{
- struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
wake_up_all(&dev_priv->gmbus_wait_queue);
}
@@ -1408,10 +1518,10 @@ static void i9xx_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
* the work queue. */
static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
{
- if (pm_iir & GEN6_PM_RPS_EVENTS) {
+ if (pm_iir & dev_priv->pm_rps_events) {
spin_lock(&dev_priv->irq_lock);
- dev_priv->rps.pm_iir |= pm_iir & GEN6_PM_RPS_EVENTS;
- snb_disable_pm_irq(dev_priv, pm_iir & GEN6_PM_RPS_EVENTS);
+ dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
+ snb_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
spin_unlock(&dev_priv->irq_lock);
queue_work(dev_priv->wq, &dev_priv->rps.work);
@@ -1422,23 +1532,89 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
notify_ring(dev_priv->dev, &dev_priv->ring[VECS]);
if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT) {
- DRM_ERROR("VEBOX CS error interrupt 0x%08x\n", pm_iir);
- i915_handle_error(dev_priv->dev, false);
+ i915_handle_error(dev_priv->dev, false,
+ "VEBOX CS error interrupt 0x%08x",
+ pm_iir);
}
}
}
+static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pipe_stats[I915_MAX_PIPES] = { };
+ int pipe;
+
+ spin_lock(&dev_priv->irq_lock);
+ for_each_pipe(pipe) {
+ int reg;
+ u32 mask, iir_bit = 0;
+
+ /*
+ * PIPESTAT bits get signalled even when the interrupt is
+ * disabled with the mask bits, and some of the status bits do
+ * not generate interrupts at all (like the underrun bit). Hence
+ * we need to be careful that we only handle what we want to
+ * handle.
+ */
+ mask = 0;
+ if (__cpu_fifo_underrun_reporting_enabled(dev, pipe))
+ mask |= PIPE_FIFO_UNDERRUN_STATUS;
+
+ switch (pipe) {
+ case PIPE_A:
+ iir_bit = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+ break;
+ case PIPE_B:
+ iir_bit = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+ break;
+ }
+ if (iir & iir_bit)
+ mask |= dev_priv->pipestat_irq_mask[pipe];
+
+ if (!mask)
+ continue;
+
+ reg = PIPESTAT(pipe);
+ mask |= PIPESTAT_INT_ENABLE_MASK;
+ pipe_stats[pipe] = I915_READ(reg) & mask;
+
+ /*
+ * Clear the PIPE*STAT regs before the IIR
+ */
+ if (pipe_stats[pipe] & (PIPE_FIFO_UNDERRUN_STATUS |
+ PIPESTAT_INT_STATUS_MASK))
+ I915_WRITE(reg, pipe_stats[pipe]);
+ }
+ spin_unlock(&dev_priv->irq_lock);
+
+ for_each_pipe(pipe) {
+ if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
+ drm_handle_vblank(dev, pipe);
+
+ if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
+ intel_prepare_page_flip(dev, pipe);
+ intel_finish_page_flip(dev, pipe);
+ }
+
+ if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+ i9xx_pipe_crc_irq_handler(dev, pipe);
+
+ if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS &&
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
+ DRM_ERROR("pipe %c underrun\n", pipe_name(pipe));
+ }
+
+ if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
+ gmbus_irq_handler(dev);
+}
+
static irqreturn_t valleyview_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *) arg;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 iir, gt_iir, pm_iir;
irqreturn_t ret = IRQ_NONE;
- unsigned long irqflags;
- int pipe;
- u32 pipe_stats[I915_MAX_PIPES];
-
- atomic_inc(&dev_priv->irq_received);
while (true) {
iir = I915_READ(VLV_IIR);
@@ -1452,44 +1628,13 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
snb_gt_irq_handler(dev, dev_priv, gt_iir);
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- for_each_pipe(pipe) {
- int reg = PIPESTAT(pipe);
- pipe_stats[pipe] = I915_READ(reg);
-
- /*
- * Clear the PIPE*STAT regs before the IIR
- */
- if (pipe_stats[pipe] & 0x8000ffff) {
- if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
- DRM_DEBUG_DRIVER("pipe %c underrun\n",
- pipe_name(pipe));
- I915_WRITE(reg, pipe_stats[pipe]);
- }
- }
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
-
- for_each_pipe(pipe) {
- if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
- drm_handle_vblank(dev, pipe);
-
- if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) {
- intel_prepare_page_flip(dev, pipe);
- intel_finish_page_flip(dev, pipe);
- }
-
- if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
- i9xx_pipe_crc_irq_handler(dev, pipe);
- }
+ valleyview_pipestat_irq_handler(dev, iir);
/* Consume port. Then clear IIR or we'll miss events */
if (iir & I915_DISPLAY_PORT_INTERRUPT) {
u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
- DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
- hotplug_status);
-
intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
@@ -1499,8 +1644,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
I915_READ(PORT_HOTPLUG_STAT);
}
- if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
- gmbus_irq_handler(dev);
if (pm_iir)
gen6_rps_irq_handler(dev_priv, pm_iir);
@@ -1516,7 +1659,7 @@ out:
static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
@@ -1559,12 +1702,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
if (pch_iir & SDE_TRANSA_FIFO_UNDER)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
false))
- DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+ DRM_ERROR("PCH transcoder A FIFO underrun\n");
if (pch_iir & SDE_TRANSB_FIFO_UNDER)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
false))
- DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+ DRM_ERROR("PCH transcoder B FIFO underrun\n");
}
static void ivb_err_int_handler(struct drm_device *dev)
@@ -1580,8 +1723,8 @@ static void ivb_err_int_handler(struct drm_device *dev)
if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) {
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
false))
- DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
- pipe_name(pipe));
+ DRM_ERROR("Pipe %c FIFO underrun\n",
+ pipe_name(pipe));
}
if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) {
@@ -1606,24 +1749,24 @@ static void cpt_serr_int_handler(struct drm_device *dev)
if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
false))
- DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+ DRM_ERROR("PCH transcoder A FIFO underrun\n");
if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
false))
- DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+ DRM_ERROR("PCH transcoder B FIFO underrun\n");
if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C,
false))
- DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n");
+ DRM_ERROR("PCH transcoder C FIFO underrun\n");
I915_WRITE(SERR_INT, serr_int);
}
static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
@@ -1678,8 +1821,8 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
- DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
- pipe_name(pipe));
+ DRM_ERROR("Pipe %c FIFO underrun\n",
+ pipe_name(pipe));
if (de_iir & DE_PIPE_CRC_DONE(pipe))
i9xx_pipe_crc_irq_handler(dev, pipe);
@@ -1711,7 +1854,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- enum pipe i;
+ enum pipe pipe;
if (de_iir & DE_ERR_INT_IVB)
ivb_err_int_handler(dev);
@@ -1722,14 +1865,14 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
if (de_iir & DE_GSE_IVB)
intel_opregion_asle_intr(dev);
- for_each_pipe(i) {
- if (de_iir & (DE_PIPE_VBLANK_IVB(i)))
- drm_handle_vblank(dev, i);
+ for_each_pipe(pipe) {
+ if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
+ drm_handle_vblank(dev, pipe);
/* plane/pipes map 1:1 on ilk+ */
- if (de_iir & DE_PLANE_FLIP_DONE_IVB(i)) {
- intel_prepare_page_flip(dev, i);
- intel_finish_page_flip_plane(dev, i);
+ if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
+ intel_prepare_page_flip(dev, pipe);
+ intel_finish_page_flip_plane(dev, pipe);
}
}
@@ -1747,12 +1890,10 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
static irqreturn_t ironlake_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *) arg;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 de_iir, gt_iir, de_ier, sde_ier = 0;
irqreturn_t ret = IRQ_NONE;
- atomic_inc(&dev_priv->irq_received);
-
/* We get interrupts on unclaimed registers, so check for this before we
* do any I915_{READ,WRITE}. */
intel_uncore_check_errors(dev);
@@ -1821,8 +1962,6 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
uint32_t tmp = 0;
enum pipe pipe;
- atomic_inc(&dev_priv->irq_received);
-
master_ctl = I915_READ(GEN8_MASTER_IRQ);
master_ctl &= ~GEN8_MASTER_IRQ_CONTROL;
if (!master_ctl)
@@ -1884,8 +2023,8 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) {
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
false))
- DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
- pipe_name(pipe));
+ DRM_ERROR("Pipe %c FIFO underrun\n",
+ pipe_name(pipe));
}
if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) {
@@ -1962,8 +2101,8 @@ static void i915_error_work_func(struct work_struct *work)
{
struct i915_gpu_error *error = container_of(work, struct i915_gpu_error,
work);
- drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t,
- gpu_error);
+ struct drm_i915_private *dev_priv =
+ container_of(error, struct drm_i915_private, gpu_error);
struct drm_device *dev = dev_priv->dev;
char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
@@ -2127,11 +2266,18 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
* so userspace knows something bad happened (should trigger collection
* of a ring dump etc.).
*/
-void i915_handle_error(struct drm_device *dev, bool wedged)
+void i915_handle_error(struct drm_device *dev, bool wedged,
+ const char *fmt, ...)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ va_list args;
+ char error_msg[80];
- i915_capture_error_state(dev);
+ va_start(args, fmt);
+ vscnprintf(error_msg, sizeof(error_msg), fmt, args);
+ va_end(args);
+
+ i915_capture_error_state(dev, wedged, error_msg);
i915_report_and_clear_eir(dev);
if (wedged) {
@@ -2165,7 +2311,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_i915_gem_object *obj;
@@ -2197,8 +2343,8 @@ static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, in
} else {
int dspaddr = DSPADDR(intel_crtc->plane);
stall_detected = I915_READ(dspaddr) == (i915_gem_obj_ggtt_offset(obj) +
- crtc->y * crtc->fb->pitches[0] +
- crtc->x * crtc->fb->bits_per_pixel/8);
+ crtc->y * crtc->primary->fb->pitches[0] +
+ crtc->x * crtc->primary->fb->bits_per_pixel/8);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
@@ -2214,7 +2360,7 @@ static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, in
*/
static int i915_enable_vblank(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
if (!i915_pipe_enabled(dev, pipe))
@@ -2223,13 +2369,13 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe)
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
if (INTEL_INFO(dev)->gen >= 4)
i915_enable_pipestat(dev_priv, pipe,
- PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ PIPE_START_VBLANK_INTERRUPT_STATUS);
else
i915_enable_pipestat(dev_priv, pipe,
- PIPE_VBLANK_INTERRUPT_ENABLE);
+ PIPE_VBLANK_INTERRUPT_STATUS);
/* maintain vblank delivery even in deep C-states */
- if (dev_priv->info->gen == 3)
+ if (INTEL_INFO(dev)->gen == 3)
I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_AGPBUSY_DIS));
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
@@ -2238,7 +2384,7 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe)
static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
DE_PIPE_VBLANK(pipe);
@@ -2255,22 +2401,15 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
- u32 imr;
if (!i915_pipe_enabled(dev, pipe))
return -EINVAL;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- imr = I915_READ(VLV_IMR);
- if (pipe == PIPE_A)
- imr &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
- else
- imr &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
- I915_WRITE(VLV_IMR, imr);
i915_enable_pipestat(dev_priv, pipe,
- PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ PIPE_START_VBLANK_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
return 0;
@@ -2297,22 +2436,22 @@ static int gen8_enable_vblank(struct drm_device *dev, int pipe)
*/
static void i915_disable_vblank(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- if (dev_priv->info->gen == 3)
+ if (INTEL_INFO(dev)->gen == 3)
I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_DIS));
i915_disable_pipestat(dev_priv, pipe,
- PIPE_VBLANK_INTERRUPT_ENABLE |
- PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ PIPE_VBLANK_INTERRUPT_STATUS |
+ PIPE_START_VBLANK_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
DE_PIPE_VBLANK(pipe);
@@ -2324,19 +2463,12 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
- u32 imr;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
i915_disable_pipestat(dev_priv, pipe,
- PIPE_START_VBLANK_INTERRUPT_ENABLE);
- imr = I915_READ(VLV_IMR);
- if (pipe == PIPE_A)
- imr |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
- else
- imr |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
- I915_WRITE(VLV_IMR, imr);
+ PIPE_START_VBLANK_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -2373,29 +2505,43 @@ static struct intel_ring_buffer *
semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
- u32 cmd, ipehr, acthd, acthd_min;
+ u32 cmd, ipehr, head;
+ int i;
ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
if ((ipehr & ~(0x3 << 16)) !=
(MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER))
return NULL;
- /* ACTHD is likely pointing to the dword after the actual command,
- * so scan backwards until we find the MBOX.
+ /*
+ * HEAD is likely pointing to the dword after the actual command,
+ * so scan backwards until we find the MBOX. But limit it to just 3
+ * dwords. Note that we don't care about ACTHD here since that might
+ * point at at batch, and semaphores are always emitted into the
+ * ringbuffer itself.
*/
- acthd = intel_ring_get_active_head(ring) & HEAD_ADDR;
- acthd_min = max((int)acthd - 3 * 4, 0);
- do {
- cmd = ioread32(ring->virtual_start + acthd);
+ head = I915_READ_HEAD(ring) & HEAD_ADDR;
+
+ for (i = 4; i; --i) {
+ /*
+ * Be paranoid and presume the hw has gone off into the wild -
+ * our ring is smaller than what the hardware (and hence
+ * HEAD_ADDR) allows. Also handles wrap-around.
+ */
+ head &= ring->size - 1;
+
+ /* This here seems to blow up */
+ cmd = ioread32(ring->virtual_start + head);
if (cmd == ipehr)
break;
- acthd -= 4;
- if (acthd < acthd_min)
- return NULL;
- } while (1);
+ head -= 4;
+ }
- *seqno = ioread32(ring->virtual_start+acthd+4)+1;
+ if (!i)
+ return NULL;
+
+ *seqno = ioread32(ring->virtual_start + head + 4) + 1;
return &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3];
}
@@ -2429,7 +2575,7 @@ static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv)
}
static enum intel_ring_hangcheck_action
-ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
+ring_stuck(struct intel_ring_buffer *ring, u64 acthd)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2448,9 +2594,9 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
*/
tmp = I915_READ_CTL(ring);
if (tmp & RING_WAIT) {
- DRM_ERROR("Kicking stuck wait on %s\n",
- ring->name);
- i915_handle_error(dev, false);
+ i915_handle_error(dev, false,
+ "Kicking stuck wait on %s",
+ ring->name);
I915_WRITE_CTL(ring, tmp);
return HANGCHECK_KICK;
}
@@ -2460,9 +2606,9 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
default:
return HANGCHECK_HUNG;
case 1:
- DRM_ERROR("Kicking stuck semaphore on %s\n",
- ring->name);
- i915_handle_error(dev, false);
+ i915_handle_error(dev, false,
+ "Kicking stuck semaphore on %s",
+ ring->name);
I915_WRITE_CTL(ring, tmp);
return HANGCHECK_KICK;
case 0:
@@ -2484,7 +2630,7 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
static void i915_hangcheck_elapsed(unsigned long data)
{
struct drm_device *dev = (struct drm_device *)data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring;
int i;
int busy_count = 0, rings_hung = 0;
@@ -2492,13 +2638,13 @@ static void i915_hangcheck_elapsed(unsigned long data)
#define BUSY 1
#define KICK 5
#define HUNG 20
-#define FIRE 30
- if (!i915_enable_hangcheck)
+ if (!i915.enable_hangcheck)
return;
for_each_ring(ring, dev_priv, i) {
- u32 seqno, acthd;
+ u64 acthd;
+ u32 seqno;
bool busy = true;
semaphore_clear_deadlocks(dev_priv);
@@ -2576,7 +2722,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
}
for_each_ring(ring, dev_priv, i) {
- if (ring->hangcheck.score > FIRE) {
+ if (ring->hangcheck.score >= HANGCHECK_SCORE_RING_HUNG) {
DRM_INFO("%s on %s\n",
stuck[i] ? "stuck" : "no progress",
ring->name);
@@ -2585,7 +2731,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
}
if (rings_hung)
- return i915_handle_error(dev, true);
+ return i915_handle_error(dev, true, "Ring hung");
if (busy_count)
/* Reset timer case chip hangs without another request
@@ -2596,7 +2742,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
void i915_queue_hangcheck(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- if (!i915_enable_hangcheck)
+ if (!i915.enable_hangcheck)
return;
mod_timer(&dev_priv->gpu_error.hangcheck_timer,
@@ -2643,9 +2789,7 @@ static void gen5_gt_irq_preinstall(struct drm_device *dev)
*/
static void ironlake_irq_preinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-
- atomic_set(&dev_priv->irq_received, 0);
+ struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(HWSTAM, 0xeffe);
@@ -2660,11 +2804,9 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
static void valleyview_irq_preinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- atomic_set(&dev_priv->irq_received, 0);
-
/* VLV magic */
I915_WRITE(VLV_IMR, 0);
I915_WRITE(RING_IMR(RENDER_RING_BASE), 0);
@@ -2694,8 +2836,6 @@ static void gen8_irq_preinstall(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- atomic_set(&dev_priv->irq_received, 0);
-
I915_WRITE(GEN8_MASTER_IRQ, 0);
POSTING_READ(GEN8_MASTER_IRQ);
@@ -2740,7 +2880,7 @@ static void gen8_irq_preinstall(struct drm_device *dev)
static void ibx_hpd_irq_setup(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *intel_encoder;
u32 hotplug_irqs, hotplug, enabled_irqs = 0;
@@ -2775,7 +2915,7 @@ static void ibx_hpd_irq_setup(struct drm_device *dev)
static void ibx_irq_postinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 mask;
if (HAS_PCH_NOP(dev))
@@ -2821,7 +2961,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
POSTING_READ(GTIER);
if (INTEL_INFO(dev)->gen >= 6) {
- pm_irqs |= GEN6_PM_RPS_EVENTS;
+ pm_irqs |= dev_priv->pm_rps_events;
if (HAS_VEBOX(dev))
pm_irqs |= PM_VEBOX_USER_INTERRUPT;
@@ -2837,7 +2977,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
static int ironlake_irq_postinstall(struct drm_device *dev)
{
unsigned long irqflags;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 display_mask, extra_mask;
if (INTEL_INFO(dev)->gen >= 7) {
@@ -2885,44 +3025,113 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
return 0;
}
+static void valleyview_display_irqs_install(struct drm_i915_private *dev_priv)
+{
+ u32 pipestat_mask;
+ u32 iir_mask;
+
+ pipestat_mask = PIPESTAT_INT_STATUS_MASK |
+ PIPE_FIFO_UNDERRUN_STATUS;
+
+ I915_WRITE(PIPESTAT(PIPE_A), pipestat_mask);
+ I915_WRITE(PIPESTAT(PIPE_B), pipestat_mask);
+ POSTING_READ(PIPESTAT(PIPE_A));
+
+ pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
+ PIPE_CRC_DONE_INTERRUPT_STATUS;
+
+ i915_enable_pipestat(dev_priv, PIPE_A, pipestat_mask |
+ PIPE_GMBUS_INTERRUPT_STATUS);
+ i915_enable_pipestat(dev_priv, PIPE_B, pipestat_mask);
+
+ iir_mask = I915_DISPLAY_PORT_INTERRUPT |
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+ dev_priv->irq_mask &= ~iir_mask;
+
+ I915_WRITE(VLV_IIR, iir_mask);
+ I915_WRITE(VLV_IIR, iir_mask);
+ I915_WRITE(VLV_IMR, dev_priv->irq_mask);
+ I915_WRITE(VLV_IER, ~dev_priv->irq_mask);
+ POSTING_READ(VLV_IER);
+}
+
+static void valleyview_display_irqs_uninstall(struct drm_i915_private *dev_priv)
+{
+ u32 pipestat_mask;
+ u32 iir_mask;
+
+ iir_mask = I915_DISPLAY_PORT_INTERRUPT |
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+
+ dev_priv->irq_mask |= iir_mask;
+ I915_WRITE(VLV_IER, ~dev_priv->irq_mask);
+ I915_WRITE(VLV_IMR, dev_priv->irq_mask);
+ I915_WRITE(VLV_IIR, iir_mask);
+ I915_WRITE(VLV_IIR, iir_mask);
+ POSTING_READ(VLV_IIR);
+
+ pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
+ PIPE_CRC_DONE_INTERRUPT_STATUS;
+
+ i915_disable_pipestat(dev_priv, PIPE_A, pipestat_mask |
+ PIPE_GMBUS_INTERRUPT_STATUS);
+ i915_disable_pipestat(dev_priv, PIPE_B, pipestat_mask);
+
+ pipestat_mask = PIPESTAT_INT_STATUS_MASK |
+ PIPE_FIFO_UNDERRUN_STATUS;
+ I915_WRITE(PIPESTAT(PIPE_A), pipestat_mask);
+ I915_WRITE(PIPESTAT(PIPE_B), pipestat_mask);
+ POSTING_READ(PIPESTAT(PIPE_A));
+}
+
+void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv)
+{
+ assert_spin_locked(&dev_priv->irq_lock);
+
+ if (dev_priv->display_irqs_enabled)
+ return;
+
+ dev_priv->display_irqs_enabled = true;
+
+ if (dev_priv->dev->irq_enabled)
+ valleyview_display_irqs_install(dev_priv);
+}
+
+void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv)
+{
+ assert_spin_locked(&dev_priv->irq_lock);
+
+ if (!dev_priv->display_irqs_enabled)
+ return;
+
+ dev_priv->display_irqs_enabled = false;
+
+ if (dev_priv->dev->irq_enabled)
+ valleyview_display_irqs_uninstall(dev_priv);
+}
+
static int valleyview_irq_postinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- u32 enable_mask;
- u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV |
- PIPE_CRC_DONE_ENABLE;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
- enable_mask = I915_DISPLAY_PORT_INTERRUPT;
- enable_mask |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
-
- /*
- *Leave vblank interrupts masked initially. enable/disable will
- * toggle them based on usage.
- */
- dev_priv->irq_mask = (~enable_mask) |
- I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
- I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+ dev_priv->irq_mask = ~0;
I915_WRITE(PORT_HOTPLUG_EN, 0);
POSTING_READ(PORT_HOTPLUG_EN);
I915_WRITE(VLV_IMR, dev_priv->irq_mask);
- I915_WRITE(VLV_IER, enable_mask);
+ I915_WRITE(VLV_IER, ~dev_priv->irq_mask);
I915_WRITE(VLV_IIR, 0xffffffff);
- I915_WRITE(PIPESTAT(0), 0xffff);
- I915_WRITE(PIPESTAT(1), 0xffff);
POSTING_READ(VLV_IER);
/* Interrupt setup is already guaranteed to be single-threaded, this is
* just to make the assert_spin_locked check happy. */
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- i915_enable_pipestat(dev_priv, PIPE_A, pipestat_enable);
- i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
- i915_enable_pipestat(dev_priv, PIPE_B, pipestat_enable);
+ if (dev_priv->display_irqs_enabled)
+ valleyview_display_irqs_install(dev_priv);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
I915_WRITE(VLV_IIR, 0xffffffff);
@@ -3018,8 +3227,6 @@ static void gen8_irq_uninstall(struct drm_device *dev)
if (!dev_priv)
return;
- atomic_set(&dev_priv->irq_received, 0);
-
I915_WRITE(GEN8_MASTER_IRQ, 0);
#define GEN8_IRQ_FINI_NDX(type, which) do { \
@@ -3054,13 +3261,14 @@ static void gen8_irq_uninstall(struct drm_device *dev)
static void valleyview_irq_uninstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long irqflags;
int pipe;
if (!dev_priv)
return;
- del_timer_sync(&dev_priv->hotplug_reenable_timer);
+ intel_hpd_irq_uninstall(dev_priv);
for_each_pipe(pipe)
I915_WRITE(PIPESTAT(pipe), 0xffff);
@@ -3068,8 +3276,14 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
I915_WRITE(HWSTAM, 0xffffffff);
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
- for_each_pipe(pipe)
- I915_WRITE(PIPESTAT(pipe), 0xffff);
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ if (dev_priv->display_irqs_enabled)
+ valleyview_display_irqs_uninstall(dev_priv);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+ dev_priv->irq_mask = 0;
+
I915_WRITE(VLV_IIR, 0xffffffff);
I915_WRITE(VLV_IMR, 0xffffffff);
I915_WRITE(VLV_IER, 0x0);
@@ -3078,12 +3292,12 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
static void ironlake_irq_uninstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (!dev_priv)
return;
- del_timer_sync(&dev_priv->hotplug_reenable_timer);
+ intel_hpd_irq_uninstall(dev_priv);
I915_WRITE(HWSTAM, 0xffffffff);
@@ -3109,11 +3323,9 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
static void i8xx_irq_preinstall(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- atomic_set(&dev_priv->irq_received, 0);
-
for_each_pipe(pipe)
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE16(IMR, 0xffff);
@@ -3123,7 +3335,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev)
static int i8xx_irq_postinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
I915_WRITE16(EMR,
@@ -3148,8 +3360,8 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
/* Interrupt setup is already guaranteed to be single-threaded, this is
* just to make the assert_spin_locked check happy. */
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
- i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
return 0;
@@ -3161,7 +3373,7 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
static bool i8xx_handle_vblank(struct drm_device *dev,
int plane, int pipe, u32 iir)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
if (!drm_handle_vblank(dev, pipe))
@@ -3189,7 +3401,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
static irqreturn_t i8xx_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *) arg;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u16 iir, new_iir;
u32 pipe_stats[2];
unsigned long irqflags;
@@ -3198,8 +3410,6 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
- atomic_inc(&dev_priv->irq_received);
-
iir = I915_READ16(IIR);
if (iir == 0)
return IRQ_NONE;
@@ -3212,7 +3422,9 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
*/
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
- i915_handle_error(dev, false);
+ i915_handle_error(dev, false,
+ "Command parser error, iir 0x%08x",
+ iir);
for_each_pipe(pipe) {
int reg = PIPESTAT(pipe);
@@ -3221,12 +3433,8 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
/*
* Clear the PIPE*STAT regs before the IIR
*/
- if (pipe_stats[pipe] & 0x8000ffff) {
- if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
- DRM_DEBUG_DRIVER("pipe %c underrun\n",
- pipe_name(pipe));
+ if (pipe_stats[pipe] & 0x8000ffff)
I915_WRITE(reg, pipe_stats[pipe]);
- }
}
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
@@ -3249,6 +3457,10 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
i9xx_pipe_crc_irq_handler(dev, pipe);
+
+ if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS &&
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
+ DRM_ERROR("pipe %c underrun\n", pipe_name(pipe));
}
iir = new_iir;
@@ -3259,7 +3471,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
static void i8xx_irq_uninstall(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
for_each_pipe(pipe) {
@@ -3274,11 +3486,9 @@ static void i8xx_irq_uninstall(struct drm_device * dev)
static void i915_irq_preinstall(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- atomic_set(&dev_priv->irq_received, 0);
-
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -3294,7 +3504,7 @@ static void i915_irq_preinstall(struct drm_device * dev)
static int i915_irq_postinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 enable_mask;
unsigned long irqflags;
@@ -3335,8 +3545,8 @@ static int i915_irq_postinstall(struct drm_device *dev)
/* Interrupt setup is already guaranteed to be single-threaded, this is
* just to make the assert_spin_locked check happy. */
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
- i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
return 0;
@@ -3348,7 +3558,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
static bool i915_handle_vblank(struct drm_device *dev,
int plane, int pipe, u32 iir)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
if (!drm_handle_vblank(dev, pipe))
@@ -3376,7 +3586,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
static irqreturn_t i915_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *) arg;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 iir, new_iir, pipe_stats[I915_MAX_PIPES];
unsigned long irqflags;
u32 flip_mask =
@@ -3384,8 +3594,6 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
int pipe, ret = IRQ_NONE;
- atomic_inc(&dev_priv->irq_received);
-
iir = I915_READ(IIR);
do {
bool irq_received = (iir & ~flip_mask) != 0;
@@ -3398,7 +3606,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
*/
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
- i915_handle_error(dev, false);
+ i915_handle_error(dev, false,
+ "Command parser error, iir 0x%08x",
+ iir);
for_each_pipe(pipe) {
int reg = PIPESTAT(pipe);
@@ -3406,9 +3616,6 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
/* Clear the PIPE*STAT regs before the IIR */
if (pipe_stats[pipe] & 0x8000ffff) {
- if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
- DRM_DEBUG_DRIVER("pipe %c underrun\n",
- pipe_name(pipe));
I915_WRITE(reg, pipe_stats[pipe]);
irq_received = true;
}
@@ -3424,9 +3631,6 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
- DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
- hotplug_status);
-
intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
@@ -3453,6 +3657,10 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
i9xx_pipe_crc_irq_handler(dev, pipe);
+
+ if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS &&
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
+ DRM_ERROR("pipe %c underrun\n", pipe_name(pipe));
}
if (blc_event || (iir & I915_ASLE_INTERRUPT))
@@ -3484,10 +3692,10 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
static void i915_irq_uninstall(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- del_timer_sync(&dev_priv->hotplug_reenable_timer);
+ intel_hpd_irq_uninstall(dev_priv);
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
@@ -3508,11 +3716,9 @@ static void i915_irq_uninstall(struct drm_device * dev)
static void i965_irq_preinstall(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- atomic_set(&dev_priv->irq_received, 0);
-
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -3526,7 +3732,7 @@ static void i965_irq_preinstall(struct drm_device * dev)
static int i965_irq_postinstall(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 enable_mask;
u32 error_mask;
unsigned long irqflags;
@@ -3551,9 +3757,9 @@ static int i965_irq_postinstall(struct drm_device *dev)
/* Interrupt setup is already guaranteed to be single-threaded, this is
* just to make the assert_spin_locked check happy. */
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
- i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
- i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
/*
@@ -3585,7 +3791,7 @@ static int i965_irq_postinstall(struct drm_device *dev)
static void i915_hpd_irq_setup(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *intel_encoder;
u32 hotplug_en;
@@ -3617,25 +3823,21 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
static irqreturn_t i965_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *) arg;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 iir, new_iir;
u32 pipe_stats[I915_MAX_PIPES];
unsigned long irqflags;
- int irq_received;
int ret = IRQ_NONE, pipe;
u32 flip_mask =
I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
- atomic_inc(&dev_priv->irq_received);
-
iir = I915_READ(IIR);
for (;;) {
+ bool irq_received = (iir & ~flip_mask) != 0;
bool blc_event = false;
- irq_received = (iir & ~flip_mask) != 0;
-
/* Can't rely on pipestat interrupt bit in iir as it might
* have been cleared after the pipestat interrupt was received.
* It doesn't set the bit in iir again, but it still produces
@@ -3643,7 +3845,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
*/
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
- i915_handle_error(dev, false);
+ i915_handle_error(dev, false,
+ "Command parser error, iir 0x%08x",
+ iir);
for_each_pipe(pipe) {
int reg = PIPESTAT(pipe);
@@ -3653,11 +3857,8 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
* Clear the PIPE*STAT regs before the IIR
*/
if (pipe_stats[pipe] & 0x8000ffff) {
- if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
- DRM_DEBUG_DRIVER("pipe %c underrun\n",
- pipe_name(pipe));
I915_WRITE(reg, pipe_stats[pipe]);
- irq_received = 1;
+ irq_received = true;
}
}
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
@@ -3674,9 +3875,6 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
HOTPLUG_INT_STATUS_G4X :
HOTPLUG_INT_STATUS_I915);
- DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
- hotplug_status);
-
intel_hpd_irq_handler(dev, hotplug_trigger,
IS_G4X(dev) ? hpd_status_g4x : hpd_status_i915);
@@ -3706,8 +3904,11 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
i9xx_pipe_crc_irq_handler(dev, pipe);
- }
+ if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS &&
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
+ DRM_ERROR("pipe %c underrun\n", pipe_name(pipe));
+ }
if (blc_event || (iir & I915_ASLE_INTERRUPT))
intel_opregion_asle_intr(dev);
@@ -3740,13 +3941,13 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
static void i965_irq_uninstall(struct drm_device * dev)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
if (!dev_priv)
return;
- del_timer_sync(&dev_priv->hotplug_reenable_timer);
+ intel_hpd_irq_uninstall(dev_priv);
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -3763,9 +3964,9 @@ static void i965_irq_uninstall(struct drm_device * dev)
I915_WRITE(IIR, I915_READ(IIR));
}
-static void i915_reenable_hotplug_timer_func(unsigned long data)
+static void intel_hpd_irq_reenable(unsigned long data)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *)data;
+ struct drm_i915_private *dev_priv = (struct drm_i915_private *)data;
struct drm_device *dev = dev_priv->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
unsigned long irqflags;
@@ -3807,10 +4008,13 @@ void intel_irq_init(struct drm_device *dev)
INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
+ /* Let's track the enabled rps events */
+ dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS;
+
setup_timer(&dev_priv->gpu_error.hangcheck_timer,
i915_hangcheck_elapsed,
(unsigned long) dev);
- setup_timer(&dev_priv->hotplug_reenable_timer, i915_reenable_hotplug_timer_func,
+ setup_timer(&dev_priv->hotplug_reenable_timer, intel_hpd_irq_reenable,
(unsigned long) dev_priv);
pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
@@ -3906,32 +4110,32 @@ void intel_hpd_init(struct drm_device *dev)
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
-/* Disable interrupts so we can allow Package C8+. */
-void hsw_pc8_disable_interrupts(struct drm_device *dev)
+/* Disable interrupts so we can allow runtime PM. */
+void hsw_runtime_pm_disable_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- dev_priv->pc8.regsave.deimr = I915_READ(DEIMR);
- dev_priv->pc8.regsave.sdeimr = I915_READ(SDEIMR);
- dev_priv->pc8.regsave.gtimr = I915_READ(GTIMR);
- dev_priv->pc8.regsave.gtier = I915_READ(GTIER);
- dev_priv->pc8.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR);
+ dev_priv->pm.regsave.deimr = I915_READ(DEIMR);
+ dev_priv->pm.regsave.sdeimr = I915_READ(SDEIMR);
+ dev_priv->pm.regsave.gtimr = I915_READ(GTIMR);
+ dev_priv->pm.regsave.gtier = I915_READ(GTIER);
+ dev_priv->pm.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR);
ironlake_disable_display_irq(dev_priv, 0xffffffff);
ibx_disable_display_interrupt(dev_priv, 0xffffffff);
ilk_disable_gt_irq(dev_priv, 0xffffffff);
snb_disable_pm_irq(dev_priv, 0xffffffff);
- dev_priv->pc8.irqs_disabled = true;
+ dev_priv->pm.irqs_disabled = true;
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
-/* Restore interrupts so we can recover from Package C8+. */
-void hsw_pc8_restore_interrupts(struct drm_device *dev)
+/* Restore interrupts so we can recover from runtime PM. */
+void hsw_runtime_pm_restore_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
@@ -3951,13 +4155,13 @@ void hsw_pc8_restore_interrupts(struct drm_device *dev)
val = I915_READ(GEN6_PMIMR);
WARN(val != 0xffffffff, "GEN6_PMIMR is 0x%08x\n", val);
- dev_priv->pc8.irqs_disabled = false;
+ dev_priv->pm.irqs_disabled = false;
- ironlake_enable_display_irq(dev_priv, ~dev_priv->pc8.regsave.deimr);
- ibx_enable_display_interrupt(dev_priv, ~dev_priv->pc8.regsave.sdeimr);
- ilk_enable_gt_irq(dev_priv, ~dev_priv->pc8.regsave.gtimr);
- snb_enable_pm_irq(dev_priv, ~dev_priv->pc8.regsave.gen6_pmimr);
- I915_WRITE(GTIER, dev_priv->pc8.regsave.gtier);
+ ironlake_enable_display_irq(dev_priv, ~dev_priv->pm.regsave.deimr);
+ ibx_enable_display_interrupt(dev_priv, ~dev_priv->pm.regsave.sdeimr);
+ ilk_enable_gt_irq(dev_priv, ~dev_priv->pm.regsave.gtimr);
+ snb_enable_pm_irq(dev_priv, ~dev_priv->pm.regsave.gen6_pmimr);
+ I915_WRITE(GTIER, dev_priv->pm.regsave.gtier);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
new file mode 100644
index 000000000000..d1d7980f0e01
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright © 2014 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, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "i915_drv.h"
+
+struct i915_params i915 __read_mostly = {
+ .modeset = -1,
+ .panel_ignore_lid = 1,
+ .powersave = 1,
+ .semaphores = -1,
+ .lvds_downclock = 0,
+ .lvds_channel_mode = 0,
+ .panel_use_ssc = -1,
+ .vbt_sdvo_panel_type = -1,
+ .enable_rc6 = -1,
+ .enable_fbc = -1,
+ .enable_hangcheck = true,
+ .enable_ppgtt = -1,
+ .enable_psr = 0,
+ .preliminary_hw_support = IS_ENABLED(CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT),
+ .disable_power_well = 1,
+ .enable_ips = 1,
+ .fastboot = 0,
+ .prefault_disable = 0,
+ .reset = true,
+ .invert_brightness = 0,
+ .disable_display = 0,
+ .enable_cmd_parser = 0,
+};
+
+module_param_named(modeset, i915.modeset, int, 0400);
+MODULE_PARM_DESC(modeset,
+ "Use kernel modesetting [KMS] (0=DRM_I915_KMS from .config, "
+ "1=on, -1=force vga console preference [default])");
+
+module_param_named(panel_ignore_lid, i915.panel_ignore_lid, int, 0600);
+MODULE_PARM_DESC(panel_ignore_lid,
+ "Override lid status (0=autodetect, 1=autodetect disabled [default], "
+ "-1=force lid closed, -2=force lid open)");
+
+module_param_named(powersave, i915.powersave, int, 0600);
+MODULE_PARM_DESC(powersave,
+ "Enable powersavings, fbc, downclocking, etc. (default: true)");
+
+module_param_named(semaphores, i915.semaphores, int, 0400);
+MODULE_PARM_DESC(semaphores,
+ "Use semaphores for inter-ring sync "
+ "(default: -1 (use per-chip defaults))");
+
+module_param_named(enable_rc6, i915.enable_rc6, int, 0400);
+MODULE_PARM_DESC(enable_rc6,
+ "Enable power-saving render C-state 6. "
+ "Different stages can be selected via bitmask values "
+ "(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). "
+ "For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. "
+ "default: -1 (use per-chip default)");
+
+module_param_named(enable_fbc, i915.enable_fbc, int, 0600);
+MODULE_PARM_DESC(enable_fbc,
+ "Enable frame buffer compression for power savings "
+ "(default: -1 (use per-chip default))");
+
+module_param_named(lvds_downclock, i915.lvds_downclock, int, 0400);
+MODULE_PARM_DESC(lvds_downclock,
+ "Use panel (LVDS/eDP) downclocking for power savings "
+ "(default: false)");
+
+module_param_named(lvds_channel_mode, i915.lvds_channel_mode, int, 0600);
+MODULE_PARM_DESC(lvds_channel_mode,
+ "Specify LVDS channel mode "
+ "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)");
+
+module_param_named(lvds_use_ssc, i915.panel_use_ssc, int, 0600);
+MODULE_PARM_DESC(lvds_use_ssc,
+ "Use Spread Spectrum Clock with panels [LVDS/eDP] "
+ "(default: auto from VBT)");
+
+module_param_named(vbt_sdvo_panel_type, i915.vbt_sdvo_panel_type, int, 0600);
+MODULE_PARM_DESC(vbt_sdvo_panel_type,
+ "Override/Ignore selection of SDVO panel mode in the VBT "
+ "(-2=ignore, -1=auto [default], index in VBT BIOS table)");
+
+module_param_named(reset, i915.reset, bool, 0600);
+MODULE_PARM_DESC(reset, "Attempt GPU resets (default: true)");
+
+module_param_named(enable_hangcheck, i915.enable_hangcheck, bool, 0644);
+MODULE_PARM_DESC(enable_hangcheck,
+ "Periodically check GPU activity for detecting hangs. "
+ "WARNING: Disabling this can cause system wide hangs. "
+ "(default: true)");
+
+module_param_named(enable_ppgtt, i915.enable_ppgtt, int, 0400);
+MODULE_PARM_DESC(enable_ppgtt,
+ "Override PPGTT usage. "
+ "(-1=auto [default], 0=disabled, 1=aliasing, 2=full)");
+
+module_param_named(enable_psr, i915.enable_psr, int, 0600);
+MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)");
+
+module_param_named(preliminary_hw_support, i915.preliminary_hw_support, int, 0600);
+MODULE_PARM_DESC(preliminary_hw_support,
+ "Enable preliminary hardware support.");
+
+module_param_named(disable_power_well, i915.disable_power_well, int, 0600);
+MODULE_PARM_DESC(disable_power_well,
+ "Disable the power well when possible (default: true)");
+
+module_param_named(enable_ips, i915.enable_ips, int, 0600);
+MODULE_PARM_DESC(enable_ips, "Enable IPS (default: true)");
+
+module_param_named(fastboot, i915.fastboot, bool, 0600);
+MODULE_PARM_DESC(fastboot,
+ "Try to skip unnecessary mode sets at boot time (default: false)");
+
+module_param_named(prefault_disable, i915.prefault_disable, bool, 0600);
+MODULE_PARM_DESC(prefault_disable,
+ "Disable page prefaulting for pread/pwrite/reloc (default:false). "
+ "For developers only.");
+
+module_param_named(invert_brightness, i915.invert_brightness, int, 0600);
+MODULE_PARM_DESC(invert_brightness,
+ "Invert backlight brightness "
+ "(-1 force normal, 0 machine defaults, 1 force inversion), please "
+ "report PCI device ID, subsystem vendor and subsystem device ID "
+ "to dri-devel@lists.freedesktop.org, if your machine needs it. "
+ "It will then be included in an upcoming module version.");
+
+module_param_named(disable_display, i915.disable_display, bool, 0600);
+MODULE_PARM_DESC(disable_display, "Disable display (default: false)");
+
+module_param_named(enable_cmd_parser, i915.enable_cmd_parser, int, 0600);
+MODULE_PARM_DESC(enable_cmd_parser,
+ "Enable command parsing (1=enabled, 0=disabled [default])");
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index a48b7cad6f11..9f5b18d9d885 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -26,7 +26,6 @@
#define _I915_REG_H_
#define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a)))
-#define _PIPE_INC(pipe, base, inc) ((base) + (pipe)*(inc))
#define _TRANSCODER(tran, a, b) ((a) + (tran)*((b)-(a)))
#define _PORT(port, a, b) ((a) + (port)*((b)-(a)))
@@ -73,7 +72,8 @@
#define I915_GC_RENDER_CLOCK_166_MHZ (0 << 0)
#define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0)
#define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0)
-#define LBB 0xf4
+#define PCI_LBPC 0xf4 /* legacy/combination backlight modes, also called LBB */
+
/* Graphics reset regs */
#define I965_GDRST 0xc0 /* PCI config register */
@@ -175,6 +175,18 @@
#define VGA_CR_DATA_CGA 0x3d5
/*
+ * Instruction field definitions used by the command parser
+ */
+#define INSTR_CLIENT_SHIFT 29
+#define INSTR_CLIENT_MASK 0xE0000000
+#define INSTR_MI_CLIENT 0x0
+#define INSTR_BC_CLIENT 0x2
+#define INSTR_RC_CLIENT 0x3
+#define INSTR_SUBCLIENT_SHIFT 27
+#define INSTR_SUBCLIENT_MASK 0x18000000
+#define INSTR_MEDIA_SUBCLIENT 0x2
+
+/*
* Memory interface instructions used by the kernel
*/
#define MI_INSTR(opcode, flags) (((opcode) << 23) | (flags))
@@ -377,14 +389,30 @@
#define DSPFREQSTAT_MASK (0x3 << DSPFREQSTAT_SHIFT)
#define DSPFREQGUAR_SHIFT 14
#define DSPFREQGUAR_MASK (0x3 << DSPFREQGUAR_SHIFT)
+
+/* See the PUNIT HAS v0.8 for the below bits */
+enum punit_power_well {
+ PUNIT_POWER_WELL_RENDER = 0,
+ PUNIT_POWER_WELL_MEDIA = 1,
+ PUNIT_POWER_WELL_DISP2D = 3,
+ PUNIT_POWER_WELL_DPIO_CMN_BC = 5,
+ PUNIT_POWER_WELL_DPIO_TX_B_LANES_01 = 6,
+ PUNIT_POWER_WELL_DPIO_TX_B_LANES_23 = 7,
+ PUNIT_POWER_WELL_DPIO_TX_C_LANES_01 = 8,
+ PUNIT_POWER_WELL_DPIO_TX_C_LANES_23 = 9,
+ PUNIT_POWER_WELL_DPIO_RX0 = 10,
+ PUNIT_POWER_WELL_DPIO_RX1 = 11,
+
+ PUNIT_POWER_WELL_NUM,
+};
+
#define PUNIT_REG_PWRGT_CTRL 0x60
#define PUNIT_REG_PWRGT_STATUS 0x61
-#define PUNIT_CLK_GATE 1
-#define PUNIT_PWR_RESET 2
-#define PUNIT_PWR_GATE 3
-#define RENDER_PWRGT (PUNIT_PWR_GATE << 0)
-#define MEDIA_PWRGT (PUNIT_PWR_GATE << 2)
-#define DISP2D_PWRGT (PUNIT_PWR_GATE << 6)
+#define PUNIT_PWRGT_MASK(power_well) (3 << ((power_well) * 2))
+#define PUNIT_PWRGT_PWR_ON(power_well) (0 << ((power_well) * 2))
+#define PUNIT_PWRGT_CLK_GATE(power_well) (1 << ((power_well) * 2))
+#define PUNIT_PWRGT_RESET(power_well) (2 << ((power_well) * 2))
+#define PUNIT_PWRGT_PWR_GATE(power_well) (3 << ((power_well) * 2))
#define PUNIT_REG_GPU_LFM 0xd3
#define PUNIT_REG_GPU_FREQ_REQ 0xd4
@@ -678,6 +706,7 @@
#define BLT_HWS_PGA_GEN7 (0x04280)
#define VEBOX_HWS_PGA_GEN7 (0x04380)
#define RING_ACTHD(base) ((base)+0x74)
+#define RING_ACTHD_UDW(base) ((base)+0x5c)
#define RING_NOPID(base) ((base)+0x94)
#define RING_IMR(base) ((base)+0xa8)
#define RING_TIMESTAMP(base) ((base)+0x358)
@@ -720,6 +749,7 @@
#define RING_INSTPS(base) ((base)+0x70)
#define RING_DMA_FADD(base) ((base)+0x78)
#define RING_INSTPM(base) ((base)+0xc0)
+#define RING_MI_MODE(base) ((base)+0x9c)
#define INSTPS 0x02070 /* 965+ only */
#define INSTDONE1 0x0207c /* 965+ only */
#define ACTHD_I965 0x02074
@@ -789,15 +819,22 @@
#define _3D_CHICKEN3 0x02090
#define _3D_CHICKEN_SF_DISABLE_OBJEND_CULL (1 << 10)
#define _3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL (1 << 5)
-#define _3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(x) ((x)<<1)
+#define _3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(x) ((x)<<1) /* gen8+ */
+#define _3D_CHICKEN3_SF_DISABLE_PIPELINED_ATTR_FETCH (1 << 1) /* gen6 */
#define MI_MODE 0x0209c
# define VS_TIMER_DISPATCH (1 << 6)
# define MI_FLUSH_ENABLE (1 << 12)
# define ASYNC_FLIP_PERF_DISABLE (1 << 14)
+# define MODE_IDLE (1 << 9)
#define GEN6_GT_MODE 0x20d0
-#define GEN6_GT_MODE_HI (1 << 9)
+#define GEN7_GT_MODE 0x7008
+#define GEN6_WIZ_HASHING(hi, lo) (((hi) << 9) | ((lo) << 7))
+#define GEN6_WIZ_HASHING_8x8 GEN6_WIZ_HASHING(0, 0)
+#define GEN6_WIZ_HASHING_8x4 GEN6_WIZ_HASHING(0, 1)
+#define GEN6_WIZ_HASHING_16x4 GEN6_WIZ_HASHING(1, 0)
+#define GEN6_WIZ_HASHING_MASK (GEN6_WIZ_HASHING(1, 1) << 16)
#define GEN6_TD_FOUR_ROW_DISPATCH_DISABLE (1 << 5)
#define GFX_MODE 0x02520
@@ -934,13 +971,19 @@
#define ECO_GATING_CX_ONLY (1<<3)
#define ECO_FLIP_DONE (1<<0)
+#define CACHE_MODE_0_GEN7 0x7000 /* IVB+ */
+#define HIZ_RAW_STALL_OPT_DISABLE (1<<2)
#define CACHE_MODE_1 0x7004 /* IVB+ */
-#define PIXEL_SUBSPAN_COLLECT_OPT_DISABLE (1<<6)
+#define PIXEL_SUBSPAN_COLLECT_OPT_DISABLE (1<<6)
+#define GEN8_4x4_STC_OPTIMIZATION_DISABLE (1<<6)
#define GEN6_BLITTER_ECOSKPD 0x221d0
#define GEN6_BLITTER_LOCK_SHIFT 16
#define GEN6_BLITTER_FBC_NOTIFY (1<<3)
+#define GEN6_RC_SLEEP_PSMI_CONTROL 0x2050
+#define GEN8_RC_SEMA_IDLE_MSG_DISABLE (1 << 12)
+
#define GEN6_BSD_SLEEP_PSMI_CONTROL 0x12050
#define GEN6_BSD_SLEEP_MSG_DISABLE (1 << 0)
#define GEN6_BSD_SLEEP_FLUSH_DISABLE (1 << 2)
@@ -1046,9 +1089,8 @@
#define FBC_CTL_IDLE_LINE (2<<2)
#define FBC_CTL_IDLE_DEBUG (3<<2)
#define FBC_CTL_CPU_FENCE (1<<1)
-#define FBC_CTL_PLANEA (0<<0)
-#define FBC_CTL_PLANEB (1<<0)
-#define FBC_FENCE_OFF 0x0321b
+#define FBC_CTL_PLANE(plane) ((plane)<<0)
+#define FBC_FENCE_OFF 0x03218 /* BSpec typo has 321Bh */
#define FBC_TAG 0x03300
#define FBC_LL_SIZE (1536)
@@ -1057,9 +1099,8 @@
#define DPFC_CB_BASE 0x3200
#define DPFC_CONTROL 0x3208
#define DPFC_CTL_EN (1<<31)
-#define DPFC_CTL_PLANEA (0<<30)
-#define DPFC_CTL_PLANEB (1<<30)
-#define IVB_DPFC_CTL_PLANE_SHIFT (29)
+#define DPFC_CTL_PLANE(plane) ((plane)<<30)
+#define IVB_DPFC_CTL_PLANE(plane) ((plane)<<29)
#define DPFC_CTL_FENCE_EN (1<<29)
#define IVB_DPFC_CTL_FENCE_EN (1<<28)
#define DPFC_CTL_PERSISTENT_MODE (1<<25)
@@ -1120,13 +1161,6 @@
#define FBC_REND_NUKE (1<<2)
#define FBC_REND_CACHE_CLEAN (1<<1)
-#define _HSW_PIPE_SLICE_CHICKEN_1_A 0x420B0
-#define _HSW_PIPE_SLICE_CHICKEN_1_B 0x420B4
-#define HSW_BYPASS_FBC_QUEUE (1<<22)
-#define HSW_PIPE_SLICE_CHICKEN_1(pipe) _PIPE(pipe, + \
- _HSW_PIPE_SLICE_CHICKEN_1_A, + \
- _HSW_PIPE_SLICE_CHICKEN_1_B)
-
/*
* GPIO regs
*/
@@ -1202,6 +1236,10 @@
/*
* Clock control & power management
*/
+#define DPLL_A_OFFSET 0x6014
+#define DPLL_B_OFFSET 0x6018
+#define DPLL(pipe) (dev_priv->info.dpll_offsets[pipe] + \
+ dev_priv->info.display_mmio_offset)
#define VGA0 0x6000
#define VGA1 0x6004
@@ -1214,9 +1252,6 @@
#define VGA1_PD_P1_DIV_2 (1 << 13)
#define VGA1_PD_P1_SHIFT 8
#define VGA1_PD_P1_MASK (0x1f << 8)
-#define _DPLL_A (dev_priv->info->display_mmio_offset + 0x6014)
-#define _DPLL_B (dev_priv->info->display_mmio_offset + 0x6018)
-#define DPLL(pipe) _PIPE(pipe, _DPLL_A, _DPLL_B)
#define DPLL_VCO_ENABLE (1 << 31)
#define DPLL_SDVO_HIGH_SPEED (1 << 30)
#define DPLL_DVO_2X_MODE (1 << 30)
@@ -1278,7 +1313,12 @@
#define SDVO_MULTIPLIER_MASK 0x000000ff
#define SDVO_MULTIPLIER_SHIFT_HIRES 4
#define SDVO_MULTIPLIER_SHIFT_VGA 0
-#define _DPLL_A_MD (dev_priv->info->display_mmio_offset + 0x601c) /* 965+ only */
+
+#define DPLL_A_MD_OFFSET 0x601c /* 965+ only */
+#define DPLL_B_MD_OFFSET 0x6020 /* 965+ only */
+#define DPLL_MD(pipe) (dev_priv->info.dpll_md_offsets[pipe] + \
+ dev_priv->info.display_mmio_offset)
+
/*
* UDI pixel divider, controlling how many pixels are stuffed into a packet.
*
@@ -1315,8 +1355,6 @@
*/
#define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f
#define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0
-#define _DPLL_B_MD (dev_priv->info->display_mmio_offset + 0x6020) /* 965+ only */
-#define DPLL_MD(pipe) _PIPE(pipe, _DPLL_A_MD, _DPLL_B_MD)
#define _FPA0 0x06040
#define _FPA1 0x06044
@@ -1348,7 +1386,7 @@
#define DSTATE_PLL_D3_OFF (1<<3)
#define DSTATE_GFX_CLOCK_GATING (1<<1)
#define DSTATE_DOT_CLOCK_GATING (1<<0)
-#define DSPCLK_GATE_D (dev_priv->info->display_mmio_offset + 0x6200)
+#define DSPCLK_GATE_D (dev_priv->info.display_mmio_offset + 0x6200)
# define DPUNIT_B_CLOCK_GATE_DISABLE (1 << 30) /* 965 */
# define VSUNIT_CLOCK_GATE_DISABLE (1 << 29) /* 965 */
# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* 965 */
@@ -1472,10 +1510,10 @@
/*
* Palette regs
*/
-
-#define _PALETTE_A (dev_priv->info->display_mmio_offset + 0xa000)
-#define _PALETTE_B (dev_priv->info->display_mmio_offset + 0xa800)
-#define PALETTE(pipe) _PIPE(pipe, _PALETTE_A, _PALETTE_B)
+#define PALETTE_A_OFFSET 0xa000
+#define PALETTE_B_OFFSET 0xa800
+#define PALETTE(pipe) (dev_priv->info.palette_offsets[pipe] + \
+ dev_priv->info.display_mmio_offset)
/* MCH MMIO space */
@@ -1862,7 +1900,7 @@
*/
/* Pipe A CRC regs */
-#define _PIPE_CRC_CTL_A (dev_priv->info->display_mmio_offset + 0x60050)
+#define _PIPE_CRC_CTL_A 0x60050
#define PIPE_CRC_ENABLE (1 << 31)
/* ivb+ source selection */
#define PIPE_CRC_SOURCE_PRIMARY_IVB (0 << 29)
@@ -1902,11 +1940,11 @@
#define _PIPE_CRC_RES_4_A_IVB 0x60070
#define _PIPE_CRC_RES_5_A_IVB 0x60074
-#define _PIPE_CRC_RES_RED_A (dev_priv->info->display_mmio_offset + 0x60060)
-#define _PIPE_CRC_RES_GREEN_A (dev_priv->info->display_mmio_offset + 0x60064)
-#define _PIPE_CRC_RES_BLUE_A (dev_priv->info->display_mmio_offset + 0x60068)
-#define _PIPE_CRC_RES_RES1_A_I915 (dev_priv->info->display_mmio_offset + 0x6006c)
-#define _PIPE_CRC_RES_RES2_A_G4X (dev_priv->info->display_mmio_offset + 0x60080)
+#define _PIPE_CRC_RES_RED_A 0x60060
+#define _PIPE_CRC_RES_GREEN_A 0x60064
+#define _PIPE_CRC_RES_BLUE_A 0x60068
+#define _PIPE_CRC_RES_RES1_A_I915 0x6006c
+#define _PIPE_CRC_RES_RES2_A_G4X 0x60080
/* Pipe B CRC regs */
#define _PIPE_CRC_RES_1_B_IVB 0x61064
@@ -1915,59 +1953,69 @@
#define _PIPE_CRC_RES_4_B_IVB 0x61070
#define _PIPE_CRC_RES_5_B_IVB 0x61074
-#define PIPE_CRC_CTL(pipe) _PIPE_INC(pipe, _PIPE_CRC_CTL_A, 0x01000)
+#define PIPE_CRC_CTL(pipe) _TRANSCODER2(pipe, _PIPE_CRC_CTL_A)
#define PIPE_CRC_RES_1_IVB(pipe) \
- _PIPE(pipe, _PIPE_CRC_RES_1_A_IVB, _PIPE_CRC_RES_1_B_IVB)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_1_A_IVB)
#define PIPE_CRC_RES_2_IVB(pipe) \
- _PIPE(pipe, _PIPE_CRC_RES_2_A_IVB, _PIPE_CRC_RES_2_B_IVB)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_2_A_IVB)
#define PIPE_CRC_RES_3_IVB(pipe) \
- _PIPE(pipe, _PIPE_CRC_RES_3_A_IVB, _PIPE_CRC_RES_3_B_IVB)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_3_A_IVB)
#define PIPE_CRC_RES_4_IVB(pipe) \
- _PIPE(pipe, _PIPE_CRC_RES_4_A_IVB, _PIPE_CRC_RES_4_B_IVB)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_4_A_IVB)
#define PIPE_CRC_RES_5_IVB(pipe) \
- _PIPE(pipe, _PIPE_CRC_RES_5_A_IVB, _PIPE_CRC_RES_5_B_IVB)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_5_A_IVB)
#define PIPE_CRC_RES_RED(pipe) \
- _PIPE_INC(pipe, _PIPE_CRC_RES_RED_A, 0x01000)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_RED_A)
#define PIPE_CRC_RES_GREEN(pipe) \
- _PIPE_INC(pipe, _PIPE_CRC_RES_GREEN_A, 0x01000)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_GREEN_A)
#define PIPE_CRC_RES_BLUE(pipe) \
- _PIPE_INC(pipe, _PIPE_CRC_RES_BLUE_A, 0x01000)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_BLUE_A)
#define PIPE_CRC_RES_RES1_I915(pipe) \
- _PIPE_INC(pipe, _PIPE_CRC_RES_RES1_A_I915, 0x01000)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_RES1_A_I915)
#define PIPE_CRC_RES_RES2_G4X(pipe) \
- _PIPE_INC(pipe, _PIPE_CRC_RES_RES2_A_G4X, 0x01000)
+ _TRANSCODER2(pipe, _PIPE_CRC_RES_RES2_A_G4X)
/* Pipe A timing regs */
-#define _HTOTAL_A (dev_priv->info->display_mmio_offset + 0x60000)
-#define _HBLANK_A (dev_priv->info->display_mmio_offset + 0x60004)
-#define _HSYNC_A (dev_priv->info->display_mmio_offset + 0x60008)
-#define _VTOTAL_A (dev_priv->info->display_mmio_offset + 0x6000c)
-#define _VBLANK_A (dev_priv->info->display_mmio_offset + 0x60010)
-#define _VSYNC_A (dev_priv->info->display_mmio_offset + 0x60014)
-#define _PIPEASRC (dev_priv->info->display_mmio_offset + 0x6001c)
-#define _BCLRPAT_A (dev_priv->info->display_mmio_offset + 0x60020)
-#define _VSYNCSHIFT_A (dev_priv->info->display_mmio_offset + 0x60028)
+#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
/* Pipe B timing regs */
-#define _HTOTAL_B (dev_priv->info->display_mmio_offset + 0x61000)
-#define _HBLANK_B (dev_priv->info->display_mmio_offset + 0x61004)
-#define _HSYNC_B (dev_priv->info->display_mmio_offset + 0x61008)
-#define _VTOTAL_B (dev_priv->info->display_mmio_offset + 0x6100c)
-#define _VBLANK_B (dev_priv->info->display_mmio_offset + 0x61010)
-#define _VSYNC_B (dev_priv->info->display_mmio_offset + 0x61014)
-#define _PIPEBSRC (dev_priv->info->display_mmio_offset + 0x6101c)
-#define _BCLRPAT_B (dev_priv->info->display_mmio_offset + 0x61020)
-#define _VSYNCSHIFT_B (dev_priv->info->display_mmio_offset + 0x61028)
-
-#define HTOTAL(trans) _TRANSCODER(trans, _HTOTAL_A, _HTOTAL_B)
-#define HBLANK(trans) _TRANSCODER(trans, _HBLANK_A, _HBLANK_B)
-#define HSYNC(trans) _TRANSCODER(trans, _HSYNC_A, _HSYNC_B)
-#define VTOTAL(trans) _TRANSCODER(trans, _VTOTAL_A, _VTOTAL_B)
-#define VBLANK(trans) _TRANSCODER(trans, _VBLANK_A, _VBLANK_B)
-#define VSYNC(trans) _TRANSCODER(trans, _VSYNC_A, _VSYNC_B)
-#define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
-#define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
+#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 TRANSCODER_A_OFFSET 0x60000
+#define TRANSCODER_B_OFFSET 0x61000
+#define TRANSCODER_C_OFFSET 0x62000
+#define TRANSCODER_EDP_OFFSET 0x6f000
+
+#define _TRANSCODER2(pipe, reg) (dev_priv->info.trans_offsets[(pipe)] - \
+ dev_priv->info.trans_offsets[TRANSCODER_A] + (reg) + \
+ dev_priv->info.display_mmio_offset)
+
+#define HTOTAL(trans) _TRANSCODER2(trans, _HTOTAL_A)
+#define HBLANK(trans) _TRANSCODER2(trans, _HBLANK_A)
+#define HSYNC(trans) _TRANSCODER2(trans, _HSYNC_A)
+#define VTOTAL(trans) _TRANSCODER2(trans, _VTOTAL_A)
+#define VBLANK(trans) _TRANSCODER2(trans, _VBLANK_A)
+#define VSYNC(trans) _TRANSCODER2(trans, _VSYNC_A)
+#define BCLRPAT(trans) _TRANSCODER2(trans, _BCLRPAT_A)
+#define VSYNCSHIFT(trans) _TRANSCODER2(trans, _VSYNCSHIFT_A)
+#define PIPESRC(trans) _TRANSCODER2(trans, _PIPEASRC)
/* HSW+ eDP PSR registers */
#define EDP_PSR_BASE(dev) (IS_HASWELL(dev) ? 0x64800 : 0x6f800)
@@ -2084,7 +2132,7 @@
/* Hotplug control (945+ only) */
-#define PORT_HOTPLUG_EN (dev_priv->info->display_mmio_offset + 0x61110)
+#define PORT_HOTPLUG_EN (dev_priv->info.display_mmio_offset + 0x61110)
#define PORTB_HOTPLUG_INT_EN (1 << 29)
#define PORTC_HOTPLUG_INT_EN (1 << 28)
#define PORTD_HOTPLUG_INT_EN (1 << 27)
@@ -2114,7 +2162,7 @@
#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2)
#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2)
-#define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114)
+#define PORT_HOTPLUG_STAT (dev_priv->info.display_mmio_offset + 0x61114)
/*
* HDMI/DP bits are gen4+
*
@@ -2332,9 +2380,7 @@
#define VIDEO_DIP_CTL 0x61170
/* Pre HSW: */
#define VIDEO_DIP_ENABLE (1 << 31)
-#define VIDEO_DIP_PORT_B (1 << 29)
-#define VIDEO_DIP_PORT_C (2 << 29)
-#define VIDEO_DIP_PORT_D (3 << 29)
+#define VIDEO_DIP_PORT(port) ((port) << 29)
#define VIDEO_DIP_PORT_MASK (3 << 29)
#define VIDEO_DIP_ENABLE_GCP (1 << 25)
#define VIDEO_DIP_ENABLE_AVI (1 << 21)
@@ -2391,7 +2437,7 @@
#define PP_DIVISOR 0x61210
/* Panel fitting */
-#define PFIT_CONTROL (dev_priv->info->display_mmio_offset + 0x61230)
+#define PFIT_CONTROL (dev_priv->info.display_mmio_offset + 0x61230)
#define PFIT_ENABLE (1 << 31)
#define PFIT_PIPE_MASK (3 << 29)
#define PFIT_PIPE_SHIFT 29
@@ -2409,7 +2455,7 @@
#define PFIT_SCALING_PROGRAMMED (1 << 26)
#define PFIT_SCALING_PILLAR (2 << 26)
#define PFIT_SCALING_LETTER (3 << 26)
-#define PFIT_PGM_RATIOS (dev_priv->info->display_mmio_offset + 0x61234)
+#define PFIT_PGM_RATIOS (dev_priv->info.display_mmio_offset + 0x61234)
/* Pre-965 */
#define PFIT_VERT_SCALE_SHIFT 20
#define PFIT_VERT_SCALE_MASK 0xfff00000
@@ -2421,25 +2467,25 @@
#define PFIT_HORIZ_SCALE_SHIFT_965 0
#define PFIT_HORIZ_SCALE_MASK_965 0x00001fff
-#define PFIT_AUTO_RATIOS (dev_priv->info->display_mmio_offset + 0x61238)
+#define PFIT_AUTO_RATIOS (dev_priv->info.display_mmio_offset + 0x61238)
-#define _VLV_BLC_PWM_CTL2_A (dev_priv->info->display_mmio_offset + 0x61250)
-#define _VLV_BLC_PWM_CTL2_B (dev_priv->info->display_mmio_offset + 0x61350)
+#define _VLV_BLC_PWM_CTL2_A (dev_priv->info.display_mmio_offset + 0x61250)
+#define _VLV_BLC_PWM_CTL2_B (dev_priv->info.display_mmio_offset + 0x61350)
#define VLV_BLC_PWM_CTL2(pipe) _PIPE(pipe, _VLV_BLC_PWM_CTL2_A, \
_VLV_BLC_PWM_CTL2_B)
-#define _VLV_BLC_PWM_CTL_A (dev_priv->info->display_mmio_offset + 0x61254)
-#define _VLV_BLC_PWM_CTL_B (dev_priv->info->display_mmio_offset + 0x61354)
+#define _VLV_BLC_PWM_CTL_A (dev_priv->info.display_mmio_offset + 0x61254)
+#define _VLV_BLC_PWM_CTL_B (dev_priv->info.display_mmio_offset + 0x61354)
#define VLV_BLC_PWM_CTL(pipe) _PIPE(pipe, _VLV_BLC_PWM_CTL_A, \
_VLV_BLC_PWM_CTL_B)
-#define _VLV_BLC_HIST_CTL_A (dev_priv->info->display_mmio_offset + 0x61260)
-#define _VLV_BLC_HIST_CTL_B (dev_priv->info->display_mmio_offset + 0x61360)
+#define _VLV_BLC_HIST_CTL_A (dev_priv->info.display_mmio_offset + 0x61260)
+#define _VLV_BLC_HIST_CTL_B (dev_priv->info.display_mmio_offset + 0x61360)
#define VLV_BLC_HIST_CTL(pipe) _PIPE(pipe, _VLV_BLC_HIST_CTL_A, \
_VLV_BLC_HIST_CTL_B)
/* Backlight control */
-#define BLC_PWM_CTL2 (dev_priv->info->display_mmio_offset + 0x61250) /* 965+ only */
+#define BLC_PWM_CTL2 (dev_priv->info.display_mmio_offset + 0x61250) /* 965+ only */
#define BLM_PWM_ENABLE (1 << 31)
#define BLM_COMBINATION_MODE (1 << 30) /* gen4 only */
#define BLM_PIPE_SELECT (1 << 29)
@@ -2462,7 +2508,7 @@
#define BLM_PHASE_IN_COUNT_MASK (0xff << 8)
#define BLM_PHASE_IN_INCR_SHIFT (0)
#define BLM_PHASE_IN_INCR_MASK (0xff << 0)
-#define BLC_PWM_CTL (dev_priv->info->display_mmio_offset + 0x61254)
+#define BLC_PWM_CTL (dev_priv->info.display_mmio_offset + 0x61254)
/*
* This is the most significant 15 bits of the number of backlight cycles in a
* complete cycle of the modulated backlight control.
@@ -2484,7 +2530,7 @@
#define BACKLIGHT_DUTY_CYCLE_MASK_PNV (0xfffe)
#define BLM_POLARITY_PNV (1 << 0) /* pnv only */
-#define BLC_HIST_CTL (dev_priv->info->display_mmio_offset + 0x61260)
+#define BLC_HIST_CTL (dev_priv->info.display_mmio_offset + 0x61260)
/* New registers for PCH-split platforms. Safe where new bits show up, the
* register layout machtes with gen4 BLC_PWM_CTL[12]. */
@@ -3178,10 +3224,10 @@
/* Display & cursor control */
/* Pipe A */
-#define _PIPEADSL (dev_priv->info->display_mmio_offset + 0x70000)
+#define _PIPEADSL 0x70000
#define DSL_LINEMASK_GEN2 0x00000fff
#define DSL_LINEMASK_GEN3 0x00001fff
-#define _PIPEACONF (dev_priv->info->display_mmio_offset + 0x70008)
+#define _PIPEACONF 0x70008
#define PIPECONF_ENABLE (1<<31)
#define PIPECONF_DISABLE 0
#define PIPECONF_DOUBLE_WIDE (1<<30)
@@ -3224,9 +3270,9 @@
#define PIPECONF_DITHER_TYPE_ST1 (1<<2)
#define PIPECONF_DITHER_TYPE_ST2 (2<<2)
#define PIPECONF_DITHER_TYPE_TEMP (3<<2)
-#define _PIPEASTAT (dev_priv->info->display_mmio_offset + 0x70024)
+#define _PIPEASTAT 0x70024
#define PIPE_FIFO_UNDERRUN_STATUS (1UL<<31)
-#define SPRITE1_FLIPDONE_INT_EN_VLV (1UL<<30)
+#define SPRITE1_FLIP_DONE_INT_EN_VLV (1UL<<30)
#define PIPE_CRC_ERROR_ENABLE (1UL<<29)
#define PIPE_CRC_DONE_ENABLE (1UL<<28)
#define PIPE_GMBUS_EVENT_ENABLE (1UL<<27)
@@ -3239,35 +3285,55 @@
#define PIPE_LEGACY_BLC_EVENT_ENABLE (1UL<<22)
#define PIPE_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21)
#define PIPE_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20)
+#define PIPE_B_PSR_INTERRUPT_ENABLE_VLV (1UL<<19)
#define PIPE_HOTPLUG_TV_INTERRUPT_ENABLE (1UL<<18) /* pre-965 */
#define PIPE_START_VBLANK_INTERRUPT_ENABLE (1UL<<18) /* 965 or later */
#define PIPE_VBLANK_INTERRUPT_ENABLE (1UL<<17)
#define PIPEA_HBLANK_INT_EN_VLV (1UL<<16)
#define PIPE_OVERLAY_UPDATED_ENABLE (1UL<<16)
-#define SPRITE1_FLIPDONE_INT_STATUS_VLV (1UL<<15)
-#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<14)
+#define SPRITE1_FLIP_DONE_INT_STATUS_VLV (1UL<<15)
+#define SPRITE0_FLIP_DONE_INT_STATUS_VLV (1UL<<14)
#define PIPE_CRC_ERROR_INTERRUPT_STATUS (1UL<<13)
#define PIPE_CRC_DONE_INTERRUPT_STATUS (1UL<<12)
#define PIPE_GMBUS_INTERRUPT_STATUS (1UL<<11)
-#define PLANE_FLIPDONE_INT_STATUS_VLV (1UL<<10)
+#define PLANE_FLIP_DONE_INT_STATUS_VLV (1UL<<10)
#define PIPE_HOTPLUG_INTERRUPT_STATUS (1UL<<10)
#define PIPE_VSYNC_INTERRUPT_STATUS (1UL<<9)
#define PIPE_DISPLAY_LINE_COMPARE_STATUS (1UL<<8)
#define PIPE_DPST_EVENT_STATUS (1UL<<7)
#define PIPE_LEGACY_BLC_EVENT_STATUS (1UL<<6)
+#define PIPE_A_PSR_STATUS_VLV (1UL<<6)
#define PIPE_ODD_FIELD_INTERRUPT_STATUS (1UL<<5)
#define PIPE_EVEN_FIELD_INTERRUPT_STATUS (1UL<<4)
+#define PIPE_B_PSR_STATUS_VLV (1UL<<3)
#define PIPE_HOTPLUG_TV_INTERRUPT_STATUS (1UL<<2) /* pre-965 */
#define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */
#define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1)
#define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0)
-#define PIPESRC(pipe) _PIPE(pipe, _PIPEASRC, _PIPEBSRC)
-#define PIPECONF(tran) _TRANSCODER(tran, _PIPEACONF, _PIPEBCONF)
-#define PIPEDSL(pipe) _PIPE(pipe, _PIPEADSL, _PIPEBDSL)
-#define PIPEFRAME(pipe) _PIPE(pipe, _PIPEAFRAMEHIGH, _PIPEBFRAMEHIGH)
-#define PIPEFRAMEPIXEL(pipe) _PIPE(pipe, _PIPEAFRAMEPIXEL, _PIPEBFRAMEPIXEL)
-#define PIPESTAT(pipe) _PIPE(pipe, _PIPEASTAT, _PIPEBSTAT)
+#define PIPESTAT_INT_ENABLE_MASK 0x7fff0000
+#define PIPESTAT_INT_STATUS_MASK 0x0000ffff
+
+#define PIPE_A_OFFSET 0x70000
+#define PIPE_B_OFFSET 0x71000
+#define PIPE_C_OFFSET 0x72000
+/*
+ * There's actually no pipe EDP. Some pipe registers have
+ * simply shifted from the pipe to the transcoder, while
+ * keeping their original offset. Thus we need PIPE_EDP_OFFSET
+ * to access such registers in transcoder EDP.
+ */
+#define PIPE_EDP_OFFSET 0x7f000
+
+#define _PIPE2(pipe, reg) (dev_priv->info.pipe_offsets[pipe] - \
+ dev_priv->info.pipe_offsets[PIPE_A] + (reg) + \
+ dev_priv->info.display_mmio_offset)
+
+#define PIPECONF(pipe) _PIPE2(pipe, _PIPEACONF)
+#define PIPEDSL(pipe) _PIPE2(pipe, _PIPEADSL)
+#define PIPEFRAME(pipe) _PIPE2(pipe, _PIPEAFRAMEHIGH)
+#define PIPEFRAMEPIXEL(pipe) _PIPE2(pipe, _PIPEAFRAMEPIXEL)
+#define PIPESTAT(pipe) _PIPE2(pipe, _PIPEASTAT)
#define _PIPE_MISC_A 0x70030
#define _PIPE_MISC_B 0x71030
@@ -3279,20 +3345,20 @@
#define PIPEMISC_DITHER_ENABLE (1<<4)
#define PIPEMISC_DITHER_TYPE_MASK (3<<2)
#define PIPEMISC_DITHER_TYPE_SP (0<<2)
-#define PIPEMISC(pipe) _PIPE(pipe, _PIPE_MISC_A, _PIPE_MISC_B)
+#define PIPEMISC(pipe) _PIPE2(pipe, _PIPE_MISC_A)
#define VLV_DPFLIPSTAT (VLV_DISPLAY_BASE + 0x70028)
#define PIPEB_LINE_COMPARE_INT_EN (1<<29)
#define PIPEB_HLINE_INT_EN (1<<28)
#define PIPEB_VBLANK_INT_EN (1<<27)
-#define SPRITED_FLIPDONE_INT_EN (1<<26)
-#define SPRITEC_FLIPDONE_INT_EN (1<<25)
-#define PLANEB_FLIPDONE_INT_EN (1<<24)
+#define SPRITED_FLIP_DONE_INT_EN (1<<26)
+#define SPRITEC_FLIP_DONE_INT_EN (1<<25)
+#define PLANEB_FLIP_DONE_INT_EN (1<<24)
#define PIPEA_LINE_COMPARE_INT_EN (1<<21)
#define PIPEA_HLINE_INT_EN (1<<20)
#define PIPEA_VBLANK_INT_EN (1<<19)
-#define SPRITEB_FLIPDONE_INT_EN (1<<18)
-#define SPRITEA_FLIPDONE_INT_EN (1<<17)
+#define SPRITEB_FLIP_DONE_INT_EN (1<<18)
+#define SPRITEA_FLIP_DONE_INT_EN (1<<17)
#define PLANEA_FLIPDONE_INT_EN (1<<16)
#define DPINVGTT (VLV_DISPLAY_BASE + 0x7002c) /* VLV only */
@@ -3323,7 +3389,7 @@
#define DSPARB_BEND_SHIFT 9 /* on 855 */
#define DSPARB_AEND_SHIFT 0
-#define DSPFW1 (dev_priv->info->display_mmio_offset + 0x70034)
+#define DSPFW1 (dev_priv->info.display_mmio_offset + 0x70034)
#define DSPFW_SR_SHIFT 23
#define DSPFW_SR_MASK (0x1ff<<23)
#define DSPFW_CURSORB_SHIFT 16
@@ -3331,11 +3397,11 @@
#define DSPFW_PLANEB_SHIFT 8
#define DSPFW_PLANEB_MASK (0x7f<<8)
#define DSPFW_PLANEA_MASK (0x7f)
-#define DSPFW2 (dev_priv->info->display_mmio_offset + 0x70038)
+#define DSPFW2 (dev_priv->info.display_mmio_offset + 0x70038)
#define DSPFW_CURSORA_MASK 0x00003f00
#define DSPFW_CURSORA_SHIFT 8
#define DSPFW_PLANEC_MASK (0x7f)
-#define DSPFW3 (dev_priv->info->display_mmio_offset + 0x7003c)
+#define DSPFW3 (dev_priv->info.display_mmio_offset + 0x7003c)
#define DSPFW_HPLL_SR_EN (1<<31)
#define DSPFW_CURSOR_SR_SHIFT 24
#define PINEVIEW_SELF_REFRESH_EN (1<<30)
@@ -3343,8 +3409,8 @@
#define DSPFW_HPLL_CURSOR_SHIFT 16
#define DSPFW_HPLL_CURSOR_MASK (0x3f<<16)
#define DSPFW_HPLL_SR_MASK (0x1ff)
-#define DSPFW4 (dev_priv->info->display_mmio_offset + 0x70070)
-#define DSPFW7 (dev_priv->info->display_mmio_offset + 0x7007c)
+#define DSPFW4 (dev_priv->info.display_mmio_offset + 0x70070)
+#define DSPFW7 (dev_priv->info.display_mmio_offset + 0x7007c)
/* drain latency register values*/
#define DRAIN_LATENCY_PRECISION_32 32
@@ -3468,12 +3534,12 @@
#define PIPE_PIXEL_MASK 0x00ffffff
#define PIPE_PIXEL_SHIFT 0
/* GM45+ just has to be different */
-#define _PIPEA_FRMCOUNT_GM45 (dev_priv->info->display_mmio_offset + 0x70040)
-#define _PIPEA_FLIPCOUNT_GM45 (dev_priv->info->display_mmio_offset + 0x70044)
+#define _PIPEA_FRMCOUNT_GM45 (dev_priv->info.display_mmio_offset + 0x70040)
+#define _PIPEA_FLIPCOUNT_GM45 (dev_priv->info.display_mmio_offset + 0x70044)
#define PIPE_FRMCOUNT_GM45(pipe) _PIPE(pipe, _PIPEA_FRMCOUNT_GM45, _PIPEB_FRMCOUNT_GM45)
/* Cursor A & B regs */
-#define _CURACNTR (dev_priv->info->display_mmio_offset + 0x70080)
+#define _CURACNTR (dev_priv->info.display_mmio_offset + 0x70080)
/* Old style CUR*CNTR flags (desktop 8xx) */
#define CURSOR_ENABLE 0x80000000
#define CURSOR_GAMMA_ENABLE 0x40000000
@@ -3489,23 +3555,27 @@
/* New style CUR*CNTR flags */
#define CURSOR_MODE 0x27
#define CURSOR_MODE_DISABLE 0x00
+#define CURSOR_MODE_128_32B_AX 0x02
+#define CURSOR_MODE_256_32B_AX 0x03
#define CURSOR_MODE_64_32B_AX 0x07
+#define CURSOR_MODE_128_ARGB_AX ((1 << 5) | CURSOR_MODE_128_32B_AX)
+#define CURSOR_MODE_256_ARGB_AX ((1 << 5) | CURSOR_MODE_256_32B_AX)
#define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX)
#define MCURSOR_PIPE_SELECT (1 << 28)
#define MCURSOR_PIPE_A 0x00
#define MCURSOR_PIPE_B (1 << 28)
#define MCURSOR_GAMMA_ENABLE (1 << 26)
#define CURSOR_TRICKLE_FEED_DISABLE (1 << 14)
-#define _CURABASE (dev_priv->info->display_mmio_offset + 0x70084)
-#define _CURAPOS (dev_priv->info->display_mmio_offset + 0x70088)
+#define _CURABASE (dev_priv->info.display_mmio_offset + 0x70084)
+#define _CURAPOS (dev_priv->info.display_mmio_offset + 0x70088)
#define CURSOR_POS_MASK 0x007FF
#define CURSOR_POS_SIGN 0x8000
#define CURSOR_X_SHIFT 0
#define CURSOR_Y_SHIFT 16
#define CURSIZE 0x700a0
-#define _CURBCNTR (dev_priv->info->display_mmio_offset + 0x700c0)
-#define _CURBBASE (dev_priv->info->display_mmio_offset + 0x700c4)
-#define _CURBPOS (dev_priv->info->display_mmio_offset + 0x700c8)
+#define _CURBCNTR (dev_priv->info.display_mmio_offset + 0x700c0)
+#define _CURBBASE (dev_priv->info.display_mmio_offset + 0x700c4)
+#define _CURBPOS (dev_priv->info.display_mmio_offset + 0x700c8)
#define _CURBCNTR_IVB 0x71080
#define _CURBBASE_IVB 0x71084
@@ -3520,7 +3590,7 @@
#define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB)
/* Display A control */
-#define _DSPACNTR (dev_priv->info->display_mmio_offset + 0x70180)
+#define _DSPACNTR 0x70180
#define DISPLAY_PLANE_ENABLE (1<<31)
#define DISPLAY_PLANE_DISABLE 0
#define DISPPLANE_GAMMA_ENABLE (1<<30)
@@ -3554,25 +3624,25 @@
#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */
#define DISPPLANE_TILED (1<<10)
-#define _DSPAADDR (dev_priv->info->display_mmio_offset + 0x70184)
-#define _DSPASTRIDE (dev_priv->info->display_mmio_offset + 0x70188)
-#define _DSPAPOS (dev_priv->info->display_mmio_offset + 0x7018C) /* reserved */
-#define _DSPASIZE (dev_priv->info->display_mmio_offset + 0x70190)
-#define _DSPASURF (dev_priv->info->display_mmio_offset + 0x7019C) /* 965+ only */
-#define _DSPATILEOFF (dev_priv->info->display_mmio_offset + 0x701A4) /* 965+ only */
-#define _DSPAOFFSET (dev_priv->info->display_mmio_offset + 0x701A4) /* HSW */
-#define _DSPASURFLIVE (dev_priv->info->display_mmio_offset + 0x701AC)
-
-#define DSPCNTR(plane) _PIPE(plane, _DSPACNTR, _DSPBCNTR)
-#define DSPADDR(plane) _PIPE(plane, _DSPAADDR, _DSPBADDR)
-#define DSPSTRIDE(plane) _PIPE(plane, _DSPASTRIDE, _DSPBSTRIDE)
-#define DSPPOS(plane) _PIPE(plane, _DSPAPOS, _DSPBPOS)
-#define DSPSIZE(plane) _PIPE(plane, _DSPASIZE, _DSPBSIZE)
-#define DSPSURF(plane) _PIPE(plane, _DSPASURF, _DSPBSURF)
-#define DSPTILEOFF(plane) _PIPE(plane, _DSPATILEOFF, _DSPBTILEOFF)
+#define _DSPAADDR 0x70184
+#define _DSPASTRIDE 0x70188
+#define _DSPAPOS 0x7018C /* reserved */
+#define _DSPASIZE 0x70190
+#define _DSPASURF 0x7019C /* 965+ only */
+#define _DSPATILEOFF 0x701A4 /* 965+ only */
+#define _DSPAOFFSET 0x701A4 /* HSW */
+#define _DSPASURFLIVE 0x701AC
+
+#define DSPCNTR(plane) _PIPE2(plane, _DSPACNTR)
+#define DSPADDR(plane) _PIPE2(plane, _DSPAADDR)
+#define DSPSTRIDE(plane) _PIPE2(plane, _DSPASTRIDE)
+#define DSPPOS(plane) _PIPE2(plane, _DSPAPOS)
+#define DSPSIZE(plane) _PIPE2(plane, _DSPASIZE)
+#define DSPSURF(plane) _PIPE2(plane, _DSPASURF)
+#define DSPTILEOFF(plane) _PIPE2(plane, _DSPATILEOFF)
#define DSPLINOFF(plane) DSPADDR(plane)
-#define DSPOFFSET(plane) _PIPE(plane, _DSPAOFFSET, _DSPBOFFSET)
-#define DSPSURFLIVE(plane) _PIPE(plane, _DSPASURFLIVE, _DSPBSURFLIVE)
+#define DSPOFFSET(plane) _PIPE2(plane, _DSPAOFFSET)
+#define DSPSURFLIVE(plane) _PIPE2(plane, _DSPASURFLIVE)
/* Display/Sprite base address macros */
#define DISP_BASEADDR_MASK (0xfffff000)
@@ -3580,44 +3650,44 @@
#define I915_HI_DISPBASE(val) (val & DISP_BASEADDR_MASK)
/* VBIOS flags */
-#define SWF00 (dev_priv->info->display_mmio_offset + 0x71410)
-#define SWF01 (dev_priv->info->display_mmio_offset + 0x71414)
-#define SWF02 (dev_priv->info->display_mmio_offset + 0x71418)
-#define SWF03 (dev_priv->info->display_mmio_offset + 0x7141c)
-#define SWF04 (dev_priv->info->display_mmio_offset + 0x71420)
-#define SWF05 (dev_priv->info->display_mmio_offset + 0x71424)
-#define SWF06 (dev_priv->info->display_mmio_offset + 0x71428)
-#define SWF10 (dev_priv->info->display_mmio_offset + 0x70410)
-#define SWF11 (dev_priv->info->display_mmio_offset + 0x70414)
-#define SWF14 (dev_priv->info->display_mmio_offset + 0x71420)
-#define SWF30 (dev_priv->info->display_mmio_offset + 0x72414)
-#define SWF31 (dev_priv->info->display_mmio_offset + 0x72418)
-#define SWF32 (dev_priv->info->display_mmio_offset + 0x7241c)
+#define SWF00 (dev_priv->info.display_mmio_offset + 0x71410)
+#define SWF01 (dev_priv->info.display_mmio_offset + 0x71414)
+#define SWF02 (dev_priv->info.display_mmio_offset + 0x71418)
+#define SWF03 (dev_priv->info.display_mmio_offset + 0x7141c)
+#define SWF04 (dev_priv->info.display_mmio_offset + 0x71420)
+#define SWF05 (dev_priv->info.display_mmio_offset + 0x71424)
+#define SWF06 (dev_priv->info.display_mmio_offset + 0x71428)
+#define SWF10 (dev_priv->info.display_mmio_offset + 0x70410)
+#define SWF11 (dev_priv->info.display_mmio_offset + 0x70414)
+#define SWF14 (dev_priv->info.display_mmio_offset + 0x71420)
+#define SWF30 (dev_priv->info.display_mmio_offset + 0x72414)
+#define SWF31 (dev_priv->info.display_mmio_offset + 0x72418)
+#define SWF32 (dev_priv->info.display_mmio_offset + 0x7241c)
/* Pipe B */
-#define _PIPEBDSL (dev_priv->info->display_mmio_offset + 0x71000)
-#define _PIPEBCONF (dev_priv->info->display_mmio_offset + 0x71008)
-#define _PIPEBSTAT (dev_priv->info->display_mmio_offset + 0x71024)
+#define _PIPEBDSL (dev_priv->info.display_mmio_offset + 0x71000)
+#define _PIPEBCONF (dev_priv->info.display_mmio_offset + 0x71008)
+#define _PIPEBSTAT (dev_priv->info.display_mmio_offset + 0x71024)
#define _PIPEBFRAMEHIGH 0x71040
#define _PIPEBFRAMEPIXEL 0x71044
-#define _PIPEB_FRMCOUNT_GM45 (dev_priv->info->display_mmio_offset + 0x71040)
-#define _PIPEB_FLIPCOUNT_GM45 (dev_priv->info->display_mmio_offset + 0x71044)
+#define _PIPEB_FRMCOUNT_GM45 (dev_priv->info.display_mmio_offset + 0x71040)
+#define _PIPEB_FLIPCOUNT_GM45 (dev_priv->info.display_mmio_offset + 0x71044)
/* Display B control */
-#define _DSPBCNTR (dev_priv->info->display_mmio_offset + 0x71180)
+#define _DSPBCNTR (dev_priv->info.display_mmio_offset + 0x71180)
#define DISPPLANE_ALPHA_TRANS_ENABLE (1<<15)
#define DISPPLANE_ALPHA_TRANS_DISABLE 0
#define DISPPLANE_SPRITE_ABOVE_DISPLAY 0
#define DISPPLANE_SPRITE_ABOVE_OVERLAY (1)
-#define _DSPBADDR (dev_priv->info->display_mmio_offset + 0x71184)
-#define _DSPBSTRIDE (dev_priv->info->display_mmio_offset + 0x71188)
-#define _DSPBPOS (dev_priv->info->display_mmio_offset + 0x7118C)
-#define _DSPBSIZE (dev_priv->info->display_mmio_offset + 0x71190)
-#define _DSPBSURF (dev_priv->info->display_mmio_offset + 0x7119C)
-#define _DSPBTILEOFF (dev_priv->info->display_mmio_offset + 0x711A4)
-#define _DSPBOFFSET (dev_priv->info->display_mmio_offset + 0x711A4)
-#define _DSPBSURFLIVE (dev_priv->info->display_mmio_offset + 0x711AC)
+#define _DSPBADDR (dev_priv->info.display_mmio_offset + 0x71184)
+#define _DSPBSTRIDE (dev_priv->info.display_mmio_offset + 0x71188)
+#define _DSPBPOS (dev_priv->info.display_mmio_offset + 0x7118C)
+#define _DSPBSIZE (dev_priv->info.display_mmio_offset + 0x71190)
+#define _DSPBSURF (dev_priv->info.display_mmio_offset + 0x7119C)
+#define _DSPBTILEOFF (dev_priv->info.display_mmio_offset + 0x711A4)
+#define _DSPBOFFSET (dev_priv->info.display_mmio_offset + 0x711A4)
+#define _DSPBSURFLIVE (dev_priv->info.display_mmio_offset + 0x711AC)
/* Sprite A control */
#define _DVSACNTR 0x72180
@@ -3866,48 +3936,45 @@
#define FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK 0xff
-#define _PIPEA_DATA_M1 (dev_priv->info->display_mmio_offset + 0x60030)
+#define _PIPEA_DATA_M1 0x60030
#define PIPE_DATA_M1_OFFSET 0
-#define _PIPEA_DATA_N1 (dev_priv->info->display_mmio_offset + 0x60034)
+#define _PIPEA_DATA_N1 0x60034
#define PIPE_DATA_N1_OFFSET 0
-#define _PIPEA_DATA_M2 (dev_priv->info->display_mmio_offset + 0x60038)
+#define _PIPEA_DATA_M2 0x60038
#define PIPE_DATA_M2_OFFSET 0
-#define _PIPEA_DATA_N2 (dev_priv->info->display_mmio_offset + 0x6003c)
+#define _PIPEA_DATA_N2 0x6003c
#define PIPE_DATA_N2_OFFSET 0
-#define _PIPEA_LINK_M1 (dev_priv->info->display_mmio_offset + 0x60040)
+#define _PIPEA_LINK_M1 0x60040
#define PIPE_LINK_M1_OFFSET 0
-#define _PIPEA_LINK_N1 (dev_priv->info->display_mmio_offset + 0x60044)
+#define _PIPEA_LINK_N1 0x60044
#define PIPE_LINK_N1_OFFSET 0
-#define _PIPEA_LINK_M2 (dev_priv->info->display_mmio_offset + 0x60048)
+#define _PIPEA_LINK_M2 0x60048
#define PIPE_LINK_M2_OFFSET 0
-#define _PIPEA_LINK_N2 (dev_priv->info->display_mmio_offset + 0x6004c)
+#define _PIPEA_LINK_N2 0x6004c
#define PIPE_LINK_N2_OFFSET 0
/* PIPEB timing regs are same start from 0x61000 */
-#define _PIPEB_DATA_M1 (dev_priv->info->display_mmio_offset + 0x61030)
-#define _PIPEB_DATA_N1 (dev_priv->info->display_mmio_offset + 0x61034)
-
-#define _PIPEB_DATA_M2 (dev_priv->info->display_mmio_offset + 0x61038)
-#define _PIPEB_DATA_N2 (dev_priv->info->display_mmio_offset + 0x6103c)
-
-#define _PIPEB_LINK_M1 (dev_priv->info->display_mmio_offset + 0x61040)
-#define _PIPEB_LINK_N1 (dev_priv->info->display_mmio_offset + 0x61044)
-
-#define _PIPEB_LINK_M2 (dev_priv->info->display_mmio_offset + 0x61048)
-#define _PIPEB_LINK_N2 (dev_priv->info->display_mmio_offset + 0x6104c)
-
-#define PIPE_DATA_M1(tran) _TRANSCODER(tran, _PIPEA_DATA_M1, _PIPEB_DATA_M1)
-#define PIPE_DATA_N1(tran) _TRANSCODER(tran, _PIPEA_DATA_N1, _PIPEB_DATA_N1)
-#define PIPE_DATA_M2(tran) _TRANSCODER(tran, _PIPEA_DATA_M2, _PIPEB_DATA_M2)
-#define PIPE_DATA_N2(tran) _TRANSCODER(tran, _PIPEA_DATA_N2, _PIPEB_DATA_N2)
-#define PIPE_LINK_M1(tran) _TRANSCODER(tran, _PIPEA_LINK_M1, _PIPEB_LINK_M1)
-#define PIPE_LINK_N1(tran) _TRANSCODER(tran, _PIPEA_LINK_N1, _PIPEB_LINK_N1)
-#define PIPE_LINK_M2(tran) _TRANSCODER(tran, _PIPEA_LINK_M2, _PIPEB_LINK_M2)
-#define PIPE_LINK_N2(tran) _TRANSCODER(tran, _PIPEA_LINK_N2, _PIPEB_LINK_N2)
+#define _PIPEB_DATA_M1 0x61030
+#define _PIPEB_DATA_N1 0x61034
+#define _PIPEB_DATA_M2 0x61038
+#define _PIPEB_DATA_N2 0x6103c
+#define _PIPEB_LINK_M1 0x61040
+#define _PIPEB_LINK_N1 0x61044
+#define _PIPEB_LINK_M2 0x61048
+#define _PIPEB_LINK_N2 0x6104c
+
+#define PIPE_DATA_M1(tran) _TRANSCODER2(tran, _PIPEA_DATA_M1)
+#define PIPE_DATA_N1(tran) _TRANSCODER2(tran, _PIPEA_DATA_N1)
+#define PIPE_DATA_M2(tran) _TRANSCODER2(tran, _PIPEA_DATA_M2)
+#define PIPE_DATA_N2(tran) _TRANSCODER2(tran, _PIPEA_DATA_N2)
+#define PIPE_LINK_M1(tran) _TRANSCODER2(tran, _PIPEA_LINK_M1)
+#define PIPE_LINK_N1(tran) _TRANSCODER2(tran, _PIPEA_LINK_N1)
+#define PIPE_LINK_M2(tran) _TRANSCODER2(tran, _PIPEA_LINK_M2)
+#define PIPE_LINK_N2(tran) _TRANSCODER2(tran, _PIPEA_LINK_N2)
/* CPU panel fitter */
/* IVB+ has 3 fitters, 0 is 7x5 capable, the other two only 3x3 */
@@ -4084,13 +4151,14 @@
#define ILK_ELPIN_409_SELECT (1 << 25)
#define ILK_DPARB_GATE (1<<22)
#define ILK_VSDPFD_FULL (1<<21)
-#define ILK_DISPLAY_CHICKEN_FUSES 0x42014
-#define ILK_INTERNAL_GRAPHICS_DISABLE (1<<31)
-#define ILK_INTERNAL_DISPLAY_DISABLE (1<<30)
-#define ILK_DISPLAY_DEBUG_DISABLE (1<<29)
-#define ILK_HDCP_DISABLE (1<<25)
-#define ILK_eDP_A_DISABLE (1<<24)
-#define ILK_DESKTOP (1<<23)
+#define FUSE_STRAP 0x42014
+#define ILK_INTERNAL_GRAPHICS_DISABLE (1 << 31)
+#define ILK_INTERNAL_DISPLAY_DISABLE (1 << 30)
+#define ILK_DISPLAY_DEBUG_DISABLE (1 << 29)
+#define ILK_HDCP_DISABLE (1 << 25)
+#define ILK_eDP_A_DISABLE (1 << 24)
+#define HSW_CDCLK_LIMIT (1 << 24)
+#define ILK_DESKTOP (1 << 23)
#define ILK_DSPCLK_GATE_D 0x42020
#define ILK_VRHUNIT_CLOCK_GATE_DISABLE (1 << 28)
@@ -4109,7 +4177,8 @@
#define _CHICKEN_PIPESL_1_A 0x420b0
#define _CHICKEN_PIPESL_1_B 0x420b4
-#define DPRS_MASK_VBLANK_SRD (1 << 0)
+#define HSW_FBCQ_DIS (1 << 22)
+#define BDW_DPRS_MASK_VBLANK_SRD (1 << 0)
#define CHICKEN_PIPESL_1(pipe) _PIPE(pipe, _CHICKEN_PIPESL_1_A, _CHICKEN_PIPESL_1_B)
#define DISP_ARB_CTL 0x45000
@@ -4120,6 +4189,8 @@
#define GEN7_MSG_CTL 0x45010
#define WAIT_FOR_PCH_RESET_ACK (1<<1)
#define WAIT_FOR_PCH_FLR_ACK (1<<0)
+#define HSW_NDE_RSTWRN_OPT 0x46408
+#define RESET_PCH_HANDSHAKE_ENABLE (1<<4)
/* GEN7 chicken */
#define GEN7_COMMON_SLICE_CHICKEN1 0x7010
@@ -4127,8 +4198,11 @@
#define COMMON_SLICE_CHICKEN2 0x7014
# define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE (1<<0)
+#define GEN7_L3SQCREG1 0xB010
+#define VLV_B0_WA_L3SQCREG1_VALUE 0x00D30000
+
#define GEN7_L3CNTLREG1 0xB01C
-#define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C4FFF8C
+#define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C47FF8C
#define GEN7_L3AGDIS (1<<19)
#define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030
@@ -4148,9 +4222,6 @@
#define HSW_SCRATCH1 0xb038
#define HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE (1<<27)
-#define HSW_FUSE_STRAP 0x42014
-#define HSW_CDCLK_LIMIT (1 << 24)
-
/* PCH */
/* south display engine interrupt: IBX */
@@ -4436,24 +4507,24 @@
#define HSW_VIDEO_DIP_GCP_B 0x61210
#define HSW_TVIDEO_DIP_CTL(trans) \
- _TRANSCODER(trans, HSW_VIDEO_DIP_CTL_A, HSW_VIDEO_DIP_CTL_B)
+ _TRANSCODER2(trans, HSW_VIDEO_DIP_CTL_A)
#define HSW_TVIDEO_DIP_AVI_DATA(trans) \
- _TRANSCODER(trans, HSW_VIDEO_DIP_AVI_DATA_A, HSW_VIDEO_DIP_AVI_DATA_B)
+ _TRANSCODER2(trans, HSW_VIDEO_DIP_AVI_DATA_A)
#define HSW_TVIDEO_DIP_VS_DATA(trans) \
- _TRANSCODER(trans, HSW_VIDEO_DIP_VS_DATA_A, HSW_VIDEO_DIP_VS_DATA_B)
+ _TRANSCODER2(trans, HSW_VIDEO_DIP_VS_DATA_A)
#define HSW_TVIDEO_DIP_SPD_DATA(trans) \
- _TRANSCODER(trans, HSW_VIDEO_DIP_SPD_DATA_A, HSW_VIDEO_DIP_SPD_DATA_B)
+ _TRANSCODER2(trans, HSW_VIDEO_DIP_SPD_DATA_A)
#define HSW_TVIDEO_DIP_GCP(trans) \
- _TRANSCODER(trans, HSW_VIDEO_DIP_GCP_A, HSW_VIDEO_DIP_GCP_B)
+ _TRANSCODER2(trans, HSW_VIDEO_DIP_GCP_A)
#define HSW_TVIDEO_DIP_VSC_DATA(trans) \
- _TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B)
+ _TRANSCODER2(trans, HSW_VIDEO_DIP_VSC_DATA_A)
#define HSW_STEREO_3D_CTL_A 0x70020
#define S3D_ENABLE (1<<31)
#define HSW_STEREO_3D_CTL_B 0x71020
#define HSW_STEREO_3D_CTL(trans) \
- _TRANSCODER(trans, HSW_STEREO_3D_CTL_A, HSW_STEREO_3D_CTL_A)
+ _PIPE2(trans, HSW_STEREO_3D_CTL_A)
#define _PCH_TRANS_HTOTAL_B 0xe1000
#define _PCH_TRANS_HBLANK_B 0xe1004
@@ -4865,6 +4936,9 @@
#define GEN7_UCGCTL4 0x940c
#define GEN7_L3BANK2X_CLOCK_GATE_DISABLE (1<<25)
+#define GEN8_UCGCTL6 0x9430
+#define GEN8_SDEUNIT_CLOCK_GATE_DISABLE (1<<14)
+
#define GEN6_RPNSWREQ 0xA008
#define GEN6_TURBO_DISABLE (1<<31)
#define GEN6_FREQUENCY(x) ((x)<<25)
@@ -4945,6 +5019,10 @@
GEN6_PM_RP_DOWN_THRESHOLD | \
GEN6_PM_RP_DOWN_TIMEOUT)
+#define VLV_GTLC_SURVIVABILITY_REG 0x130098
+#define VLV_GFX_CLK_STATUS_BIT (1<<3)
+#define VLV_GFX_CLK_FORCE_ON_BIT (1<<2)
+
#define GEN6_GT_GFX_RC6_LOCKED 0x138104
#define VLV_COUNTER_CONTROL 0x138104
#define VLV_COUNT_RANGE_HIGH (1<<15)
@@ -5006,6 +5084,10 @@
#define GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE (1<<10)
#define GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE (1<<3)
+#define GEN8_ROW_CHICKEN 0xe4f0
+#define PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE (1<<8)
+#define STALL_DOP_GATING_DISABLE (1<<5)
+
#define GEN7_ROW_CHICKEN2 0xe4f4
#define GEN7_ROW_CHICKEN2_GT2 0xf4f4
#define DOP_CLOCK_GATING_DISABLE (1<<0)
@@ -5017,7 +5099,7 @@
#define GEN8_CENTROID_PIXEL_OPT_DIS (1<<8)
#define GEN8_SAMPLER_POWER_BYPASS_DIS (1<<1)
-#define G4X_AUD_VID_DID (dev_priv->info->display_mmio_offset + 0x62020)
+#define G4X_AUD_VID_DID (dev_priv->info.display_mmio_offset + 0x62020)
#define INTEL_AUDIO_DEVCL 0x808629FB
#define INTEL_AUDIO_DEVBLC 0x80862801
#define INTEL_AUDIO_DEVCTG 0x80862802
@@ -5178,8 +5260,8 @@
#define TRANS_DDI_FUNC_CTL_B 0x61400
#define TRANS_DDI_FUNC_CTL_C 0x62400
#define TRANS_DDI_FUNC_CTL_EDP 0x6F400
-#define TRANS_DDI_FUNC_CTL(tran) _TRANSCODER(tran, TRANS_DDI_FUNC_CTL_A, \
- TRANS_DDI_FUNC_CTL_B)
+#define TRANS_DDI_FUNC_CTL(tran) _TRANSCODER2(tran, TRANS_DDI_FUNC_CTL_A)
+
#define TRANS_DDI_FUNC_ENABLE (1<<31)
/* Those bits are ignored by pipe EDP since it can only connect to DDI A */
#define TRANS_DDI_PORT_MASK (7<<28)
@@ -5311,8 +5393,12 @@
#define SPLL_PLL_ENABLE (1<<31)
#define SPLL_PLL_SSC (1<<28)
#define SPLL_PLL_NON_SSC (2<<28)
+#define SPLL_PLL_LCPLL (3<<28)
+#define SPLL_PLL_REF_MASK (3<<28)
#define SPLL_PLL_FREQ_810MHz (0<<26)
#define SPLL_PLL_FREQ_1350MHz (1<<26)
+#define SPLL_PLL_FREQ_2700MHz (2<<26)
+#define SPLL_PLL_FREQ_MASK (3<<26)
/* WRPLL */
#define WRPLL_CTL1 0x46040
@@ -5323,8 +5409,13 @@
#define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28)
/* WRPLL divider programming */
#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0)
+#define WRPLL_DIVIDER_REF_MASK (0xff)
#define WRPLL_DIVIDER_POST(x) ((x)<<8)
+#define WRPLL_DIVIDER_POST_MASK (0x3f<<8)
+#define WRPLL_DIVIDER_POST_SHIFT 8
#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16)
+#define WRPLL_DIVIDER_FB_SHIFT 16
+#define WRPLL_DIVIDER_FB_MASK (0xff<<16)
/* Port clock selection */
#define PORT_CLK_SEL_A 0x46100
@@ -5337,6 +5428,7 @@
#define PORT_CLK_SEL_WRPLL1 (4<<29)
#define PORT_CLK_SEL_WRPLL2 (5<<29)
#define PORT_CLK_SEL_NONE (7<<29)
+#define PORT_CLK_SEL_MASK (7<<29)
/* Transcoder clock selection */
#define TRANS_CLK_SEL_A 0x46140
@@ -5346,10 +5438,12 @@
#define TRANS_CLK_SEL_DISABLED (0x0<<29)
#define TRANS_CLK_SEL_PORT(x) ((x+1)<<29)
-#define _TRANSA_MSA_MISC 0x60410
-#define _TRANSB_MSA_MISC 0x61410
-#define TRANS_MSA_MISC(tran) _TRANSCODER(tran, _TRANSA_MSA_MISC, \
- _TRANSB_MSA_MISC)
+#define TRANSA_MSA_MISC 0x60410
+#define TRANSB_MSA_MISC 0x61410
+#define TRANSC_MSA_MISC 0x62410
+#define TRANS_EDP_MSA_MISC 0x6f410
+#define TRANS_MSA_MISC(tran) _TRANSCODER2(tran, TRANSA_MSA_MISC)
+
#define TRANS_MSA_SYNC_CLK (1<<0)
#define TRANS_MSA_6_BPC (0<<5)
#define TRANS_MSA_8_BPC (1<<5)
@@ -5389,6 +5483,8 @@
/* SFUSE_STRAP */
#define SFUSE_STRAP 0xc2014
+#define SFUSE_STRAP_FUSE_LOCK (1<<13)
+#define SFUSE_STRAP_DISPLAY_DISABLED (1<<7)
#define SFUSE_STRAP_DDIB_DETECTED (1<<2)
#define SFUSE_STRAP_DDIC_DETECTED (1<<1)
#define SFUSE_STRAP_DDID_DETECTED (1<<0)
@@ -5857,4 +5953,12 @@
#define MIPI_READ_DATA_VALID(pipe) _PIPE(pipe, _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID)
#define READ_DATA_VALID(n) (1 << (n))
+/* For UMS only (deprecated): */
+#define _PALETTE_A (dev_priv->info.display_mmio_offset + 0xa000)
+#define _PALETTE_B (dev_priv->info.display_mmio_offset + 0xa800)
+#define _DPLL_A (dev_priv->info.display_mmio_offset + 0x6014)
+#define _DPLL_B (dev_priv->info.display_mmio_offset + 0x6018)
+#define _DPLL_A_MD (dev_priv->info.display_mmio_offset + 0x601c)
+#define _DPLL_B_MD (dev_priv->info.display_mmio_offset + 0x6020)
+
#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 8150fdc08d49..56785e8fb2eb 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -236,19 +236,9 @@ static void i915_save_display(struct drm_device *dev)
dev_priv->regfile.savePP_DIVISOR = I915_READ(PP_DIVISOR);
}
- /* Only regfile.save FBC state on the platform that supports FBC */
- if (HAS_FBC(dev)) {
- if (HAS_PCH_SPLIT(dev)) {
- dev_priv->regfile.saveDPFC_CB_BASE = I915_READ(ILK_DPFC_CB_BASE);
- } else if (IS_GM45(dev)) {
- dev_priv->regfile.saveDPFC_CB_BASE = I915_READ(DPFC_CB_BASE);
- } else {
- dev_priv->regfile.saveFBC_CFB_BASE = I915_READ(FBC_CFB_BASE);
- dev_priv->regfile.saveFBC_LL_BASE = I915_READ(FBC_LL_BASE);
- dev_priv->regfile.saveFBC_CONTROL2 = I915_READ(FBC_CONTROL2);
- dev_priv->regfile.saveFBC_CONTROL = I915_READ(FBC_CONTROL);
- }
- }
+ /* save FBC interval */
+ if (HAS_FBC(dev) && INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev))
+ dev_priv->regfile.saveFBC_CONTROL = I915_READ(FBC_CONTROL);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
i915_save_vga(dev);
@@ -300,18 +290,10 @@ static void i915_restore_display(struct drm_device *dev)
/* only restore FBC info on the platform that supports FBC*/
intel_disable_fbc(dev);
- if (HAS_FBC(dev)) {
- if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->regfile.saveDPFC_CB_BASE);
- } else if (IS_GM45(dev)) {
- I915_WRITE(DPFC_CB_BASE, dev_priv->regfile.saveDPFC_CB_BASE);
- } else {
- I915_WRITE(FBC_CFB_BASE, dev_priv->regfile.saveFBC_CFB_BASE);
- I915_WRITE(FBC_LL_BASE, dev_priv->regfile.saveFBC_LL_BASE);
- I915_WRITE(FBC_CONTROL2, dev_priv->regfile.saveFBC_CONTROL2);
- I915_WRITE(FBC_CONTROL, dev_priv->regfile.saveFBC_CONTROL);
- }
- }
+
+ /* restore FBC interval */
+ if (HAS_FBC(dev) && INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev))
+ I915_WRITE(FBC_CONTROL, dev_priv->regfile.saveFBC_CONTROL);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
i915_restore_vga(dev);
@@ -324,10 +306,6 @@ int i915_save_state(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
- if (INTEL_INFO(dev)->gen <= 4)
- pci_read_config_byte(dev->pdev, LBB,
- &dev_priv->regfile.saveLBB);
-
mutex_lock(&dev->struct_mutex);
i915_save_display(dev);
@@ -377,10 +355,6 @@ int i915_restore_state(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
- if (INTEL_INFO(dev)->gen <= 4)
- pci_write_config_byte(dev->pdev, LBB,
- dev_priv->regfile.saveLBB);
-
mutex_lock(&dev->struct_mutex);
i915_gem_restore_fences(dev);
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 33bcae314bf8..9c57029f6f4b 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -269,7 +269,7 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
freq = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
ret = vlv_gpu_freq(dev_priv, (freq >> 8) & 0xff);
} else {
- ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER;
+ ret = dev_priv->rps.cur_freq * GT_FREQUENCY_MULTIPLIER;
}
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -284,7 +284,7 @@ static ssize_t vlv_rpe_freq_mhz_show(struct device *kdev,
struct drm_i915_private *dev_priv = dev->dev_private;
return snprintf(buf, PAGE_SIZE, "%d\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.rpe_delay));
+ vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq));
}
static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
@@ -298,9 +298,9 @@ static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute
mutex_lock(&dev_priv->rps.hw_lock);
if (IS_VALLEYVIEW(dev_priv->dev))
- ret = vlv_gpu_freq(dev_priv, dev_priv->rps.max_delay);
+ ret = vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit);
else
- ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
+ ret = dev_priv->rps.max_freq_softlimit * GT_FREQUENCY_MULTIPLIER;
mutex_unlock(&dev_priv->rps.hw_lock);
return snprintf(buf, PAGE_SIZE, "%d\n", ret);
@@ -313,7 +313,7 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
struct drm_minor *minor = dev_to_drm_minor(kdev);
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 val, rp_state_cap, hw_max, hw_min, non_oc_max;
+ u32 val;
ssize_t ret;
ret = kstrtou32(buf, 0, &val);
@@ -324,38 +324,34 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
mutex_lock(&dev_priv->rps.hw_lock);
- if (IS_VALLEYVIEW(dev_priv->dev)) {
+ if (IS_VALLEYVIEW(dev_priv->dev))
val = vlv_freq_opcode(dev_priv, val);
-
- hw_max = valleyview_rps_max_freq(dev_priv);
- hw_min = valleyview_rps_min_freq(dev_priv);
- non_oc_max = hw_max;
- } else {
+ else
val /= GT_FREQUENCY_MULTIPLIER;
- rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
- hw_max = dev_priv->rps.hw_max;
- non_oc_max = (rp_state_cap & 0xff);
- hw_min = ((rp_state_cap & 0xff0000) >> 16);
- }
-
- if (val < hw_min || val > hw_max ||
- val < dev_priv->rps.min_delay) {
+ if (val < dev_priv->rps.min_freq ||
+ val > dev_priv->rps.max_freq ||
+ val < dev_priv->rps.min_freq_softlimit) {
mutex_unlock(&dev_priv->rps.hw_lock);
return -EINVAL;
}
- if (val > non_oc_max)
+ if (val > dev_priv->rps.rp0_freq)
DRM_DEBUG("User requested overclocking to %d\n",
val * GT_FREQUENCY_MULTIPLIER);
- dev_priv->rps.max_delay = val;
+ dev_priv->rps.max_freq_softlimit = val;
- if (dev_priv->rps.cur_delay > val) {
+ if (dev_priv->rps.cur_freq > val) {
if (IS_VALLEYVIEW(dev))
valleyview_set_rps(dev, val);
else
gen6_set_rps(dev, val);
+ } else if (!IS_VALLEYVIEW(dev)) {
+ /* We still need gen6_set_rps to process the new max_delay and
+ * update the interrupt limits even though frequency request is
+ * unchanged. */
+ gen6_set_rps(dev, dev_priv->rps.cur_freq);
}
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -374,9 +370,9 @@ static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute
mutex_lock(&dev_priv->rps.hw_lock);
if (IS_VALLEYVIEW(dev_priv->dev))
- ret = vlv_gpu_freq(dev_priv, dev_priv->rps.min_delay);
+ ret = vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit);
else
- ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
+ ret = dev_priv->rps.min_freq_softlimit * GT_FREQUENCY_MULTIPLIER;
mutex_unlock(&dev_priv->rps.hw_lock);
return snprintf(buf, PAGE_SIZE, "%d\n", ret);
@@ -389,7 +385,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
struct drm_minor *minor = dev_to_drm_minor(kdev);
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 val, rp_state_cap, hw_max, hw_min;
+ u32 val;
ssize_t ret;
ret = kstrtou32(buf, 0, &val);
@@ -400,31 +396,30 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
mutex_lock(&dev_priv->rps.hw_lock);
- if (IS_VALLEYVIEW(dev)) {
+ if (IS_VALLEYVIEW(dev))
val = vlv_freq_opcode(dev_priv, val);
-
- hw_max = valleyview_rps_max_freq(dev_priv);
- hw_min = valleyview_rps_min_freq(dev_priv);
- } else {
+ else
val /= GT_FREQUENCY_MULTIPLIER;
- rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
- hw_max = dev_priv->rps.hw_max;
- hw_min = ((rp_state_cap & 0xff0000) >> 16);
- }
-
- if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) {
+ if (val < dev_priv->rps.min_freq ||
+ val > dev_priv->rps.max_freq ||
+ val > dev_priv->rps.max_freq_softlimit) {
mutex_unlock(&dev_priv->rps.hw_lock);
return -EINVAL;
}
- dev_priv->rps.min_delay = val;
+ dev_priv->rps.min_freq_softlimit = val;
- if (dev_priv->rps.cur_delay < val) {
+ if (dev_priv->rps.cur_freq < val) {
if (IS_VALLEYVIEW(dev))
valleyview_set_rps(dev, val);
else
gen6_set_rps(dev, val);
+ } else if (!IS_VALLEYVIEW(dev)) {
+ /* We still need gen6_set_rps to process the new min_delay and
+ * update the interrupt limits even though frequency request is
+ * unchanged. */
+ gen6_set_rps(dev, dev_priv->rps.cur_freq);
}
mutex_unlock(&dev_priv->rps.hw_lock);
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h
index 6e580c98dede..23c26f1f8b37 100644
--- a/drivers/gpu/drm/i915/i915_trace.h
+++ b/drivers/gpu/drm/i915/i915_trace.h
@@ -34,15 +34,15 @@ TRACE_EVENT(i915_gem_object_create,
);
TRACE_EVENT(i915_vma_bind,
- TP_PROTO(struct i915_vma *vma, bool mappable),
- TP_ARGS(vma, mappable),
+ TP_PROTO(struct i915_vma *vma, unsigned flags),
+ TP_ARGS(vma, flags),
TP_STRUCT__entry(
__field(struct drm_i915_gem_object *, obj)
__field(struct i915_address_space *, vm)
__field(u32, offset)
__field(u32, size)
- __field(bool, mappable)
+ __field(unsigned, flags)
),
TP_fast_assign(
@@ -50,12 +50,12 @@ TRACE_EVENT(i915_vma_bind,
__entry->vm = vma->vm;
__entry->offset = vma->node.start;
__entry->size = vma->node.size;
- __entry->mappable = mappable;
+ __entry->flags = flags;
),
TP_printk("obj=%p, offset=%08x size=%x%s vm=%p",
__entry->obj, __entry->offset, __entry->size,
- __entry->mappable ? ", mappable" : "",
+ __entry->flags & PIN_MAPPABLE ? ", mappable" : "",
__entry->vm)
);
@@ -196,26 +196,26 @@ DEFINE_EVENT(i915_gem_object, i915_gem_object_destroy,
);
TRACE_EVENT(i915_gem_evict,
- TP_PROTO(struct drm_device *dev, u32 size, u32 align, bool mappable),
- TP_ARGS(dev, size, align, mappable),
+ TP_PROTO(struct drm_device *dev, u32 size, u32 align, unsigned flags),
+ TP_ARGS(dev, size, align, flags),
TP_STRUCT__entry(
__field(u32, dev)
__field(u32, size)
__field(u32, align)
- __field(bool, mappable)
+ __field(unsigned, flags)
),
TP_fast_assign(
__entry->dev = dev->primary->index;
__entry->size = size;
__entry->align = align;
- __entry->mappable = mappable;
+ __entry->flags = flags;
),
TP_printk("dev=%d, size=%d, align=%d %s",
__entry->dev, __entry->size, __entry->align,
- __entry->mappable ? ", mappable" : "")
+ __entry->flags & PIN_MAPPABLE ? ", mappable" : "")
);
TRACE_EVENT(i915_gem_evict_everything,
@@ -238,14 +238,16 @@ TRACE_EVENT(i915_gem_evict_vm,
TP_ARGS(vm),
TP_STRUCT__entry(
+ __field(u32, dev)
__field(struct i915_address_space *, vm)
),
TP_fast_assign(
+ __entry->dev = vm->dev->primary->index;
__entry->vm = vm;
),
- TP_printk("dev=%d, vm=%p", __entry->vm->dev->primary->index, __entry->vm)
+ TP_printk("dev=%d, vm=%p", __entry->dev, __entry->vm)
);
TRACE_EVENT(i915_gem_ring_sync_to,
diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c
index caa18e855815..480da593e6c0 100644
--- a/drivers/gpu/drm/i915/i915_ums.c
+++ b/drivers/gpu/drm/i915/i915_ums.c
@@ -271,6 +271,10 @@ void i915_save_display_reg(struct drm_device *dev)
/* FIXME: regfile.save TV & SDVO state */
/* Backlight */
+ if (INTEL_INFO(dev)->gen <= 4)
+ pci_read_config_byte(dev->pdev, PCI_LBPC,
+ &dev_priv->regfile.saveLBB);
+
if (HAS_PCH_SPLIT(dev)) {
dev_priv->regfile.saveBLC_PWM_CTL = I915_READ(BLC_PWM_PCH_CTL1);
dev_priv->regfile.saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2);
@@ -293,6 +297,10 @@ void i915_restore_display_reg(struct drm_device *dev)
int i;
/* Backlight */
+ if (INTEL_INFO(dev)->gen <= 4)
+ pci_write_config_byte(dev->pdev, PCI_LBPC,
+ dev_priv->regfile.saveLBB);
+
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(BLC_PWM_PCH_CTL1, dev_priv->regfile.saveBLC_PWM_CTL);
I915_WRITE(BLC_PWM_PCH_CTL2, dev_priv->regfile.saveBLC_PWM_CTL2);
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index f22041973f3a..4867f4cc0938 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -259,7 +259,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
downclock = dvo_timing->clock;
}
- if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
+ if (downclock < panel_dvo_timing->clock && i915.lvds_downclock) {
dev_priv->lvds_downclock_avail = 1;
dev_priv->lvds_downclock = downclock * 10;
DRM_DEBUG_KMS("LVDS downclock is found in VBT. "
@@ -318,7 +318,7 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv,
struct drm_display_mode *panel_fixed_mode;
int index;
- index = i915_vbt_sdvo_panel_type;
+ index = i915.vbt_sdvo_panel_type;
if (index == -2) {
DRM_DEBUG_KMS("Ignore SDVO panel mode from BIOS VBT tables.\n");
return;
@@ -599,14 +599,14 @@ parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
{
struct bdb_mipi *mipi;
- mipi = find_section(bdb, BDB_MIPI);
+ mipi = find_section(bdb, BDB_MIPI_CONFIG);
if (!mipi) {
DRM_DEBUG_KMS("No MIPI BDB found");
return;
}
/* XXX: add more info */
- dev_priv->vbt.dsi.panel_id = mipi->panel_id;
+ dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
}
static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index 282de5e9f39d..83b7629e4367 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -104,7 +104,8 @@ struct vbios_data {
#define BDB_LVDS_LFP_DATA 42
#define BDB_LVDS_BACKLIGHT 43
#define BDB_LVDS_POWER 44
-#define BDB_MIPI 50
+#define BDB_MIPI_CONFIG 52
+#define BDB_MIPI_SEQUENCE 53
#define BDB_SKIP 254 /* VBIOS private block, ignore */
struct bdb_general_features {
@@ -711,44 +712,159 @@ int intel_parse_bios(struct drm_device *dev);
#define DVO_PORT_DPD 9
#define DVO_PORT_DPA 10
-/* MIPI DSI panel info */
-struct bdb_mipi {
- u16 panel_id;
- u16 bridge_revision;
-
- /* General params */
- u32 dithering:1;
- u32 bpp_pixel_format:1;
- u32 rsvd1:1;
- u32 dphy_valid:1;
- u32 resvd2:28;
+/* Block 52 contains MIPI Panel info
+ * 6 such enteries will there. Index into correct
+ * entery is based on the panel_index in #40 LFP
+ */
+#define MAX_MIPI_CONFIGURATIONS 6
- u16 port_info;
- u16 rsvd3:2;
- u16 num_lanes:2;
- u16 rsvd4:12;
+#define MIPI_DSI_UNDEFINED_PANEL_ID 0
+#define MIPI_DSI_GENERIC_PANEL_ID 1
- /* DSI config */
- u16 virt_ch_num:2;
- u16 vtm:2;
- u16 rsvd5:12;
+struct mipi_config {
+ u16 panel_id;
- u32 dsi_clock;
+ /* General Params */
+ u32 enable_dithering:1;
+ u32 rsvd1:1;
+ u32 is_bridge:1;
+
+ u32 panel_arch_type:2;
+ u32 is_cmd_mode:1;
+
+#define NON_BURST_SYNC_PULSE 0x1
+#define NON_BURST_SYNC_EVENTS 0x2
+#define BURST_MODE 0x3
+ u32 video_transfer_mode:2;
+
+ u32 cabc_supported:1;
+ u32 pwm_blc:1;
+
+ /* Bit 13:10 */
+#define PIXEL_FORMAT_RGB565 0x1
+#define PIXEL_FORMAT_RGB666 0x2
+#define PIXEL_FORMAT_RGB666_LOOSELY_PACKED 0x3
+#define PIXEL_FORMAT_RGB888 0x4
+ u32 videomode_color_format:4;
+
+ /* Bit 15:14 */
+#define ENABLE_ROTATION_0 0x0
+#define ENABLE_ROTATION_90 0x1
+#define ENABLE_ROTATION_180 0x2
+#define ENABLE_ROTATION_270 0x3
+ u32 rotation:2;
+ u32 bta_enabled:1;
+ u32 rsvd2:15;
+
+ /* 2 byte Port Description */
+#define DUAL_LINK_NOT_SUPPORTED 0
+#define DUAL_LINK_FRONT_BACK 1
+#define DUAL_LINK_PIXEL_ALT 2
+ u16 dual_link:2;
+ u16 lane_cnt:2;
+ u16 rsvd3:12;
+
+ u16 rsvd4;
+
+ u8 rsvd5[5];
+ u32 dsi_ddr_clk;
u32 bridge_ref_clk;
- u16 rsvd_pwr;
- /* Dphy Params */
- u32 prepare_cnt:5;
- u32 rsvd6:3;
+#define BYTE_CLK_SEL_20MHZ 0
+#define BYTE_CLK_SEL_10MHZ 1
+#define BYTE_CLK_SEL_5MHZ 2
+ u8 byte_clk_sel:2;
+
+ u8 rsvd6:6;
+
+ /* DPHY Flags */
+ u16 dphy_param_valid:1;
+ u16 eot_pkt_disabled:1;
+ u16 enable_clk_stop:1;
+ u16 rsvd7:13;
+
+ u32 hs_tx_timeout;
+ u32 lp_rx_timeout;
+ u32 turn_around_timeout;
+ u32 device_reset_timer;
+ u32 master_init_timer;
+ u32 dbi_bw_timer;
+ u32 lp_byte_clk_val;
+
+ /* 4 byte Dphy Params */
+ u32 prepare_cnt:6;
+ u32 rsvd8:2;
u32 clk_zero_cnt:8;
u32 trail_cnt:5;
- u32 rsvd7:3;
+ u32 rsvd9:3;
u32 exit_zero_cnt:6;
- u32 rsvd8:2;
+ u32 rsvd10:2;
- u32 hl_switch_cnt;
- u32 lp_byte_clk;
u32 clk_lane_switch_cnt;
+ u32 hl_switch_cnt;
+
+ u32 rsvd11[6];
+
+ /* timings based on dphy spec */
+ u8 tclk_miss;
+ u8 tclk_post;
+ u8 rsvd12;
+ u8 tclk_pre;
+ u8 tclk_prepare;
+ u8 tclk_settle;
+ u8 tclk_term_enable;
+ u8 tclk_trail;
+ u16 tclk_prepare_clkzero;
+ u8 rsvd13;
+ u8 td_term_enable;
+ u8 teot;
+ u8 ths_exit;
+ u8 ths_prepare;
+ u16 ths_prepare_hszero;
+ u8 rsvd14;
+ u8 ths_settle;
+ u8 ths_skip;
+ u8 ths_trail;
+ u8 tinit;
+ u8 tlpx;
+ u8 rsvd15[3];
+
+ /* GPIOs */
+ u8 panel_enable;
+ u8 bl_enable;
+ u8 pwm_enable;
+ u8 reset_r_n;
+ u8 pwr_down_r;
+ u8 stdby_r_n;
+
} __packed;
+/* Block 52 contains MIPI configuration block
+ * 6 * bdb_mipi_config, followed by 6 pps data
+ * block below
+ *
+ * all delays has a unit of 100us
+ */
+struct mipi_pps_data {
+ u16 panel_on_delay;
+ u16 bl_enable_delay;
+ u16 bl_disable_delay;
+ u16 panel_off_delay;
+ u16 panel_power_cycle_delay;
+};
+
+struct bdb_mipi_config {
+ struct mipi_config config[MAX_MIPI_CONFIGURATIONS];
+ struct mipi_pps_data pps[MAX_MIPI_CONFIGURATIONS];
+};
+
+/* Block 53 contains MIPI sequences as needed by the panel
+ * for enabling it. This block can be variable in size and
+ * can be maximum of 6 blocks
+ */
+struct bdb_mipi_sequence {
+ u8 version;
+ u8 data[0];
+};
+
#endif /* _I830_BIOS_H_ */
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index e2e39e65f109..aa5a3dc43342 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -68,8 +68,13 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
+ enum intel_display_power_domain power_domain;
u32 tmp;
+ power_domain = intel_display_port_power_domain(encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
tmp = I915_READ(crt->adpa_reg);
if (!(tmp & ADPA_DAC_ENABLE))
@@ -262,6 +267,10 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
if (HAS_PCH_LPT(dev))
pipe_config->pipe_bpp = 24;
+ /* FDI must always be 2.7 GHz */
+ if (HAS_DDI(dev))
+ pipe_config->port_clock = 135000 * 2;
+
return true;
}
@@ -630,14 +639,22 @@ static enum drm_connector_status
intel_crt_detect(struct drm_connector *connector, bool force)
{
struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_attached_crt(connector);
+ struct intel_encoder *intel_encoder = &crt->base;
+ enum intel_display_power_domain power_domain;
enum drm_connector_status status;
struct intel_load_detect_pipe tmp;
+ intel_runtime_pm_get(dev_priv);
+
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
connector->base.id, drm_get_connector_name(connector),
force);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
if (I915_HAS_HOTPLUG(dev)) {
/* We can not rely on the HPD pin always being correctly wired
* up, for example many KVM do not pass it through, and so
@@ -645,23 +662,30 @@ intel_crt_detect(struct drm_connector *connector, bool force)
*/
if (intel_crt_detect_hotplug(connector)) {
DRM_DEBUG_KMS("CRT detected via hotplug\n");
- return connector_status_connected;
+ status = connector_status_connected;
+ goto out;
} else
DRM_DEBUG_KMS("CRT not detected via hotplug\n");
}
- if (intel_crt_detect_ddc(connector))
- return connector_status_connected;
+ if (intel_crt_detect_ddc(connector)) {
+ status = connector_status_connected;
+ goto out;
+ }
/* Load detection is broken on HPD capable machines. Whoever wants a
* broken monitor (without edid) to work behind a broken kvm (that fails
* to have the right resistors for HP detection) needs to fix this up.
* For now just bail out. */
- if (I915_HAS_HOTPLUG(dev))
- return connector_status_disconnected;
+ if (I915_HAS_HOTPLUG(dev)) {
+ status = connector_status_disconnected;
+ goto out;
+ }
- if (!force)
- return connector->status;
+ if (!force) {
+ status = connector->status;
+ goto out;
+ }
/* for pre-945g platforms use load detect */
if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
@@ -673,6 +697,10 @@ intel_crt_detect(struct drm_connector *connector, bool force)
} else
status = connector_status_unknown;
+out:
+ intel_display_power_put(dev_priv, power_domain);
+ intel_runtime_pm_put(dev_priv);
+
return status;
}
@@ -686,17 +714,28 @@ static int intel_crt_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crt *crt = intel_attached_crt(connector);
+ struct intel_encoder *intel_encoder = &crt->base;
+ enum intel_display_power_domain power_domain;
int ret;
struct i2c_adapter *i2c;
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin);
ret = intel_crt_ddc_get_modes(connector, i2c);
if (ret || !IS_G4X(dev))
- return ret;
+ goto out;
/* Try to probe digital port for output in DVI-I -> VGA mode. */
i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB);
- return intel_crt_ddc_get_modes(connector, i2c);
+ ret = intel_crt_ddc_get_modes(connector, i2c);
+
+out:
+ intel_display_power_put(dev_priv, power_domain);
+
+ return ret;
}
static int intel_crt_set_property(struct drm_connector *connector,
@@ -765,6 +804,14 @@ static const struct dmi_system_id intel_no_crt[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
},
},
+ {
+ .callback = intel_no_crt_dmi_callback,
+ .ident = "DELL XPS 8700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 8700"),
+ },
+ },
{ }
};
@@ -800,7 +847,7 @@ void intel_crt_init(struct drm_device *dev)
intel_connector_attach_encoder(intel_connector, &crt->base);
crt->base.type = INTEL_OUTPUT_ANALOG;
- crt->base.cloneable = true;
+ crt->base.cloneable = (1 << INTEL_OUTPUT_DVO) | (1 << INTEL_OUTPUT_HDMI);
if (IS_I830(dev))
crt->base.crtc_mask = (1 << 0);
else
@@ -833,6 +880,7 @@ void intel_crt_init(struct drm_device *dev)
crt->base.get_hw_state = intel_crt_get_hw_state;
}
intel_connector->get_hw_state = intel_connector_get_hw_state;
+ intel_connector->unregister = intel_connector_unregister;
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
@@ -857,4 +905,6 @@ void intel_crt_init(struct drm_device *dev)
dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config;
}
+
+ intel_crt_reset(connector);
}
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 234ac5f7bc5a..0ad4e9600063 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -633,6 +633,97 @@ static void wrpll_update_rnp(uint64_t freq2k, unsigned budget,
/* Otherwise a < c && b >= d, do nothing */
}
+static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
+ int reg)
+{
+ int refclk = LC_FREQ;
+ int n, p, r;
+ u32 wrpll;
+
+ wrpll = I915_READ(reg);
+ switch (wrpll & SPLL_PLL_REF_MASK) {
+ case SPLL_PLL_SSC:
+ case SPLL_PLL_NON_SSC:
+ /*
+ * We could calculate spread here, but our checking
+ * code only cares about 5% accuracy, and spread is a max of
+ * 0.5% downspread.
+ */
+ refclk = 135;
+ break;
+ case SPLL_PLL_LCPLL:
+ refclk = LC_FREQ;
+ break;
+ default:
+ WARN(1, "bad wrpll refclk\n");
+ return 0;
+ }
+
+ r = wrpll & WRPLL_DIVIDER_REF_MASK;
+ p = (wrpll & WRPLL_DIVIDER_POST_MASK) >> WRPLL_DIVIDER_POST_SHIFT;
+ n = (wrpll & WRPLL_DIVIDER_FB_MASK) >> WRPLL_DIVIDER_FB_SHIFT;
+
+ /* Convert to KHz, p & r have a fixed point portion */
+ return (refclk * n * 100) / (p * r);
+}
+
+static void intel_ddi_clock_get(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
+{
+ struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ enum port port = intel_ddi_get_encoder_port(encoder);
+ int link_clock = 0;
+ u32 val, pll;
+
+ val = I915_READ(PORT_CLK_SEL(port));
+ switch (val & PORT_CLK_SEL_MASK) {
+ case PORT_CLK_SEL_LCPLL_810:
+ link_clock = 81000;
+ break;
+ case PORT_CLK_SEL_LCPLL_1350:
+ link_clock = 135000;
+ break;
+ case PORT_CLK_SEL_LCPLL_2700:
+ link_clock = 270000;
+ break;
+ case PORT_CLK_SEL_WRPLL1:
+ link_clock = intel_ddi_calc_wrpll_link(dev_priv, WRPLL_CTL1);
+ break;
+ case PORT_CLK_SEL_WRPLL2:
+ link_clock = intel_ddi_calc_wrpll_link(dev_priv, WRPLL_CTL2);
+ break;
+ case PORT_CLK_SEL_SPLL:
+ pll = I915_READ(SPLL_CTL) & SPLL_PLL_FREQ_MASK;
+ if (pll == SPLL_PLL_FREQ_810MHz)
+ link_clock = 81000;
+ else if (pll == SPLL_PLL_FREQ_1350MHz)
+ link_clock = 135000;
+ else if (pll == SPLL_PLL_FREQ_2700MHz)
+ link_clock = 270000;
+ else {
+ WARN(1, "bad spll freq\n");
+ return;
+ }
+ break;
+ default:
+ WARN(1, "bad port clock sel\n");
+ return;
+ }
+
+ pipe_config->port_clock = link_clock * 2;
+
+ if (pipe_config->has_pch_encoder)
+ pipe_config->adjusted_mode.crtc_clock =
+ intel_dotclock_calculate(pipe_config->port_clock,
+ &pipe_config->fdi_m_n);
+ else if (pipe_config->has_dp_encoder)
+ pipe_config->adjusted_mode.crtc_clock =
+ intel_dotclock_calculate(pipe_config->port_clock,
+ &pipe_config->dp_m_n);
+ else
+ pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock;
+}
+
static void
intel_ddi_calculate_wrpll(int clock /* in Hz */,
unsigned *r2_out, unsigned *n2_out, unsigned *p_out)
@@ -1017,8 +1108,13 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
enum port port = intel_ddi_get_encoder_port(intel_encoder);
enum pipe pipe = 0;
enum transcoder cpu_transcoder;
+ enum intel_display_power_domain power_domain;
uint32_t tmp;
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
if (!intel_encoder->get_hw_state(intel_encoder, &pipe))
return false;
@@ -1054,9 +1150,14 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
enum port port = intel_ddi_get_encoder_port(encoder);
+ enum intel_display_power_domain power_domain;
u32 tmp;
int i;
+ power_domain = intel_display_port_power_domain(encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
tmp = I915_READ(DDI_BUF_CTL(port));
if (!(tmp & DDI_BUF_CTL_ENABLE))
@@ -1200,7 +1301,7 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
if (type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- ironlake_edp_panel_on(intel_dp);
+ intel_edp_panel_on(intel_dp);
}
WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
@@ -1244,8 +1345,8 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
- ironlake_edp_panel_vdd_on(intel_dp);
- ironlake_edp_panel_off(intel_dp);
+ intel_edp_panel_vdd_on(intel_dp);
+ intel_edp_panel_off(intel_dp);
}
I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE);
@@ -1280,7 +1381,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
if (port == PORT_A)
intel_dp_stop_link_train(intel_dp);
- ironlake_edp_backlight_on(intel_dp);
+ intel_edp_backlight_on(intel_dp);
intel_edp_psr_enable(intel_dp);
}
@@ -1313,7 +1414,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
intel_edp_psr_disable(intel_dp);
- ironlake_edp_backlight_off(intel_dp);
+ intel_edp_backlight_off(intel_dp);
}
}
@@ -1325,7 +1426,7 @@ int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
if (lcpll & LCPLL_CD_SOURCE_FCLK) {
return 800000;
- } else if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT) {
+ } else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT) {
return 450000;
} else if (freq == LCPLL_CLK_FREQ_450) {
return 450000;
@@ -1510,6 +1611,8 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
pipe_config->pipe_bpp, dev_priv->vbt.edp_bpp);
dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp;
}
+
+ intel_ddi_clock_get(encoder, pipe_config);
}
static void intel_ddi_destroy(struct drm_encoder *encoder)
@@ -1620,7 +1723,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 0;
intel_encoder->hot_plug = intel_ddi_hot_plug;
if (init_dp)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 9b8a7c7ea7fc..dae976f51d83 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -51,7 +51,10 @@ static void ironlake_pch_clock_get(struct intel_crtc *crtc,
static int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
int x, int y, struct drm_framebuffer *old_fb);
-
+static int intel_framebuffer_init(struct drm_device *dev,
+ struct intel_framebuffer *ifb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_i915_gem_object *obj);
typedef struct {
int min, max;
@@ -738,10 +741,10 @@ bool intel_crtc_active(struct drm_crtc *crtc)
* We can ditch the adjusted_mode.crtc_clock check as soon
* as Haswell has gained clock readout/fastboot support.
*
- * We can ditch the crtc->fb check as soon as we can
+ * We can ditch the crtc->primary->fb check as soon as we can
* properly reconstruct framebuffers.
*/
- return intel_crtc->active && crtc->fb &&
+ return intel_crtc->active && crtc->primary->fb &&
intel_crtc->config.adjusted_mode.crtc_clock;
}
@@ -1030,7 +1033,7 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv,
u32 val;
/* ILK FDI PLL is always enabled */
- if (dev_priv->info->gen == 5)
+ if (INTEL_INFO(dev_priv->dev)->gen == 5)
return;
/* On Haswell, DDI ports are responsible for the FDI PLL setup */
@@ -1119,7 +1122,7 @@ void assert_pipe(struct drm_i915_private *dev_priv,
if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
state = true;
- if (!intel_display_power_enabled(dev_priv->dev,
+ if (!intel_display_power_enabled(dev_priv,
POWER_DOMAIN_TRANSCODER(cpu_transcoder))) {
cur_state = false;
} else {
@@ -1163,7 +1166,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv,
if (INTEL_INFO(dev)->gen >= 4) {
reg = DSPCNTR(pipe);
val = I915_READ(reg);
- WARN((val & DISPLAY_PLANE_ENABLE),
+ WARN(val & DISPLAY_PLANE_ENABLE,
"plane %c assertion failure, should be disabled but not\n",
plane_name(pipe));
return;
@@ -1185,27 +1188,27 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv,
enum pipe pipe)
{
struct drm_device *dev = dev_priv->dev;
- int reg, i;
+ int reg, sprite;
u32 val;
if (IS_VALLEYVIEW(dev)) {
- for (i = 0; i < dev_priv->num_plane; i++) {
- reg = SPCNTR(pipe, i);
+ for_each_sprite(pipe, sprite) {
+ reg = SPCNTR(pipe, sprite);
val = I915_READ(reg);
- WARN((val & SP_ENABLE),
+ WARN(val & SP_ENABLE,
"sprite %c assertion failure, should be off on pipe %c but is still active\n",
- sprite_name(pipe, i), pipe_name(pipe));
+ sprite_name(pipe, sprite), pipe_name(pipe));
}
} else if (INTEL_INFO(dev)->gen >= 7) {
reg = SPRCTL(pipe);
val = I915_READ(reg);
- WARN((val & SPRITE_ENABLE),
+ WARN(val & SPRITE_ENABLE,
"sprite %c assertion failure, should be off on pipe %c but is still active\n",
plane_name(pipe), pipe_name(pipe));
} else if (INTEL_INFO(dev)->gen >= 5) {
reg = DVSCNTR(pipe);
val = I915_READ(reg);
- WARN((val & DVS_ENABLE),
+ WARN(val & DVS_ENABLE,
"sprite %c assertion failure, should be off on pipe %c but is still active\n",
plane_name(pipe), pipe_name(pipe));
}
@@ -1443,7 +1446,7 @@ static void i9xx_enable_pll(struct intel_crtc *crtc)
assert_pipe_disabled(dev_priv, crtc->pipe);
/* No really, not for ILK+ */
- BUG_ON(dev_priv->info->gen >= 5);
+ BUG_ON(INTEL_INFO(dev)->gen >= 5);
/* PLL is protected by panel, make sure we can write it */
if (IS_MOBILE(dev) && !IS_I830(dev))
@@ -1549,11 +1552,12 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
*/
static void ironlake_enable_shared_dpll(struct intel_crtc *crtc)
{
- struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
/* PCH PLLs only available on ILK, SNB and IVB */
- BUG_ON(dev_priv->info->gen < 5);
+ BUG_ON(INTEL_INFO(dev)->gen < 5);
if (WARN_ON(pll == NULL))
return;
@@ -1578,11 +1582,12 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc)
static void intel_disable_shared_dpll(struct intel_crtc *crtc)
{
- struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
/* PCH only available on ILK+ */
- BUG_ON(dev_priv->info->gen < 5);
+ BUG_ON(INTEL_INFO(dev)->gen < 5);
if (WARN_ON(pll == NULL))
return;
@@ -1617,7 +1622,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
uint32_t reg, val, pipeconf_val;
/* PCH only available on ILK+ */
- BUG_ON(dev_priv->info->gen < 5);
+ BUG_ON(INTEL_INFO(dev)->gen < 5);
/* Make sure PCH DPLL is enabled */
assert_shared_dpll_enabled(dev_priv,
@@ -1670,7 +1675,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
u32 val, pipeconf_val;
/* PCH only available on ILK+ */
- BUG_ON(dev_priv->info->gen < 5);
+ BUG_ON(INTEL_INFO(dev_priv->dev)->gen < 5);
/* FDI must be feeding us bits for PCH ports */
assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder);
@@ -1744,21 +1749,16 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
/**
* intel_enable_pipe - enable a pipe, asserting requirements
- * @dev_priv: i915 private structure
- * @pipe: pipe to enable
- * @pch_port: on ILK+, is this pipe driving a PCH port or not
+ * @crtc: crtc responsible for the pipe
*
- * Enable @pipe, making sure that various hardware specific requirements
+ * Enable @crtc's pipe, making sure that various hardware specific requirements
* are met, if applicable, e.g. PLL enabled, LVDS pairs enabled, etc.
- *
- * @pipe should be %PIPE_A or %PIPE_B.
- *
- * Will wait until the pipe is actually running (i.e. first vblank) before
- * returning.
*/
-static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
- bool pch_port, bool dsi)
+static void intel_enable_pipe(struct intel_crtc *crtc)
{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum pipe pipe = crtc->pipe;
enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
pipe);
enum pipe pch_transcoder;
@@ -1780,12 +1780,12 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
* need the check.
*/
if (!HAS_PCH_SPLIT(dev_priv->dev))
- if (dsi)
+ if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DSI))
assert_dsi_pll_enabled(dev_priv);
else
assert_pll_enabled(dev_priv, pipe);
else {
- if (pch_port) {
+ if (crtc->config.has_pch_encoder) {
/* if driving the PCH, we need FDI enabled */
assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder);
assert_fdi_tx_pll_enabled(dev_priv,
@@ -1796,11 +1796,24 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
reg = PIPECONF(cpu_transcoder);
val = I915_READ(reg);
- if (val & PIPECONF_ENABLE)
+ if (val & PIPECONF_ENABLE) {
+ WARN_ON(!(pipe == PIPE_A &&
+ dev_priv->quirks & QUIRK_PIPEA_FORCE));
return;
+ }
I915_WRITE(reg, val | PIPECONF_ENABLE);
- intel_wait_for_vblank(dev_priv->dev, pipe);
+ POSTING_READ(reg);
+
+ /*
+ * There's no guarantee the pipe will really start running now. It
+ * depends on the Gen, the output type and the relative order between
+ * pipe and plane enabling. Avoid waiting on HSW+ since it's not
+ * necessary.
+ * TODO: audit the previous gens.
+ */
+ if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
+ intel_wait_for_vblank(dev_priv->dev, pipe);
}
/**
@@ -1851,22 +1864,23 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
enum plane plane)
{
- u32 reg = dev_priv->info->gen >= 4 ? DSPSURF(plane) : DSPADDR(plane);
+ struct drm_device *dev = dev_priv->dev;
+ u32 reg = INTEL_INFO(dev)->gen >= 4 ? DSPSURF(plane) : DSPADDR(plane);
I915_WRITE(reg, I915_READ(reg));
POSTING_READ(reg);
}
/**
- * intel_enable_primary_plane - enable the primary plane on a given pipe
+ * intel_enable_primary_hw_plane - enable the primary plane on a given pipe
* @dev_priv: i915 private structure
* @plane: plane to enable
* @pipe: pipe being fed
*
* Enable @plane on @pipe, making sure that @pipe is running first.
*/
-static void intel_enable_primary_plane(struct drm_i915_private *dev_priv,
- enum plane plane, enum pipe pipe)
+static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv,
+ enum plane plane, enum pipe pipe)
{
struct intel_crtc *intel_crtc =
to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
@@ -1891,15 +1905,15 @@ static void intel_enable_primary_plane(struct drm_i915_private *dev_priv,
}
/**
- * intel_disable_primary_plane - disable the primary plane
+ * intel_disable_primary_hw_plane - disable the primary hardware plane
* @dev_priv: i915 private structure
* @plane: plane to disable
* @pipe: pipe consuming the data
*
* Disable @plane; should be an independent operation.
*/
-static void intel_disable_primary_plane(struct drm_i915_private *dev_priv,
- enum plane plane, enum pipe pipe)
+static void intel_disable_primary_hw_plane(struct drm_i915_private *dev_priv,
+ enum plane plane, enum pipe pipe)
{
struct intel_crtc *intel_crtc =
to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
@@ -1929,6 +1943,14 @@ static bool need_vtd_wa(struct drm_device *dev)
return false;
}
+static int intel_align_height(struct drm_device *dev, int height, bool tiled)
+{
+ int tile_height;
+
+ tile_height = tiled ? (IS_GEN2(dev) ? 16 : 8) : 1;
+ return ALIGN(height, tile_height);
+}
+
int
intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
@@ -2025,8 +2047,114 @@ unsigned long intel_gen4_compute_page_offset(int *x, int *y,
}
}
-static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
- int x, int y)
+int intel_format_to_fourcc(int format)
+{
+ switch (format) {
+ case DISPPLANE_8BPP:
+ return DRM_FORMAT_C8;
+ case DISPPLANE_BGRX555:
+ return DRM_FORMAT_XRGB1555;
+ case DISPPLANE_BGRX565:
+ return DRM_FORMAT_RGB565;
+ default:
+ case DISPPLANE_BGRX888:
+ return DRM_FORMAT_XRGB8888;
+ case DISPPLANE_RGBX888:
+ return DRM_FORMAT_XBGR8888;
+ case DISPPLANE_BGRX101010:
+ return DRM_FORMAT_XRGB2101010;
+ case DISPPLANE_RGBX101010:
+ return DRM_FORMAT_XBGR2101010;
+ }
+}
+
+static bool intel_alloc_plane_obj(struct intel_crtc *crtc,
+ struct intel_plane_config *plane_config)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_gem_object *obj = NULL;
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ u32 base = plane_config->base;
+
+ if (plane_config->size == 0)
+ return false;
+
+ obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base,
+ plane_config->size);
+ if (!obj)
+ return false;
+
+ if (plane_config->tiled) {
+ obj->tiling_mode = I915_TILING_X;
+ obj->stride = crtc->base.primary->fb->pitches[0];
+ }
+
+ mode_cmd.pixel_format = crtc->base.primary->fb->pixel_format;
+ mode_cmd.width = crtc->base.primary->fb->width;
+ mode_cmd.height = crtc->base.primary->fb->height;
+ mode_cmd.pitches[0] = crtc->base.primary->fb->pitches[0];
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (intel_framebuffer_init(dev, to_intel_framebuffer(crtc->base.primary->fb),
+ &mode_cmd, obj)) {
+ DRM_DEBUG_KMS("intel fb init failed\n");
+ goto out_unref_obj;
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ DRM_DEBUG_KMS("plane fb obj %p\n", obj);
+ return true;
+
+out_unref_obj:
+ drm_gem_object_unreference(&obj->base);
+ mutex_unlock(&dev->struct_mutex);
+ return false;
+}
+
+static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
+ struct intel_plane_config *plane_config)
+{
+ struct drm_device *dev = intel_crtc->base.dev;
+ struct drm_crtc *c;
+ struct intel_crtc *i;
+ struct intel_framebuffer *fb;
+
+ if (!intel_crtc->base.primary->fb)
+ return;
+
+ if (intel_alloc_plane_obj(intel_crtc, plane_config))
+ return;
+
+ kfree(intel_crtc->base.primary->fb);
+ intel_crtc->base.primary->fb = NULL;
+
+ /*
+ * Failed to alloc the obj, check to see if we should share
+ * an fb with another CRTC instead
+ */
+ list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+ i = to_intel_crtc(c);
+
+ if (c == &intel_crtc->base)
+ continue;
+
+ if (!i->active || !c->primary->fb)
+ continue;
+
+ fb = to_intel_framebuffer(c->primary->fb);
+ if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) {
+ drm_framebuffer_reference(c->primary->fb);
+ intel_crtc->base.primary->fb = c->primary->fb;
+ break;
+ }
+ }
+}
+
+static int i9xx_update_primary_plane(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2125,8 +2253,9 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
return 0;
}
-static int ironlake_update_plane(struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int x, int y)
+static int ironlake_update_primary_plane(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2230,7 +2359,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
dev_priv->display.disable_fbc(dev);
intel_increase_pllclock(crtc);
- return dev_priv->display.update_plane(crtc, fb, x, y);
+ return dev_priv->display.update_primary_plane(crtc, fb, x, y);
}
void intel_display_handle_reset(struct drm_device *dev)
@@ -2267,11 +2396,13 @@ void intel_display_handle_reset(struct drm_device *dev)
/*
* FIXME: Once we have proper support for primary planes (and
* disabling them without disabling the entire crtc) allow again
- * a NULL crtc->fb.
+ * a NULL crtc->primary->fb.
*/
- if (intel_crtc->active && crtc->fb)
- dev_priv->display.update_plane(crtc, crtc->fb,
- crtc->x, crtc->y);
+ if (intel_crtc->active && crtc->primary->fb)
+ dev_priv->display.update_primary_plane(crtc,
+ crtc->primary->fb,
+ crtc->x,
+ crtc->y);
mutex_unlock(&crtc->mutex);
}
}
@@ -2299,31 +2430,23 @@ intel_finish_fb(struct drm_framebuffer *old_fb)
return ret;
}
-static void intel_crtc_update_sarea_pos(struct drm_crtc *crtc, int x, int y)
+static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_master_private *master_priv;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ unsigned long flags;
+ bool pending;
- if (!dev->primary->master)
- return;
+ if (i915_reset_in_progress(&dev_priv->gpu_error) ||
+ intel_crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
+ return false;
- master_priv = dev->primary->master->driver_priv;
- if (!master_priv->sarea_priv)
- return;
+ spin_lock_irqsave(&dev->event_lock, flags);
+ pending = to_intel_crtc(crtc)->unpin_work != NULL;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
- switch (intel_crtc->pipe) {
- case 0:
- master_priv->sarea_priv->pipeA_x = x;
- master_priv->sarea_priv->pipeA_y = y;
- break;
- case 1:
- master_priv->sarea_priv->pipeB_x = x;
- master_priv->sarea_priv->pipeB_y = y;
- break;
- default:
- break;
- }
+ return pending;
}
static int
@@ -2336,6 +2459,11 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb;
int ret;
+ if (intel_crtc_has_pending_flip(crtc)) {
+ DRM_ERROR("pipe is still busy with an old pageflip\n");
+ return -EBUSY;
+ }
+
/* no fb bound */
if (!fb) {
DRM_ERROR("No FB bound\n");
@@ -2353,8 +2481,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
ret = intel_pin_and_fence_fb_obj(dev,
to_intel_framebuffer(fb)->obj,
NULL);
+ mutex_unlock(&dev->struct_mutex);
if (ret != 0) {
- mutex_unlock(&dev->struct_mutex);
DRM_ERROR("pin & fence failed\n");
return ret;
}
@@ -2372,7 +2500,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
* whether the platform allows pfit disable with pipe active, and only
* then update the pipesrc and pfit state, even on the flip path.
*/
- if (i915_fastboot) {
+ if (i915.fastboot) {
const struct drm_display_mode *adjusted_mode =
&intel_crtc->config.adjusted_mode;
@@ -2390,31 +2518,33 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
intel_crtc->config.pipe_src_h = adjusted_mode->crtc_vdisplay;
}
- ret = dev_priv->display.update_plane(crtc, fb, x, y);
+ ret = dev_priv->display.update_primary_plane(crtc, fb, x, y);
if (ret) {
+ mutex_lock(&dev->struct_mutex);
intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj);
mutex_unlock(&dev->struct_mutex);
DRM_ERROR("failed to update base address\n");
return ret;
}
- old_fb = crtc->fb;
- crtc->fb = fb;
+ old_fb = crtc->primary->fb;
+ crtc->primary->fb = fb;
crtc->x = x;
crtc->y = y;
if (old_fb) {
if (intel_crtc->active && old_fb != fb)
intel_wait_for_vblank(dev, intel_crtc->pipe);
+ mutex_lock(&dev->struct_mutex);
intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
+ mutex_unlock(&dev->struct_mutex);
}
+ mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
intel_edp_psr_update(dev);
mutex_unlock(&dev->struct_mutex);
- intel_crtc_update_sarea_pos(crtc, x, y);
-
return 0;
}
@@ -2963,25 +3093,6 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
udelay(100);
}
-static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- unsigned long flags;
- bool pending;
-
- if (i915_reset_in_progress(&dev_priv->gpu_error) ||
- intel_crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
- return false;
-
- spin_lock_irqsave(&dev->event_lock, flags);
- pending = to_intel_crtc(crtc)->unpin_work != NULL;
- spin_unlock_irqrestore(&dev->event_lock, flags);
-
- return pending;
-}
-
bool intel_has_pending_fb_unpin(struct drm_device *dev)
{
struct intel_crtc *crtc;
@@ -3011,7 +3122,7 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (crtc->fb == NULL)
+ if (crtc->primary->fb == NULL)
return;
WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue));
@@ -3020,7 +3131,7 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
!intel_crtc_has_pending_flip(crtc));
mutex_lock(&dev->struct_mutex);
- intel_finish_fb(crtc->fb);
+ intel_finish_fb(crtc->primary->fb);
mutex_unlock(&dev->struct_mutex);
}
@@ -3425,22 +3536,28 @@ static void intel_enable_planes(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
enum pipe pipe = to_intel_crtc(crtc)->pipe;
+ struct drm_plane *plane;
struct intel_plane *intel_plane;
- list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head)
+ drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
+ intel_plane = to_intel_plane(plane);
if (intel_plane->pipe == pipe)
intel_plane_restore(&intel_plane->base);
+ }
}
static void intel_disable_planes(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
enum pipe pipe = to_intel_crtc(crtc)->pipe;
+ struct drm_plane *plane;
struct intel_plane *intel_plane;
- list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head)
+ drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
+ intel_plane = to_intel_plane(plane);
if (intel_plane->pipe == pipe)
intel_plane_disable(&intel_plane->base);
+ }
}
void hsw_enable_ips(struct intel_crtc *crtc)
@@ -3587,9 +3704,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
intel_crtc_load_lut(crtc);
intel_update_watermarks(crtc);
- intel_enable_pipe(dev_priv, pipe,
- intel_crtc->config.has_pch_encoder, false);
- intel_enable_primary_plane(dev_priv, plane, pipe);
+ intel_enable_pipe(intel_crtc);
+ intel_enable_primary_hw_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
intel_crtc_update_cursor(crtc, true);
@@ -3631,7 +3747,7 @@ static void haswell_crtc_enable_planes(struct drm_crtc *crtc)
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
- intel_enable_primary_plane(dev_priv, plane, pipe);
+ intel_enable_primary_hw_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
intel_crtc_update_cursor(crtc, true);
@@ -3661,7 +3777,7 @@ static void haswell_crtc_disable_planes(struct drm_crtc *crtc)
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_primary_plane(dev_priv, plane, pipe);
+ intel_disable_primary_hw_plane(dev_priv, plane, pipe);
}
/*
@@ -3733,8 +3849,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
intel_ddi_enable_transcoder_func(crtc);
intel_update_watermarks(crtc);
- intel_enable_pipe(dev_priv, pipe,
- intel_crtc->config.has_pch_encoder, false);
+ intel_enable_pipe(intel_crtc);
if (intel_crtc->config.has_pch_encoder)
lpt_pch_enable(crtc);
@@ -3748,16 +3863,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
* to change the workaround. */
haswell_mode_set_planes_workaround(intel_crtc);
haswell_crtc_enable_planes(crtc);
-
- /*
- * There seems to be a race in PCH platform hw (at least on some
- * outputs) where an enabled pipe still completes any pageflip right
- * away (as if the pipe is off) instead of waiting for vblank. As soon
- * as the first vblank happend, everything works as expected. Hence just
- * wait for one vblank before returning to avoid strange things
- * happening.
- */
- intel_wait_for_vblank(dev, intel_crtc->pipe);
}
static void ironlake_pfit_disable(struct intel_crtc *crtc)
@@ -3800,7 +3905,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_primary_plane(dev_priv, plane, pipe);
+ intel_disable_primary_hw_plane(dev_priv, plane, pipe);
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
@@ -3972,6 +4077,117 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc)
I915_WRITE(BCLRPAT(crtc->pipe), 0);
}
+#define for_each_power_domain(domain, mask) \
+ for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \
+ if ((1 << (domain)) & (mask))
+
+enum intel_display_power_domain
+intel_display_port_power_domain(struct intel_encoder *intel_encoder)
+{
+ struct drm_device *dev = intel_encoder->base.dev;
+ struct intel_digital_port *intel_dig_port;
+
+ switch (intel_encoder->type) {
+ case INTEL_OUTPUT_UNKNOWN:
+ /* Only DDI platforms should ever use this output type */
+ WARN_ON_ONCE(!HAS_DDI(dev));
+ case INTEL_OUTPUT_DISPLAYPORT:
+ case INTEL_OUTPUT_HDMI:
+ case INTEL_OUTPUT_EDP:
+ intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+ switch (intel_dig_port->port) {
+ case PORT_A:
+ return POWER_DOMAIN_PORT_DDI_A_4_LANES;
+ case PORT_B:
+ return POWER_DOMAIN_PORT_DDI_B_4_LANES;
+ case PORT_C:
+ return POWER_DOMAIN_PORT_DDI_C_4_LANES;
+ case PORT_D:
+ return POWER_DOMAIN_PORT_DDI_D_4_LANES;
+ default:
+ WARN_ON_ONCE(1);
+ return POWER_DOMAIN_PORT_OTHER;
+ }
+ case INTEL_OUTPUT_ANALOG:
+ return POWER_DOMAIN_PORT_CRT;
+ case INTEL_OUTPUT_DSI:
+ return POWER_DOMAIN_PORT_DSI;
+ default:
+ return POWER_DOMAIN_PORT_OTHER;
+ }
+}
+
+static unsigned long get_crtc_power_domains(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct intel_encoder *intel_encoder;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ enum pipe pipe = intel_crtc->pipe;
+ bool pfit_enabled = intel_crtc->config.pch_pfit.enabled;
+ unsigned long mask;
+ enum transcoder transcoder;
+
+ transcoder = intel_pipe_to_cpu_transcoder(dev->dev_private, pipe);
+
+ mask = BIT(POWER_DOMAIN_PIPE(pipe));
+ mask |= BIT(POWER_DOMAIN_TRANSCODER(transcoder));
+ if (pfit_enabled)
+ mask |= BIT(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe));
+
+ for_each_encoder_on_crtc(dev, crtc, intel_encoder)
+ mask |= BIT(intel_display_port_power_domain(intel_encoder));
+
+ return mask;
+}
+
+void intel_display_set_init_power(struct drm_i915_private *dev_priv,
+ bool enable)
+{
+ if (dev_priv->power_domains.init_power_on == enable)
+ return;
+
+ if (enable)
+ intel_display_power_get(dev_priv, POWER_DOMAIN_INIT);
+ else
+ intel_display_power_put(dev_priv, POWER_DOMAIN_INIT);
+
+ dev_priv->power_domains.init_power_on = enable;
+}
+
+static void modeset_update_crtc_power_domains(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long pipe_domains[I915_MAX_PIPES] = { 0, };
+ struct intel_crtc *crtc;
+
+ /*
+ * First get all needed power domains, then put all unneeded, to avoid
+ * any unnecessary toggling of the power wells.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ enum intel_display_power_domain domain;
+
+ if (!crtc->base.enabled)
+ continue;
+
+ pipe_domains[crtc->pipe] = get_crtc_power_domains(&crtc->base);
+
+ for_each_power_domain(domain, pipe_domains[crtc->pipe])
+ intel_display_power_get(dev_priv, domain);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ enum intel_display_power_domain domain;
+
+ for_each_power_domain(domain, crtc->enabled_power_domains)
+ intel_display_power_put(dev_priv, domain);
+
+ crtc->enabled_power_domains = pipe_domains[crtc->pipe];
+ }
+
+ intel_display_set_init_power(dev_priv, false);
+}
+
int valleyview_get_vco(struct drm_i915_private *dev_priv)
{
int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
@@ -4088,9 +4304,8 @@ static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv,
/* Looks like the 200MHz CDclk freq doesn't work on some configs */
}
-static int intel_mode_max_pixclk(struct drm_i915_private *dev_priv,
- unsigned modeset_pipes,
- struct intel_crtc_config *pipe_config)
+/* compute the max pixel clock for new configuration */
+static int intel_mode_max_pixclk(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
struct intel_crtc *intel_crtc;
@@ -4098,31 +4313,26 @@ static int intel_mode_max_pixclk(struct drm_i915_private *dev_priv,
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
base.head) {
- if (modeset_pipes & (1 << intel_crtc->pipe))
- max_pixclk = max(max_pixclk,
- pipe_config->adjusted_mode.crtc_clock);
- else if (intel_crtc->base.enabled)
+ if (intel_crtc->new_enabled)
max_pixclk = max(max_pixclk,
- intel_crtc->config.adjusted_mode.crtc_clock);
+ intel_crtc->new_config->adjusted_mode.crtc_clock);
}
return max_pixclk;
}
static void valleyview_modeset_global_pipes(struct drm_device *dev,
- unsigned *prepare_pipes,
- unsigned modeset_pipes,
- struct intel_crtc_config *pipe_config)
+ unsigned *prepare_pipes)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc;
- int max_pixclk = intel_mode_max_pixclk(dev_priv, modeset_pipes,
- pipe_config);
+ int max_pixclk = intel_mode_max_pixclk(dev_priv);
int cur_cdclk = valleyview_cur_cdclk(dev_priv);
if (valleyview_calc_cdclk(dev_priv, max_pixclk) == cur_cdclk)
return;
+ /* disable/enable all currently active pipes while we change cdclk */
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
base.head)
if (intel_crtc->base.enabled)
@@ -4132,12 +4342,13 @@ static void valleyview_modeset_global_pipes(struct drm_device *dev,
static void valleyview_modeset_global_resources(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int max_pixclk = intel_mode_max_pixclk(dev_priv, 0, NULL);
+ int max_pixclk = intel_mode_max_pixclk(dev_priv);
int cur_cdclk = valleyview_cur_cdclk(dev_priv);
int req_cdclk = valleyview_calc_cdclk(dev_priv, max_pixclk);
if (req_cdclk != cur_cdclk)
valleyview_set_cdclk(dev, req_cdclk);
+ modeset_update_crtc_power_domains(dev);
}
static void valleyview_crtc_enable(struct drm_crtc *crtc)
@@ -4175,8 +4386,9 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
intel_crtc_load_lut(crtc);
intel_update_watermarks(crtc);
- intel_enable_pipe(dev_priv, pipe, false, is_dsi);
- intel_enable_primary_plane(dev_priv, plane, pipe);
+ intel_enable_pipe(intel_crtc);
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+ intel_enable_primary_hw_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
intel_crtc_update_cursor(crtc, true);
@@ -4213,8 +4425,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
intel_crtc_load_lut(crtc);
intel_update_watermarks(crtc);
- intel_enable_pipe(dev_priv, pipe, false, false);
- intel_enable_primary_plane(dev_priv, plane, pipe);
+ intel_enable_pipe(intel_crtc);
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+ intel_enable_primary_hw_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
/* The fixup needs to happen before cursor is enabled */
if (IS_G4X(dev))
@@ -4270,8 +4483,9 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
intel_crtc_dpms_overlay(intel_crtc, false);
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_primary_plane(dev_priv, plane, pipe);
+ intel_disable_primary_hw_plane(dev_priv, plane, pipe);
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
intel_disable_pipe(dev_priv, pipe);
i9xx_pfit_disable(intel_crtc);
@@ -4365,11 +4579,11 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
assert_cursor_disabled(dev_priv, to_intel_crtc(crtc)->pipe);
assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe);
- if (crtc->fb) {
+ if (crtc->primary->fb) {
mutex_lock(&dev->struct_mutex);
- intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj);
+ intel_unpin_fb_obj(to_intel_framebuffer(crtc->primary->fb)->obj);
mutex_unlock(&dev->struct_mutex);
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
}
/* Update computed state. */
@@ -4583,7 +4797,7 @@ retry:
static void hsw_compute_ips_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config)
{
- pipe_config->ips_enabled = i915_enable_ips &&
+ pipe_config->ips_enabled = i915.enable_ips &&
hsw_crtc_supports_ips(crtc) &&
pipe_config->pipe_bpp <= 24;
}
@@ -4784,8 +4998,8 @@ intel_link_compute_m_n(int bits_per_pixel, int nlanes,
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
{
- if (i915_panel_use_ssc >= 0)
- return i915_panel_use_ssc != 0;
+ if (i915.panel_use_ssc >= 0)
+ return i915.panel_use_ssc != 0;
return dev_priv->vbt.lvds_use_ssc
&& !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
}
@@ -4844,7 +5058,7 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
crtc->lowfreq_avail = false;
if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) &&
- reduced_clock && i915_powersave) {
+ reduced_clock && i915.powersave) {
I915_WRITE(FP1(pipe), fp2);
crtc->config.dpll_hw_state.fp1 = fp2;
crtc->lowfreq_avail = true;
@@ -5161,21 +5375,26 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc)
enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
struct drm_display_mode *adjusted_mode =
&intel_crtc->config.adjusted_mode;
- uint32_t vsyncshift, crtc_vtotal, crtc_vblank_end;
+ uint32_t crtc_vtotal, crtc_vblank_end;
+ int vsyncshift = 0;
/* We need to be careful not to changed the adjusted mode, for otherwise
* the hw state checker will get angry at the mismatch. */
crtc_vtotal = adjusted_mode->crtc_vtotal;
crtc_vblank_end = adjusted_mode->crtc_vblank_end;
- if (!IS_GEN2(dev) && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
/* the chip adds 2 halflines automatically */
crtc_vtotal -= 1;
crtc_vblank_end -= 1;
- vsyncshift = adjusted_mode->crtc_hsync_start
- - adjusted_mode->crtc_htotal / 2;
- } else {
- vsyncshift = 0;
+
+ if (intel_pipe_has_type(&intel_crtc->base, INTEL_OUTPUT_SDVO))
+ vsyncshift = (adjusted_mode->crtc_htotal - 1) / 2;
+ else
+ vsyncshift = adjusted_mode->crtc_hsync_start -
+ adjusted_mode->crtc_htotal / 2;
+ if (vsyncshift < 0)
+ vsyncshift += adjusted_mode->crtc_htotal;
}
if (INTEL_INFO(dev)->gen > 3)
@@ -5259,25 +5478,23 @@ static void intel_get_pipe_timings(struct intel_crtc *crtc,
pipe_config->requested_mode.hdisplay = pipe_config->pipe_src_w;
}
-static void intel_crtc_mode_from_pipe_config(struct intel_crtc *intel_crtc,
- struct intel_crtc_config *pipe_config)
+void intel_mode_from_pipe_config(struct drm_display_mode *mode,
+ struct intel_crtc_config *pipe_config)
{
- struct drm_crtc *crtc = &intel_crtc->base;
-
- crtc->mode.hdisplay = pipe_config->adjusted_mode.crtc_hdisplay;
- crtc->mode.htotal = pipe_config->adjusted_mode.crtc_htotal;
- crtc->mode.hsync_start = pipe_config->adjusted_mode.crtc_hsync_start;
- crtc->mode.hsync_end = pipe_config->adjusted_mode.crtc_hsync_end;
+ mode->hdisplay = pipe_config->adjusted_mode.crtc_hdisplay;
+ mode->htotal = pipe_config->adjusted_mode.crtc_htotal;
+ mode->hsync_start = pipe_config->adjusted_mode.crtc_hsync_start;
+ mode->hsync_end = pipe_config->adjusted_mode.crtc_hsync_end;
- crtc->mode.vdisplay = pipe_config->adjusted_mode.crtc_vdisplay;
- crtc->mode.vtotal = pipe_config->adjusted_mode.crtc_vtotal;
- crtc->mode.vsync_start = pipe_config->adjusted_mode.crtc_vsync_start;
- crtc->mode.vsync_end = pipe_config->adjusted_mode.crtc_vsync_end;
+ mode->vdisplay = pipe_config->adjusted_mode.crtc_vdisplay;
+ mode->vtotal = pipe_config->adjusted_mode.crtc_vtotal;
+ mode->vsync_start = pipe_config->adjusted_mode.crtc_vsync_start;
+ mode->vsync_end = pipe_config->adjusted_mode.crtc_vsync_end;
- crtc->mode.flags = pipe_config->adjusted_mode.flags;
+ mode->flags = pipe_config->adjusted_mode.flags;
- crtc->mode.clock = pipe_config->adjusted_mode.crtc_clock;
- crtc->mode.flags |= pipe_config->adjusted_mode.flags;
+ mode->clock = pipe_config->adjusted_mode.crtc_clock;
+ mode->flags |= pipe_config->adjusted_mode.flags;
}
static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
@@ -5327,10 +5544,13 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
}
}
- if (!IS_GEN2(dev) &&
- intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
- pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
- else
+ if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) {
+ if (INTEL_INFO(dev)->gen < 4 ||
+ intel_pipe_has_type(&intel_crtc->base, INTEL_OUTPUT_SDVO))
+ pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
+ else
+ pipeconf |= PIPECONF_INTERLACE_W_SYNC_SHIFT;
+ } else
pipeconf |= PIPECONF_PROGRESSIVE;
if (IS_VALLEYVIEW(dev) && intel_crtc->config.limited_color_range)
@@ -5512,6 +5732,67 @@ static void vlv_crtc_clock_get(struct intel_crtc *crtc,
pipe_config->port_clock = clock.dot / 5;
}
+static void i9xx_get_plane_config(struct intel_crtc *crtc,
+ struct intel_plane_config *plane_config)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val, base, offset;
+ int pipe = crtc->pipe, plane = crtc->plane;
+ int fourcc, pixel_format;
+ int aligned_height;
+
+ crtc->base.primary->fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
+ if (!crtc->base.primary->fb) {
+ DRM_DEBUG_KMS("failed to alloc fb\n");
+ return;
+ }
+
+ val = I915_READ(DSPCNTR(plane));
+
+ if (INTEL_INFO(dev)->gen >= 4)
+ if (val & DISPPLANE_TILED)
+ plane_config->tiled = true;
+
+ pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
+ fourcc = intel_format_to_fourcc(pixel_format);
+ crtc->base.primary->fb->pixel_format = fourcc;
+ crtc->base.primary->fb->bits_per_pixel =
+ drm_format_plane_cpp(fourcc, 0) * 8;
+
+ if (INTEL_INFO(dev)->gen >= 4) {
+ if (plane_config->tiled)
+ offset = I915_READ(DSPTILEOFF(plane));
+ else
+ offset = I915_READ(DSPLINOFF(plane));
+ base = I915_READ(DSPSURF(plane)) & 0xfffff000;
+ } else {
+ base = I915_READ(DSPADDR(plane));
+ }
+ plane_config->base = base;
+
+ val = I915_READ(PIPESRC(pipe));
+ crtc->base.primary->fb->width = ((val >> 16) & 0xfff) + 1;
+ crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1;
+
+ val = I915_READ(DSPSTRIDE(pipe));
+ crtc->base.primary->fb->pitches[0] = val & 0xffffff80;
+
+ aligned_height = intel_align_height(dev, crtc->base.primary->fb->height,
+ plane_config->tiled);
+
+ plane_config->size = ALIGN(crtc->base.primary->fb->pitches[0] *
+ aligned_height, PAGE_SIZE);
+
+ DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
+ pipe, plane, crtc->base.primary->fb->width,
+ crtc->base.primary->fb->height,
+ crtc->base.primary->fb->bits_per_pixel, base,
+ crtc->base.primary->fb->pitches[0],
+ plane_config->size);
+
+}
+
static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config)
{
@@ -5519,6 +5800,10 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t tmp;
+ if (!intel_display_power_enabled(dev_priv,
+ POWER_DOMAIN_PIPE(crtc->pipe)))
+ return false;
+
pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
pipe_config->shared_dpll = DPLL_ID_PRIVATE;
@@ -6180,7 +6465,7 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp)
* is 2.5%; use 5% for safety's sake.
*/
u32 bps = target_clock * bpp * 21 / 20;
- return bps / (link_bw * 8) + 1;
+ return DIV_ROUND_UP(bps, link_bw * 8);
}
static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor)
@@ -6348,7 +6633,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
if (intel_crtc->config.has_dp_encoder)
intel_dp_set_m_n(intel_crtc);
- if (is_lvds && has_reduced_clock && i915_powersave)
+ if (is_lvds && has_reduced_clock && i915.powersave)
intel_crtc->lowfreq_avail = true;
else
intel_crtc->lowfreq_avail = false;
@@ -6455,6 +6740,66 @@ static void ironlake_get_pfit_config(struct intel_crtc *crtc,
}
}
+static void ironlake_get_plane_config(struct intel_crtc *crtc,
+ struct intel_plane_config *plane_config)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val, base, offset;
+ int pipe = crtc->pipe, plane = crtc->plane;
+ int fourcc, pixel_format;
+ int aligned_height;
+
+ crtc->base.primary->fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
+ if (!crtc->base.primary->fb) {
+ DRM_DEBUG_KMS("failed to alloc fb\n");
+ return;
+ }
+
+ val = I915_READ(DSPCNTR(plane));
+
+ if (INTEL_INFO(dev)->gen >= 4)
+ if (val & DISPPLANE_TILED)
+ plane_config->tiled = true;
+
+ pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
+ fourcc = intel_format_to_fourcc(pixel_format);
+ crtc->base.primary->fb->pixel_format = fourcc;
+ crtc->base.primary->fb->bits_per_pixel =
+ drm_format_plane_cpp(fourcc, 0) * 8;
+
+ base = I915_READ(DSPSURF(plane)) & 0xfffff000;
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+ offset = I915_READ(DSPOFFSET(plane));
+ } else {
+ if (plane_config->tiled)
+ offset = I915_READ(DSPTILEOFF(plane));
+ else
+ offset = I915_READ(DSPLINOFF(plane));
+ }
+ plane_config->base = base;
+
+ val = I915_READ(PIPESRC(pipe));
+ crtc->base.primary->fb->width = ((val >> 16) & 0xfff) + 1;
+ crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1;
+
+ val = I915_READ(DSPSTRIDE(pipe));
+ crtc->base.primary->fb->pitches[0] = val & 0xffffff80;
+
+ aligned_height = intel_align_height(dev, crtc->base.primary->fb->height,
+ plane_config->tiled);
+
+ plane_config->size = ALIGN(crtc->base.primary->fb->pitches[0] *
+ aligned_height, PAGE_SIZE);
+
+ DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
+ pipe, plane, crtc->base.primary->fb->width,
+ crtc->base.primary->fb->height,
+ crtc->base.primary->fb->bits_per_pixel, base,
+ crtc->base.primary->fb->pitches[0],
+ plane_config->size);
+}
+
static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config)
{
@@ -6629,6 +6974,7 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
{
uint32_t val;
+ unsigned long irqflags;
val = I915_READ(LCPLL_CTL);
@@ -6636,9 +6982,22 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
LCPLL_POWER_DOWN_ALLOW)) == LCPLL_PLL_LOCK)
return;
- /* Make sure we're not on PC8 state before disabling PC8, otherwise
- * we'll hang the machine! */
- gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
+ /*
+ * Make sure we're not on PC8 state before disabling PC8, otherwise
+ * we'll hang the machine. To prevent PC8 state, just enable force_wake.
+ *
+ * The other problem is that hsw_restore_lcpll() is called as part of
+ * the runtime PM resume sequence, so we can't just call
+ * gen6_gt_force_wake_get() because that function calls
+ * intel_runtime_pm_get(), and we can't change the runtime PM refcount
+ * while we are on the resume sequence. So to solve this problem we have
+ * to call special forcewake code that doesn't touch runtime PM and
+ * doesn't enable the forcewake delayed work.
+ */
+ spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+ if (dev_priv->uncore.forcewake_count++ == 0)
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, FORCEWAKE_ALL);
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
if (val & LCPLL_POWER_DOWN_ALLOW) {
val &= ~LCPLL_POWER_DOWN_ALLOW;
@@ -6672,26 +7031,45 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
DRM_ERROR("Switching back to LCPLL failed\n");
}
- gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
+ /* See the big comment above. */
+ spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+ if (--dev_priv->uncore.forcewake_count == 0)
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, FORCEWAKE_ALL);
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
-void hsw_enable_pc8_work(struct work_struct *__work)
+/*
+ * Package states C8 and deeper are really deep PC states that can only be
+ * reached when all the devices on the system allow it, so even if the graphics
+ * device allows PC8+, it doesn't mean the system will actually get to these
+ * states. Our driver only allows PC8+ when going into runtime PM.
+ *
+ * The requirements for PC8+ are that all the outputs are disabled, the power
+ * well is disabled and most interrupts are disabled, and these are also
+ * requirements for runtime PM. When these conditions are met, we manually do
+ * the other conditions: disable the interrupts, clocks and switch LCPLL refclk
+ * to Fclk. If we're in PC8+ and we get an non-hotplug interrupt, we can hard
+ * hang the machine.
+ *
+ * When we really reach PC8 or deeper states (not just when we allow it) we lose
+ * the state of some registers, so when we come back from PC8+ we need to
+ * restore this state. We don't get into PC8+ if we're not in RC6, so we don't
+ * need to take care of the registers kept by RC6. Notice that this happens even
+ * if we don't put the device in PCI D3 state (which is what currently happens
+ * because of the runtime PM support).
+ *
+ * For more, read "Display Sequences for Package C8" on the hardware
+ * documentation.
+ */
+void hsw_enable_pc8(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv =
- container_of(to_delayed_work(__work), struct drm_i915_private,
- pc8.enable_work);
struct drm_device *dev = dev_priv->dev;
uint32_t val;
WARN_ON(!HAS_PC8(dev));
- if (dev_priv->pc8.enabled)
- return;
-
DRM_DEBUG_KMS("Enabling package C8+\n");
- dev_priv->pc8.enabled = true;
-
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
val = I915_READ(SOUTH_DSPCLK_GATE_D);
val &= ~PCH_LP_PARTITION_LEVEL_DISABLE;
@@ -6699,51 +7077,21 @@ void hsw_enable_pc8_work(struct work_struct *__work)
}
lpt_disable_clkout_dp(dev);
- hsw_pc8_disable_interrupts(dev);
+ hsw_runtime_pm_disable_interrupts(dev);
hsw_disable_lcpll(dev_priv, true, true);
-
- intel_runtime_pm_put(dev_priv);
-}
-
-static void __hsw_enable_package_c8(struct drm_i915_private *dev_priv)
-{
- WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock));
- WARN(dev_priv->pc8.disable_count < 1,
- "pc8.disable_count: %d\n", dev_priv->pc8.disable_count);
-
- dev_priv->pc8.disable_count--;
- if (dev_priv->pc8.disable_count != 0)
- return;
-
- schedule_delayed_work(&dev_priv->pc8.enable_work,
- msecs_to_jiffies(i915_pc8_timeout));
}
-static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv)
+void hsw_disable_pc8(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
uint32_t val;
- WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock));
- WARN(dev_priv->pc8.disable_count < 0,
- "pc8.disable_count: %d\n", dev_priv->pc8.disable_count);
-
- dev_priv->pc8.disable_count++;
- if (dev_priv->pc8.disable_count != 1)
- return;
-
WARN_ON(!HAS_PC8(dev));
- cancel_delayed_work_sync(&dev_priv->pc8.enable_work);
- if (!dev_priv->pc8.enabled)
- return;
-
DRM_DEBUG_KMS("Disabling package C8+\n");
- intel_runtime_pm_get(dev_priv);
-
hsw_restore_lcpll(dev_priv);
- hsw_pc8_restore_interrupts(dev);
+ hsw_runtime_pm_restore_interrupts(dev);
lpt_init_pch_refclk(dev);
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
@@ -6757,185 +7105,11 @@ static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->rps.hw_lock);
gen6_update_ring_freq(dev);
mutex_unlock(&dev_priv->rps.hw_lock);
- dev_priv->pc8.enabled = false;
-}
-
-void hsw_enable_package_c8(struct drm_i915_private *dev_priv)
-{
- if (!HAS_PC8(dev_priv->dev))
- return;
-
- mutex_lock(&dev_priv->pc8.lock);
- __hsw_enable_package_c8(dev_priv);
- mutex_unlock(&dev_priv->pc8.lock);
-}
-
-void hsw_disable_package_c8(struct drm_i915_private *dev_priv)
-{
- if (!HAS_PC8(dev_priv->dev))
- return;
-
- mutex_lock(&dev_priv->pc8.lock);
- __hsw_disable_package_c8(dev_priv);
- mutex_unlock(&dev_priv->pc8.lock);
-}
-
-static bool hsw_can_enable_package_c8(struct drm_i915_private *dev_priv)
-{
- struct drm_device *dev = dev_priv->dev;
- struct intel_crtc *crtc;
- uint32_t val;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
- if (crtc->base.enabled)
- return false;
-
- /* This case is still possible since we have the i915.disable_power_well
- * parameter and also the KVMr or something else might be requesting the
- * power well. */
- val = I915_READ(HSW_PWR_WELL_DRIVER);
- if (val != 0) {
- DRM_DEBUG_KMS("Not enabling PC8: power well on\n");
- return false;
- }
-
- return true;
-}
-
-/* Since we're called from modeset_global_resources there's no way to
- * symmetrically increase and decrease the refcount, so we use
- * dev_priv->pc8.requirements_met to track whether we already have the refcount
- * or not.
- */
-static void hsw_update_package_c8(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- bool allow;
-
- if (!HAS_PC8(dev_priv->dev))
- return;
-
- if (!i915_enable_pc8)
- return;
-
- mutex_lock(&dev_priv->pc8.lock);
-
- allow = hsw_can_enable_package_c8(dev_priv);
-
- if (allow == dev_priv->pc8.requirements_met)
- goto done;
-
- dev_priv->pc8.requirements_met = allow;
-
- if (allow)
- __hsw_enable_package_c8(dev_priv);
- else
- __hsw_disable_package_c8(dev_priv);
-
-done:
- mutex_unlock(&dev_priv->pc8.lock);
-}
-
-static void hsw_package_c8_gpu_idle(struct drm_i915_private *dev_priv)
-{
- if (!HAS_PC8(dev_priv->dev))
- return;
-
- mutex_lock(&dev_priv->pc8.lock);
- if (!dev_priv->pc8.gpu_idle) {
- dev_priv->pc8.gpu_idle = true;
- __hsw_enable_package_c8(dev_priv);
- }
- mutex_unlock(&dev_priv->pc8.lock);
-}
-
-static void hsw_package_c8_gpu_busy(struct drm_i915_private *dev_priv)
-{
- if (!HAS_PC8(dev_priv->dev))
- return;
-
- mutex_lock(&dev_priv->pc8.lock);
- if (dev_priv->pc8.gpu_idle) {
- dev_priv->pc8.gpu_idle = false;
- __hsw_disable_package_c8(dev_priv);
- }
- mutex_unlock(&dev_priv->pc8.lock);
-}
-
-#define for_each_power_domain(domain, mask) \
- for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \
- if ((1 << (domain)) & (mask))
-
-static unsigned long get_pipe_power_domains(struct drm_device *dev,
- enum pipe pipe, bool pfit_enabled)
-{
- unsigned long mask;
- enum transcoder transcoder;
-
- transcoder = intel_pipe_to_cpu_transcoder(dev->dev_private, pipe);
-
- mask = BIT(POWER_DOMAIN_PIPE(pipe));
- mask |= BIT(POWER_DOMAIN_TRANSCODER(transcoder));
- if (pfit_enabled)
- mask |= BIT(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe));
-
- return mask;
-}
-
-void intel_display_set_init_power(struct drm_device *dev, bool enable)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- if (dev_priv->power_domains.init_power_on == enable)
- return;
-
- if (enable)
- intel_display_power_get(dev, POWER_DOMAIN_INIT);
- else
- intel_display_power_put(dev, POWER_DOMAIN_INIT);
-
- dev_priv->power_domains.init_power_on = enable;
-}
-
-static void modeset_update_power_wells(struct drm_device *dev)
-{
- unsigned long pipe_domains[I915_MAX_PIPES] = { 0, };
- struct intel_crtc *crtc;
-
- /*
- * First get all needed power domains, then put all unneeded, to avoid
- * any unnecessary toggling of the power wells.
- */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
- enum intel_display_power_domain domain;
-
- if (!crtc->base.enabled)
- continue;
-
- pipe_domains[crtc->pipe] = get_pipe_power_domains(dev,
- crtc->pipe,
- crtc->config.pch_pfit.enabled);
-
- for_each_power_domain(domain, pipe_domains[crtc->pipe])
- intel_display_power_get(dev, domain);
- }
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
- enum intel_display_power_domain domain;
-
- for_each_power_domain(domain, crtc->enabled_power_domains)
- intel_display_power_put(dev, domain);
-
- crtc->enabled_power_domains = pipe_domains[crtc->pipe];
- }
-
- intel_display_set_init_power(dev, false);
}
static void haswell_modeset_global_resources(struct drm_device *dev)
{
- modeset_update_power_wells(dev);
- hsw_update_package_c8(dev);
+ modeset_update_crtc_power_domains(dev);
}
static int haswell_crtc_mode_set(struct drm_crtc *crtc,
@@ -6985,6 +7159,10 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
enum intel_display_power_domain pfit_domain;
uint32_t tmp;
+ if (!intel_display_power_enabled(dev_priv,
+ POWER_DOMAIN_PIPE(crtc->pipe)))
+ return false;
+
pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
pipe_config->shared_dpll = DPLL_ID_PRIVATE;
@@ -7010,7 +7188,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
pipe_config->cpu_transcoder = TRANSCODER_EDP;
}
- if (!intel_display_power_enabled(dev,
+ if (!intel_display_power_enabled(dev_priv,
POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder)))
return false;
@@ -7038,7 +7216,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
intel_get_pipe_timings(crtc, pipe_config);
pfit_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe);
- if (intel_display_power_enabled(dev, pfit_domain))
+ if (intel_display_power_enabled(dev_priv, pfit_domain))
ironlake_get_pfit_config(crtc, pipe_config);
if (IS_HASWELL(dev))
@@ -7435,10 +7613,26 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
bool visible = base != 0;
if (intel_crtc->cursor_visible != visible) {
+ int16_t width = intel_crtc->cursor_width;
uint32_t cntl = I915_READ(CURCNTR(pipe));
if (base) {
cntl &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT);
- cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+ cntl |= MCURSOR_GAMMA_ENABLE;
+
+ switch (width) {
+ case 64:
+ cntl |= CURSOR_MODE_64_ARGB_AX;
+ break;
+ case 128:
+ cntl |= CURSOR_MODE_128_ARGB_AX;
+ break;
+ case 256:
+ cntl |= CURSOR_MODE_256_ARGB_AX;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
cntl |= pipe << 28; /* Connect to correct pipe */
} else {
cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
@@ -7463,10 +7657,25 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
bool visible = base != 0;
if (intel_crtc->cursor_visible != visible) {
+ int16_t width = intel_crtc->cursor_width;
uint32_t cntl = I915_READ(CURCNTR_IVB(pipe));
if (base) {
cntl &= ~CURSOR_MODE;
- cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+ cntl |= MCURSOR_GAMMA_ENABLE;
+ switch (width) {
+ case 64:
+ cntl |= CURSOR_MODE_64_ARGB_AX;
+ break;
+ case 128:
+ cntl |= CURSOR_MODE_128_ARGB_AX;
+ break;
+ case 256:
+ cntl |= CURSOR_MODE_256_ARGB_AX;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
} else {
cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
cntl |= CURSOR_MODE_DISABLE;
@@ -7550,6 +7759,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_i915_gem_object *obj;
+ unsigned old_width;
uint32_t addr;
int ret;
@@ -7562,9 +7772,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
goto finish;
}
- /* Currently we only support 64x64 cursors */
- if (width != 64 || height != 64) {
- DRM_ERROR("we currently only support 64x64 cursors\n");
+ /* Check for which cursor types we support */
+ if (!((width == 64 && height == 64) ||
+ (width == 128 && height == 128 && !IS_GEN2(dev)) ||
+ (width == 256 && height == 256 && !IS_GEN2(dev)))) {
+ DRM_DEBUG("Cursor dimension not supported\n");
return -EINVAL;
}
@@ -7573,18 +7785,18 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
return -ENOENT;
if (obj->base.size < width * height * 4) {
- DRM_ERROR("buffer is to small\n");
+ DRM_DEBUG_KMS("buffer is to small\n");
ret = -ENOMEM;
goto fail;
}
/* we only need to pin inside GTT if cursor is non-phy */
mutex_lock(&dev->struct_mutex);
- if (!dev_priv->info->cursor_needs_physical) {
+ if (!INTEL_INFO(dev)->cursor_needs_physical) {
unsigned alignment;
if (obj->tiling_mode) {
- DRM_ERROR("cursor cannot be tiled\n");
+ DRM_DEBUG_KMS("cursor cannot be tiled\n");
ret = -EINVAL;
goto fail_locked;
}
@@ -7600,13 +7812,13 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
ret = i915_gem_object_pin_to_display_plane(obj, alignment, NULL);
if (ret) {
- DRM_ERROR("failed to move cursor bo into the GTT\n");
+ DRM_DEBUG_KMS("failed to move cursor bo into the GTT\n");
goto fail_locked;
}
ret = i915_gem_object_put_fence(obj);
if (ret) {
- DRM_ERROR("failed to release fence for cursor");
+ DRM_DEBUG_KMS("failed to release fence for cursor");
goto fail_unpin;
}
@@ -7617,7 +7829,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
(intel_crtc->pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1,
align);
if (ret) {
- DRM_ERROR("failed to attach phys object\n");
+ DRM_DEBUG_KMS("failed to attach phys object\n");
goto fail_locked;
}
addr = obj->phys_obj->handle->busaddr;
@@ -7628,7 +7840,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
finish:
if (intel_crtc->cursor_bo) {
- if (dev_priv->info->cursor_needs_physical) {
+ if (INTEL_INFO(dev)->cursor_needs_physical) {
if (intel_crtc->cursor_bo != obj)
i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
} else
@@ -7638,13 +7850,18 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
mutex_unlock(&dev->struct_mutex);
+ old_width = intel_crtc->cursor_width;
+
intel_crtc->cursor_addr = addr;
intel_crtc->cursor_bo = obj;
intel_crtc->cursor_width = width;
intel_crtc->cursor_height = height;
- if (intel_crtc->active)
+ if (intel_crtc->active) {
+ if (old_width != width)
+ intel_update_watermarks(crtc);
intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
+ }
return 0;
fail_unpin:
@@ -7690,10 +7907,10 @@ static struct drm_display_mode load_detect_mode = {
704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
};
-static struct drm_framebuffer *
-intel_framebuffer_create(struct drm_device *dev,
- struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_i915_gem_object *obj)
+struct drm_framebuffer *
+__intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_i915_gem_object *obj)
{
struct intel_framebuffer *intel_fb;
int ret;
@@ -7704,12 +7921,7 @@ intel_framebuffer_create(struct drm_device *dev,
return ERR_PTR(-ENOMEM);
}
- ret = i915_mutex_lock_interruptible(dev);
- if (ret)
- goto err;
-
ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
- mutex_unlock(&dev->struct_mutex);
if (ret)
goto err;
@@ -7721,6 +7933,23 @@ err:
return ERR_PTR(ret);
}
+static struct drm_framebuffer *
+intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_i915_gem_object *obj)
+{
+ struct drm_framebuffer *fb;
+ int ret;
+
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ERR_PTR(ret);
+ fb = __intel_framebuffer_create(dev, mode_cmd, obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ return fb;
+}
+
static u32
intel_framebuffer_pitch_for_width(int width, int bpp)
{
@@ -7766,14 +7995,16 @@ mode_fits_in_fbdev(struct drm_device *dev,
struct drm_i915_gem_object *obj;
struct drm_framebuffer *fb;
- if (dev_priv->fbdev == NULL)
+ if (!dev_priv->fbdev)
return NULL;
- obj = dev_priv->fbdev->ifb.obj;
- if (obj == NULL)
+ if (!dev_priv->fbdev->fb)
return NULL;
- fb = &dev_priv->fbdev->ifb.base;
+ obj = dev_priv->fbdev->fb->obj;
+ BUG_ON(!obj);
+
+ fb = &dev_priv->fbdev->fb->base;
if (fb->pitches[0] < intel_framebuffer_pitch_for_width(mode->hdisplay,
fb->bits_per_pixel))
return NULL;
@@ -7855,6 +8086,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
to_intel_connector(connector)->new_encoder = intel_encoder;
intel_crtc = to_intel_crtc(crtc);
+ intel_crtc->new_enabled = true;
+ intel_crtc->new_config = &intel_crtc->config;
old->dpms_mode = connector->dpms;
old->load_detect_temp = true;
old->release_fb = NULL;
@@ -7878,21 +8111,28 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
if (IS_ERR(fb)) {
DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
- mutex_unlock(&crtc->mutex);
- return false;
+ goto fail;
}
if (intel_set_mode(crtc, mode, 0, 0, fb)) {
DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
if (old->release_fb)
old->release_fb->funcs->destroy(old->release_fb);
- mutex_unlock(&crtc->mutex);
- return false;
+ goto fail;
}
/* let the connector get through one full cycle before testing */
intel_wait_for_vblank(dev, intel_crtc->pipe);
return true;
+
+ fail:
+ intel_crtc->new_enabled = crtc->enabled;
+ if (intel_crtc->new_enabled)
+ intel_crtc->new_config = &intel_crtc->config;
+ else
+ intel_crtc->new_config = NULL;
+ mutex_unlock(&crtc->mutex);
+ return false;
}
void intel_release_load_detect_pipe(struct drm_connector *connector,
@@ -7902,6 +8142,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
intel_attached_encoder(connector);
struct drm_encoder *encoder = &intel_encoder->base;
struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
connector->base.id, drm_get_connector_name(connector),
@@ -7910,6 +8151,8 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
if (old->load_detect_temp) {
to_intel_connector(connector)->new_encoder = NULL;
intel_encoder->new_crtc = NULL;
+ intel_crtc->new_enabled = false;
+ intel_crtc->new_config = NULL;
intel_set_mode(crtc, NULL, 0, 0, NULL);
if (old->release_fb) {
@@ -8122,7 +8365,7 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
static void intel_increase_pllclock(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
int dpll_reg = DPLL(pipe);
@@ -8153,7 +8396,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
static void intel_decrease_pllclock(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
if (HAS_PCH_SPLIT(dev))
@@ -8190,8 +8433,12 @@ void intel_mark_busy(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- hsw_package_c8_gpu_busy(dev_priv);
+ if (dev_priv->mm.busy)
+ return;
+
+ intel_runtime_pm_get(dev_priv);
i915_update_gfx_val(dev_priv);
+ dev_priv->mm.busy = true;
}
void intel_mark_idle(struct drm_device *dev)
@@ -8199,20 +8446,26 @@ void intel_mark_idle(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc;
- hsw_package_c8_gpu_idle(dev_priv);
-
- if (!i915_powersave)
+ if (!dev_priv->mm.busy)
return;
+ dev_priv->mm.busy = false;
+
+ if (!i915.powersave)
+ goto out;
+
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (!crtc->fb)
+ if (!crtc->primary->fb)
continue;
intel_decrease_pllclock(crtc);
}
- if (dev_priv->info->gen >= 6)
+ if (INTEL_INFO(dev)->gen >= 6)
gen6_rps_idle(dev->dev_private);
+
+out:
+ intel_runtime_pm_put(dev_priv);
}
void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
@@ -8221,14 +8474,14 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
struct drm_device *dev = obj->base.dev;
struct drm_crtc *crtc;
- if (!i915_powersave)
+ if (!i915.powersave)
return;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (!crtc->fb)
+ if (!crtc->primary->fb)
continue;
- if (to_intel_framebuffer(crtc->fb)->obj != obj)
+ if (to_intel_framebuffer(crtc->primary->fb)->obj != obj)
continue;
intel_increase_pllclock(crtc);
@@ -8284,7 +8537,7 @@ static void intel_unpin_work_fn(struct work_struct *__work)
static void do_intel_finish_page_flip(struct drm_device *dev,
struct drm_crtc *crtc)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_unpin_work *work;
unsigned long flags;
@@ -8325,7 +8578,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
void intel_finish_page_flip(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
do_intel_finish_page_flip(dev, crtc);
@@ -8333,7 +8586,7 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe)
void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane];
do_intel_finish_page_flip(dev, crtc);
@@ -8341,7 +8594,7 @@ void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
void intel_prepare_page_flip(struct drm_device *dev, int plane)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc =
to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
unsigned long flags;
@@ -8656,7 +8909,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_framebuffer *old_fb = crtc->fb;
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_unpin_work *work;
@@ -8664,7 +8917,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
int ret;
/* Can't change pixel format via MI display flips. */
- if (fb->pixel_format != crtc->fb->pixel_format)
+ if (fb->pixel_format != crtc->primary->fb->pixel_format)
return -EINVAL;
/*
@@ -8672,10 +8925,13 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
* Note that pitch changes could also affect these register.
*/
if (INTEL_INFO(dev)->gen > 3 &&
- (fb->offsets[0] != crtc->fb->offsets[0] ||
- fb->pitches[0] != crtc->fb->pitches[0]))
+ (fb->offsets[0] != crtc->primary->fb->offsets[0] ||
+ fb->pitches[0] != crtc->primary->fb->pitches[0]))
return -EINVAL;
+ if (i915_terminally_wedged(&dev_priv->gpu_error))
+ goto out_hang;
+
work = kzalloc(sizeof(*work), GFP_KERNEL);
if (work == NULL)
return -ENOMEM;
@@ -8713,7 +8969,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
drm_gem_object_reference(&work->old_fb_obj->base);
drm_gem_object_reference(&obj->base);
- crtc->fb = fb;
+ crtc->primary->fb = fb;
work->pending_flip_obj = obj;
@@ -8736,7 +8992,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
cleanup_pending:
atomic_dec(&intel_crtc->unpin_work_count);
- crtc->fb = old_fb;
+ crtc->primary->fb = old_fb;
drm_gem_object_unreference(&work->old_fb_obj->base);
drm_gem_object_unreference(&obj->base);
mutex_unlock(&dev->struct_mutex);
@@ -8750,6 +9006,13 @@ cleanup:
free_work:
kfree(work);
+ if (ret == -EIO) {
+out_hang:
+ intel_crtc_wait_for_pending_flips(crtc);
+ ret = intel_pipe_set_base(crtc, crtc->x, crtc->y, fb);
+ if (ret == 0 && event)
+ drm_send_vblank_event(dev, intel_crtc->pipe, event);
+ }
return ret;
}
@@ -8766,6 +9029,7 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = {
*/
static void intel_modeset_update_staged_output_state(struct drm_device *dev)
{
+ struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_connector *connector;
@@ -8780,6 +9044,16 @@ static void intel_modeset_update_staged_output_state(struct drm_device *dev)
encoder->new_crtc =
to_intel_crtc(encoder->base.crtc);
}
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+ base.head) {
+ crtc->new_enabled = crtc->base.enabled;
+
+ if (crtc->new_enabled)
+ crtc->new_config = &crtc->config;
+ else
+ crtc->new_config = NULL;
+ }
}
/**
@@ -8789,6 +9063,7 @@ static void intel_modeset_update_staged_output_state(struct drm_device *dev)
*/
static void intel_modeset_commit_output_state(struct drm_device *dev)
{
+ struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_connector *connector;
@@ -8801,6 +9076,11 @@ static void intel_modeset_commit_output_state(struct drm_device *dev)
base.head) {
encoder->base.crtc = &encoder->new_crtc->base;
}
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+ base.head) {
+ crtc->base.enabled = crtc->new_enabled;
+ }
}
static void
@@ -8941,23 +9221,47 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
DRM_DEBUG_KMS("double wide: %i\n", pipe_config->double_wide);
}
-static bool check_encoder_cloning(struct drm_crtc *crtc)
+static bool encoders_cloneable(const struct intel_encoder *a,
+ const struct intel_encoder *b)
{
- int num_encoders = 0;
- bool uncloneable_encoders = false;
+ /* masks could be asymmetric, so check both ways */
+ return a == b || (a->cloneable & (1 << b->type) &&
+ b->cloneable & (1 << a->type));
+}
+
+static bool check_single_encoder_cloning(struct intel_crtc *crtc,
+ struct intel_encoder *encoder)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct intel_encoder *source_encoder;
+
+ list_for_each_entry(source_encoder,
+ &dev->mode_config.encoder_list, base.head) {
+ if (source_encoder->new_crtc != crtc)
+ continue;
+
+ if (!encoders_cloneable(encoder, source_encoder))
+ return false;
+ }
+
+ return true;
+}
+
+static bool check_encoder_cloning(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
struct intel_encoder *encoder;
- list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list,
- base.head) {
- if (&encoder->new_crtc->base != crtc)
+ list_for_each_entry(encoder,
+ &dev->mode_config.encoder_list, base.head) {
+ if (encoder->new_crtc != crtc)
continue;
- num_encoders++;
- if (!encoder->cloneable)
- uncloneable_encoders = true;
+ if (!check_single_encoder_cloning(crtc, encoder))
+ return false;
}
- return !(num_encoders > 1 && uncloneable_encoders);
+ return true;
}
static struct intel_crtc_config *
@@ -8971,7 +9275,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
int plane_bpp, ret = -EINVAL;
bool retry = true;
- if (!check_encoder_cloning(crtc)) {
+ if (!check_encoder_cloning(to_intel_crtc(crtc))) {
DRM_DEBUG_KMS("rejecting invalid cloning configuration\n");
return ERR_PTR(-EINVAL);
}
@@ -9127,29 +9431,22 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
*prepare_pipes |= 1 << encoder->new_crtc->pipe;
}
- /* Check for any pipes that will be fully disabled ... */
+ /* Check for pipes that will be enabled/disabled ... */
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
base.head) {
- bool used = false;
-
- /* Don't try to disable disabled crtcs. */
- if (!intel_crtc->base.enabled)
+ if (intel_crtc->base.enabled == intel_crtc->new_enabled)
continue;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
- if (encoder->new_crtc == intel_crtc)
- used = true;
- }
-
- if (!used)
+ if (!intel_crtc->new_enabled)
*disable_pipes |= 1 << intel_crtc->pipe;
+ else
+ *prepare_pipes |= 1 << intel_crtc->pipe;
}
/* set_mode is also used to update properties on life display pipes. */
intel_crtc = to_intel_crtc(crtc);
- if (crtc->enabled)
+ if (intel_crtc->new_enabled)
*prepare_pipes |= 1 << intel_crtc->pipe;
/*
@@ -9208,10 +9505,13 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
intel_modeset_commit_output_state(dev);
- /* Update computed state. */
+ /* Double check state. */
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
base.head) {
- intel_crtc->base.enabled = intel_crtc_in_use(&intel_crtc->base);
+ WARN_ON(intel_crtc->base.enabled != intel_crtc_in_use(&intel_crtc->base));
+ WARN_ON(intel_crtc->new_config &&
+ intel_crtc->new_config != &intel_crtc->config);
+ WARN_ON(intel_crtc->base.enabled != !!intel_crtc->new_config);
}
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -9380,10 +9680,8 @@ intel_pipe_config_compare(struct drm_device *dev,
if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5)
PIPE_CONF_CHECK_I(pipe_bpp);
- if (!HAS_DDI(dev)) {
- PIPE_CONF_CHECK_CLOCK_FUZZY(adjusted_mode.crtc_clock);
- PIPE_CONF_CHECK_CLOCK_FUZZY(port_clock);
- }
+ PIPE_CONF_CHECK_CLOCK_FUZZY(adjusted_mode.crtc_clock);
+ PIPE_CONF_CHECK_CLOCK_FUZZY(port_clock);
#undef PIPE_CONF_CHECK_X
#undef PIPE_CONF_CHECK_I
@@ -9471,7 +9769,7 @@ check_encoder_state(struct drm_device *dev)
static void
check_crtc_state(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_crtc_config pipe_config;
@@ -9539,7 +9837,7 @@ check_crtc_state(struct drm_device *dev)
static void
check_shared_dpll_state(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *crtc;
struct intel_dpll_hw_state dpll_hw_state;
int i;
@@ -9612,7 +9910,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
int x, int y, struct drm_framebuffer *fb)
{
struct drm_device *dev = crtc->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_display_mode *saved_mode;
struct intel_crtc_config *pipe_config = NULL;
struct intel_crtc *intel_crtc;
@@ -9643,6 +9941,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
}
intel_dump_pipe_config(to_intel_crtc(crtc), pipe_config,
"[modeset]");
+ to_intel_crtc(crtc)->new_config = pipe_config;
}
/*
@@ -9653,8 +9952,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
* adjusted_mode bits in the crtc directly.
*/
if (IS_VALLEYVIEW(dev)) {
- valleyview_modeset_global_pipes(dev, &prepare_pipes,
- modeset_pipes, pipe_config);
+ valleyview_modeset_global_pipes(dev, &prepare_pipes);
/* may have added more to prepare_pipes than we should */
prepare_pipes &= ~disable_pipes;
@@ -9676,6 +9974,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
/* mode_set/enable/disable functions rely on a correct pipe
* config. */
to_intel_crtc(crtc)->config = *pipe_config;
+ to_intel_crtc(crtc)->new_config = &to_intel_crtc(crtc)->config;
/*
* Calculate and store various constants which
@@ -9734,7 +10033,7 @@ static int intel_set_mode(struct drm_crtc *crtc,
void intel_crtc_restore_mode(struct drm_crtc *crtc)
{
- intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb);
+ intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->primary->fb);
}
#undef for_each_intel_crtc_masked
@@ -9746,16 +10045,24 @@ static void intel_set_config_free(struct intel_set_config *config)
kfree(config->save_connector_encoders);
kfree(config->save_encoder_crtcs);
+ kfree(config->save_crtc_enabled);
kfree(config);
}
static int intel_set_config_save_state(struct drm_device *dev,
struct intel_set_config *config)
{
+ struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
int count;
+ config->save_crtc_enabled =
+ kcalloc(dev->mode_config.num_crtc,
+ sizeof(bool), GFP_KERNEL);
+ if (!config->save_crtc_enabled)
+ return -ENOMEM;
+
config->save_encoder_crtcs =
kcalloc(dev->mode_config.num_encoder,
sizeof(struct drm_crtc *), GFP_KERNEL);
@@ -9773,6 +10080,11 @@ static int intel_set_config_save_state(struct drm_device *dev,
* restored, not the drivers personal bookkeeping.
*/
count = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ config->save_crtc_enabled[count++] = crtc->enabled;
+ }
+
+ count = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
config->save_encoder_crtcs[count++] = encoder->crtc;
}
@@ -9788,11 +10100,22 @@ static int intel_set_config_save_state(struct drm_device *dev,
static void intel_set_config_restore_state(struct drm_device *dev,
struct intel_set_config *config)
{
+ struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_connector *connector;
int count;
count = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ crtc->new_enabled = config->save_crtc_enabled[count++];
+
+ if (crtc->new_enabled)
+ crtc->new_config = &crtc->config;
+ else
+ crtc->new_config = NULL;
+ }
+
+ count = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
encoder->new_crtc =
to_intel_crtc(config->save_encoder_crtcs[count++]);
@@ -9834,13 +10157,13 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set,
* and then just flip_or_move it */
if (is_crtc_connector_off(set)) {
config->mode_changed = true;
- } else if (set->crtc->fb != set->fb) {
+ } else if (set->crtc->primary->fb != set->fb) {
/* If we have no fb then treat it as a full mode set */
- if (set->crtc->fb == NULL) {
+ if (set->crtc->primary->fb == NULL) {
struct intel_crtc *intel_crtc =
to_intel_crtc(set->crtc);
- if (intel_crtc->active && i915_fastboot) {
+ if (intel_crtc->active && i915.fastboot) {
DRM_DEBUG_KMS("crtc has no fb, will flip\n");
config->fb_changed = true;
} else {
@@ -9850,7 +10173,7 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set,
} else if (set->fb == NULL) {
config->mode_changed = true;
} else if (set->fb->pixel_format !=
- set->crtc->fb->pixel_format) {
+ set->crtc->primary->fb->pixel_format) {
config->mode_changed = true;
} else {
config->fb_changed = true;
@@ -9876,9 +10199,9 @@ intel_modeset_stage_output_state(struct drm_device *dev,
struct drm_mode_set *set,
struct intel_set_config *config)
{
- struct drm_crtc *new_crtc;
struct intel_connector *connector;
struct intel_encoder *encoder;
+ struct intel_crtc *crtc;
int ro;
/* The upper layers ensure that we either disable a crtc or have a list
@@ -9921,6 +10244,8 @@ intel_modeset_stage_output_state(struct drm_device *dev,
/* Update crtc of enabled connectors. */
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
+ struct drm_crtc *new_crtc;
+
if (!connector->new_encoder)
continue;
@@ -9971,9 +10296,58 @@ intel_modeset_stage_output_state(struct drm_device *dev,
}
/* Now we've also updated encoder->new_crtc for all encoders. */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+ base.head) {
+ crtc->new_enabled = false;
+
+ list_for_each_entry(encoder,
+ &dev->mode_config.encoder_list,
+ base.head) {
+ if (encoder->new_crtc == crtc) {
+ crtc->new_enabled = true;
+ break;
+ }
+ }
+
+ if (crtc->new_enabled != crtc->base.enabled) {
+ DRM_DEBUG_KMS("crtc %sabled, full mode switch\n",
+ crtc->new_enabled ? "en" : "dis");
+ config->mode_changed = true;
+ }
+
+ if (crtc->new_enabled)
+ crtc->new_config = &crtc->config;
+ else
+ crtc->new_config = NULL;
+ }
+
return 0;
}
+static void disable_crtc_nofb(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct intel_encoder *encoder;
+ struct intel_connector *connector;
+
+ DRM_DEBUG_KMS("Trying to restore without FB -> disabling pipe %c\n",
+ pipe_name(crtc->pipe));
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) {
+ if (connector->new_encoder &&
+ connector->new_encoder->new_crtc == crtc)
+ connector->new_encoder = NULL;
+ }
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ if (encoder->new_crtc == crtc)
+ encoder->new_crtc = NULL;
+ }
+
+ crtc->new_enabled = false;
+ crtc->new_config = NULL;
+}
+
static int intel_crtc_set_config(struct drm_mode_set *set)
{
struct drm_device *dev;
@@ -10012,7 +10386,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
save_set.mode = &set->crtc->mode;
save_set.x = set->crtc->x;
save_set.y = set->crtc->y;
- save_set.fb = set->crtc->fb;
+ save_set.fb = set->crtc->primary->fb;
/* Compute whether we need a full modeset, only an fb base update or no
* change at all. In the future we might also check whether only the
@@ -10040,7 +10414,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
* flipping, so increasing its cost here shouldn't be a big
* deal).
*/
- if (i915_fastboot && ret == 0)
+ if (i915.fastboot && ret == 0)
intel_modeset_check_state(set->crtc->dev);
}
@@ -10050,6 +10424,15 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
fail:
intel_set_config_restore_state(dev, config);
+ /*
+ * HACK: if the pipe was on, but we didn't have a framebuffer,
+ * force the pipe off to avoid oopsing in the modeset code
+ * due to fb==NULL. This should only happen during boot since
+ * we don't yet reconstruct the FB from the hardware state.
+ */
+ if (to_intel_crtc(save_set.crtc)->new_enabled && !save_set.fb)
+ disable_crtc_nofb(to_intel_crtc(save_set.crtc));
+
/* Try to restore the config */
if (config->mode_changed &&
intel_set_mode(save_set.crtc, save_set.mode,
@@ -10174,7 +10557,7 @@ static void intel_shared_dpll_init(struct drm_device *dev)
static void intel_crtc_init(struct drm_device *dev, int pipe)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc;
int i;
@@ -10184,6 +10567,16 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
+ if (IS_GEN2(dev)) {
+ intel_crtc->max_cursor_width = GEN2_CURSOR_WIDTH;
+ intel_crtc->max_cursor_height = GEN2_CURSOR_HEIGHT;
+ } else {
+ intel_crtc->max_cursor_width = CURSOR_WIDTH;
+ intel_crtc->max_cursor_height = CURSOR_HEIGHT;
+ }
+ dev->mode_config.cursor_width = intel_crtc->max_cursor_width;
+ dev->mode_config.cursor_height = intel_crtc->max_cursor_height;
+
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
for (i = 0; i < 256; i++) {
intel_crtc->lut_r[i] = i;
@@ -10255,12 +10648,7 @@ static int intel_encoder_clones(struct intel_encoder *encoder)
list_for_each_entry(source_encoder,
&dev->mode_config.encoder_list, base.head) {
-
- if (encoder == source_encoder)
- index_mask |= (1 << entry);
-
- /* Intel hw has only one MUX where enocoders could be cloned. */
- if (encoder->cloneable && source_encoder->cloneable)
+ if (encoders_cloneable(encoder, source_encoder))
index_mask |= (1 << entry);
entry++;
@@ -10279,8 +10667,7 @@ static bool has_edp_a(struct drm_device *dev)
if ((I915_READ(DP_A) & DP_DETECTED) == 0)
return false;
- if (IS_GEN5(dev) &&
- (I915_READ(ILK_DISPLAY_CHICKEN_FUSES) & ILK_eDP_A_DISABLE))
+ if (IS_GEN5(dev) && (I915_READ(FUSE_STRAP) & ILK_eDP_A_DISABLE))
return false;
return true;
@@ -10433,18 +10820,13 @@ static void intel_setup_outputs(struct drm_device *dev)
drm_helper_move_panel_connectors_to_head(dev);
}
-void intel_framebuffer_fini(struct intel_framebuffer *fb)
-{
- drm_framebuffer_cleanup(&fb->base);
- WARN_ON(!fb->obj->framebuffer_references--);
- drm_gem_object_unreference_unlocked(&fb->obj->base);
-}
-
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
- intel_framebuffer_fini(intel_fb);
+ drm_framebuffer_cleanup(fb);
+ WARN_ON(!intel_fb->obj->framebuffer_references--);
+ drm_gem_object_unreference_unlocked(&intel_fb->obj->base);
kfree(intel_fb);
}
@@ -10463,12 +10845,12 @@ static const struct drm_framebuffer_funcs intel_fb_funcs = {
.create_handle = intel_user_framebuffer_create_handle,
};
-int intel_framebuffer_init(struct drm_device *dev,
- struct intel_framebuffer *intel_fb,
- struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_i915_gem_object *obj)
+static int intel_framebuffer_init(struct drm_device *dev,
+ struct intel_framebuffer *intel_fb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_i915_gem_object *obj)
{
- int aligned_height, tile_height;
+ int aligned_height;
int pitch_limit;
int ret;
@@ -10562,9 +10944,8 @@ int intel_framebuffer_init(struct drm_device *dev,
if (mode_cmd->offsets[0] != 0)
return -EINVAL;
- tile_height = IS_GEN2(dev) ? 16 : 8;
- aligned_height = ALIGN(mode_cmd->height,
- obj->tiling_mode ? tile_height : 1);
+ aligned_height = intel_align_height(dev, mode_cmd->height,
+ obj->tiling_mode);
/* FIXME drm helper for size checks (especially planar formats)? */
if (obj->base.size < aligned_height * mode_cmd->pitches[0])
return -EINVAL;
@@ -10624,32 +11005,40 @@ static void intel_init_display(struct drm_device *dev)
if (HAS_DDI(dev)) {
dev_priv->display.get_pipe_config = haswell_get_pipe_config;
+ dev_priv->display.get_plane_config = ironlake_get_plane_config;
dev_priv->display.crtc_mode_set = haswell_crtc_mode_set;
dev_priv->display.crtc_enable = haswell_crtc_enable;
dev_priv->display.crtc_disable = haswell_crtc_disable;
dev_priv->display.off = haswell_crtc_off;
- dev_priv->display.update_plane = ironlake_update_plane;
+ dev_priv->display.update_primary_plane =
+ ironlake_update_primary_plane;
} else if (HAS_PCH_SPLIT(dev)) {
dev_priv->display.get_pipe_config = ironlake_get_pipe_config;
+ dev_priv->display.get_plane_config = ironlake_get_plane_config;
dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
dev_priv->display.crtc_enable = ironlake_crtc_enable;
dev_priv->display.crtc_disable = ironlake_crtc_disable;
dev_priv->display.off = ironlake_crtc_off;
- dev_priv->display.update_plane = ironlake_update_plane;
+ dev_priv->display.update_primary_plane =
+ ironlake_update_primary_plane;
} else if (IS_VALLEYVIEW(dev)) {
dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
+ dev_priv->display.get_plane_config = i9xx_get_plane_config;
dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
dev_priv->display.crtc_enable = valleyview_crtc_enable;
dev_priv->display.crtc_disable = i9xx_crtc_disable;
dev_priv->display.off = i9xx_crtc_off;
- dev_priv->display.update_plane = i9xx_update_plane;
+ dev_priv->display.update_primary_plane =
+ i9xx_update_primary_plane;
} else {
dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
+ dev_priv->display.get_plane_config = i9xx_get_plane_config;
dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
dev_priv->display.crtc_enable = i9xx_crtc_enable;
dev_priv->display.crtc_disable = i9xx_crtc_disable;
dev_priv->display.off = i9xx_crtc_off;
- dev_priv->display.update_plane = i9xx_update_plane;
+ dev_priv->display.update_primary_plane =
+ i9xx_update_primary_plane;
}
/* Returns the core display clock speed */
@@ -10839,6 +11228,9 @@ static struct intel_quirk intel_quirks[] = {
/* Acer Aspire 4736Z */
{ 0x2a42, 0x1025, 0x0260, quirk_invert_brightness },
+
+ /* Acer Aspire 5336 */
+ { 0x2a42, 0x1025, 0x048a, quirk_invert_brightness },
};
static void intel_init_quirks(struct drm_device *dev)
@@ -10869,6 +11261,7 @@ static void i915_disable_vga(struct drm_device *dev)
u8 sr1;
u32 vga_reg = i915_vgacntrl_reg(dev);
+ /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */
vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
outb(SR01, VGA_SR_INDEX);
sr1 = inb(VGA_SR_DATA);
@@ -10901,7 +11294,9 @@ void intel_modeset_suspend_hw(struct drm_device *dev)
void intel_modeset_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int i, j, ret;
+ int sprite, ret;
+ enum pipe pipe;
+ struct intel_crtc *crtc;
drm_mode_config_init(dev);
@@ -10938,13 +11333,13 @@ void intel_modeset_init(struct drm_device *dev)
INTEL_INFO(dev)->num_pipes,
INTEL_INFO(dev)->num_pipes > 1 ? "s" : "");
- for_each_pipe(i) {
- intel_crtc_init(dev, i);
- for (j = 0; j < dev_priv->num_plane; j++) {
- ret = intel_plane_init(dev, i, j);
+ for_each_pipe(pipe) {
+ intel_crtc_init(dev, pipe);
+ for_each_sprite(pipe, sprite) {
+ ret = intel_plane_init(dev, pipe, sprite);
if (ret)
DRM_DEBUG_KMS("pipe %c sprite %c init failed: %d\n",
- pipe_name(i), sprite_name(i, j), ret);
+ pipe_name(pipe), sprite_name(pipe, sprite), ret);
}
}
@@ -10960,6 +11355,33 @@ void intel_modeset_init(struct drm_device *dev)
/* Just in case the BIOS is doing something questionable. */
intel_disable_fbc(dev);
+
+ mutex_lock(&dev->mode_config.mutex);
+ intel_modeset_setup_hw_state(dev, false);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+ base.head) {
+ if (!crtc->active)
+ continue;
+
+ /*
+ * Note that reserving the BIOS fb up front prevents us
+ * from stuffing other stolen allocations like the ring
+ * on top. This prevents some ugliness at boot time, and
+ * can even allow for smooth boot transitions if the BIOS
+ * fb is large enough for the active pipe configuration.
+ */
+ if (dev_priv->display.get_plane_config) {
+ dev_priv->display.get_plane_config(crtc,
+ &crtc->plane_config);
+ /*
+ * If the fb is shared between multiple heads, we'll
+ * just get the first one.
+ */
+ intel_find_plane_obj(crtc, &crtc->plane_config);
+ }
+ }
}
static void
@@ -11097,6 +11519,17 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
encoder->base.crtc = NULL;
}
}
+ if (crtc->active) {
+ /*
+ * We start out with underrun reporting disabled to avoid races.
+ * For correct bookkeeping mark this on active crtcs.
+ *
+ * No protection against concurrent access is required - at
+ * worst a fifo underrun happens which also sets this to false.
+ */
+ crtc->cpu_fifo_underrun_disabled = true;
+ crtc->pch_fifo_underrun_disabled = true;
+ }
}
static void intel_sanitize_encoder(struct intel_encoder *encoder)
@@ -11142,11 +11575,21 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
* the crtc fixup. */
}
-void i915_redisable_vga(struct drm_device *dev)
+void i915_redisable_vga_power_on(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 vga_reg = i915_vgacntrl_reg(dev);
+ if (!(I915_READ(vga_reg) & VGA_DISP_DISABLE)) {
+ DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n");
+ i915_disable_vga(dev);
+ }
+}
+
+void i915_redisable_vga(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
/* This function can be called both from intel_modeset_setup_hw_state or
* at a very early point in our resume sequence, where the power well
* structures are not yet restored. Since this function is at a very
@@ -11154,14 +11597,10 @@ void i915_redisable_vga(struct drm_device *dev)
* level, just check if the power well is enabled instead of trying to
* follow the "don't touch the power well if we don't need it" policy
* the rest of the driver uses. */
- if ((IS_HASWELL(dev) || IS_BROADWELL(dev)) &&
- (I915_READ(HSW_PWR_WELL_DRIVER) & HSW_PWR_WELL_STATE_ENABLED) == 0)
+ if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_VGA))
return;
- if (!(I915_READ(vga_reg) & VGA_DISP_DISABLE)) {
- DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n");
- i915_disable_vga(dev);
- }
+ i915_redisable_vga_power_on(dev);
}
static void intel_modeset_readout_hw_state(struct drm_device *dev)
@@ -11265,9 +11704,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
*/
list_for_each_entry(crtc, &dev->mode_config.crtc_list,
base.head) {
- if (crtc->active && i915_fastboot) {
- intel_crtc_mode_from_pipe_config(crtc, &crtc->config);
-
+ if (crtc->active && i915.fastboot) {
+ intel_mode_from_pipe_config(&crtc->base.mode, &crtc->config);
DRM_DEBUG_KMS("[CRTC:%d] found active mode: ",
crtc->base.base.id);
drm_mode_debug_printmodeline(&crtc->base.mode);
@@ -11313,7 +11751,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
dev_priv->pipe_to_crtc_mapping[pipe];
__intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y,
- crtc->fb);
+ crtc->primary->fb);
}
} else {
intel_modeset_update_staged_output_state(dev);
@@ -11324,14 +11762,44 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
void intel_modeset_gem_init(struct drm_device *dev)
{
+ struct drm_crtc *c;
+ struct intel_framebuffer *fb;
+
+ mutex_lock(&dev->struct_mutex);
+ intel_init_gt_powersave(dev);
+ mutex_unlock(&dev->struct_mutex);
+
intel_modeset_init_hw(dev);
intel_setup_overlay(dev);
- mutex_lock(&dev->mode_config.mutex);
- drm_mode_config_reset(dev);
- intel_modeset_setup_hw_state(dev, false);
- mutex_unlock(&dev->mode_config.mutex);
+ /*
+ * Make sure any fbs we allocated at startup are properly
+ * pinned & fenced. When we do the allocation it's too early
+ * for this.
+ */
+ mutex_lock(&dev->struct_mutex);
+ list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+ if (!c->primary->fb)
+ continue;
+
+ fb = to_intel_framebuffer(c->primary->fb);
+ if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) {
+ DRM_ERROR("failed to pin boot fb on pipe %d\n",
+ to_intel_crtc(c)->pipe);
+ drm_framebuffer_unreference(c->primary->fb);
+ c->primary->fb = NULL;
+ }
+ }
+ mutex_unlock(&dev->struct_mutex);
+}
+
+void intel_connector_unregister(struct intel_connector *intel_connector)
+{
+ struct drm_connector *connector = &intel_connector->base;
+
+ intel_panel_destroy_backlight(connector);
+ drm_sysfs_connector_remove(connector);
}
void intel_modeset_cleanup(struct drm_device *dev)
@@ -11359,7 +11827,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
/* Skip inactive CRTCs */
- if (!crtc->fb)
+ if (!crtc->primary->fb)
continue;
intel_increase_pllclock(crtc);
@@ -11378,13 +11846,19 @@ void intel_modeset_cleanup(struct drm_device *dev)
/* destroy the backlight and sysfs files before encoders/connectors */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- intel_panel_destroy_backlight(connector);
- drm_sysfs_connector_remove(connector);
+ struct intel_connector *intel_connector;
+
+ intel_connector = to_intel_connector(connector);
+ intel_connector->unregister(intel_connector);
}
drm_mode_config_cleanup(dev);
intel_cleanup_overlay(dev);
+
+ mutex_lock(&dev->struct_mutex);
+ intel_cleanup_gt_powersave(dev);
+ mutex_unlock(&dev->struct_mutex);
}
/*
@@ -11412,12 +11886,24 @@ int intel_modeset_vga_set_state(struct drm_device *dev, bool state)
unsigned reg = INTEL_INFO(dev)->gen >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL;
u16 gmch_ctrl;
- pci_read_config_word(dev_priv->bridge_dev, reg, &gmch_ctrl);
+ if (pci_read_config_word(dev_priv->bridge_dev, reg, &gmch_ctrl)) {
+ DRM_ERROR("failed to read control word\n");
+ return -EIO;
+ }
+
+ if (!!(gmch_ctrl & INTEL_GMCH_VGA_DISABLE) == !state)
+ return 0;
+
if (state)
gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE;
else
gmch_ctrl |= INTEL_GMCH_VGA_DISABLE;
- pci_write_config_word(dev_priv->bridge_dev, reg, gmch_ctrl);
+
+ if (pci_write_config_word(dev_priv->bridge_dev, reg, gmch_ctrl)) {
+ DRM_ERROR("failed to write control word\n");
+ return -EIO;
+ }
+
return 0;
}
@@ -11467,7 +11953,7 @@ struct intel_display_error_state {
struct intel_display_error_state *
intel_display_capture_error_state(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_display_error_state *error;
int transcoders[] = {
TRANSCODER_A,
@@ -11489,7 +11975,8 @@ intel_display_capture_error_state(struct drm_device *dev)
for_each_pipe(i) {
error->pipe[i].power_domain_on =
- intel_display_power_enabled_sw(dev, POWER_DOMAIN_PIPE(i));
+ intel_display_power_enabled_sw(dev_priv,
+ POWER_DOMAIN_PIPE(i));
if (!error->pipe[i].power_domain_on)
continue;
@@ -11527,7 +12014,7 @@ intel_display_capture_error_state(struct drm_device *dev)
enum transcoder cpu_transcoder = transcoders[i];
error->transcoder[i].power_domain_on =
- intel_display_power_enabled_sw(dev,
+ intel_display_power_enabled_sw(dev_priv,
POWER_DOMAIN_TRANSCODER(cpu_transcoder));
if (!error->transcoder[i].power_domain_on)
continue;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 2688f6d64bb9..a0dad1a2f819 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -91,18 +91,25 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
}
static void intel_dp_link_down(struct intel_dp *intel_dp);
+static bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
+static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
static int
intel_dp_max_link_bw(struct intel_dp *intel_dp)
{
int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
+ struct drm_device *dev = intel_dp->attached_connector->base.dev;
switch (max_link_bw) {
case DP_LINK_BW_1_62:
case DP_LINK_BW_2_7:
break;
case DP_LINK_BW_5_4: /* 1.2 capable displays may advertise higher bw */
- max_link_bw = DP_LINK_BW_2_7;
+ if ((IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8) &&
+ intel_dp->dpcd[DP_DPCD_REV] >= 0x12)
+ max_link_bw = DP_LINK_BW_5_4;
+ else
+ max_link_bw = DP_LINK_BW_2_7;
break;
default:
WARN(1, "invalid max DP link bw val %x, using 1.62Gbps\n",
@@ -294,7 +301,7 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp)
return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
}
-static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp)
+static bool edp_have_panel_power(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -302,12 +309,13 @@ static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp)
return (I915_READ(_pp_stat_reg(intel_dp)) & PP_ON) != 0;
}
-static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp)
+static bool edp_have_panel_vdd(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
- return (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0;
+ return !dev_priv->pm.suspended &&
+ (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0;
}
static void
@@ -319,7 +327,7 @@ intel_dp_check_edp(struct intel_dp *intel_dp)
if (!is_edp(intel_dp))
return;
- if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) {
+ if (!edp_have_panel_power(intel_dp) && !edp_have_panel_vdd(intel_dp)) {
WARN(1, "eDP powered off while attempting aux channel communication.\n");
DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n",
I915_READ(_pp_stat_reg(intel_dp)),
@@ -351,31 +359,46 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq)
return status;
}
-static uint32_t get_aux_clock_divider(struct intel_dp *intel_dp,
- int index)
+static uint32_t i9xx_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_device *dev = intel_dig_port->base.base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- /* The clock divider is based off the hrawclk,
- * and would like to run at 2MHz. So, take the
- * hrawclk value and divide by 2 and use that
- *
- * Note that PCH attached eDP panels should use a 125MHz input
- * clock divider.
+ /*
+ * The clock divider is based off the hrawclk, and would like to run at
+ * 2MHz. So, take the hrawclk value and divide by 2 and use that
*/
- if (IS_VALLEYVIEW(dev)) {
- return index ? 0 : 100;
- } else if (intel_dig_port->port == PORT_A) {
- if (index)
- return 0;
- if (HAS_DDI(dev))
- return DIV_ROUND_CLOSEST(intel_ddi_get_cdclk_freq(dev_priv), 2000);
- else if (IS_GEN6(dev) || IS_GEN7(dev))
+ return index ? 0 : intel_hrawclk(dev) / 2;
+}
+
+static uint32_t ilk_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+
+ if (index)
+ return 0;
+
+ if (intel_dig_port->port == PORT_A) {
+ if (IS_GEN6(dev) || IS_GEN7(dev))
return 200; /* SNB & IVB eDP input clock at 400Mhz */
else
return 225; /* eDP input clock at 450Mhz */
+ } else {
+ return DIV_ROUND_UP(intel_pch_rawclk(dev), 2);
+ }
+}
+
+static uint32_t hsw_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (intel_dig_port->port == PORT_A) {
+ if (index)
+ return 0;
+ return DIV_ROUND_CLOSEST(intel_ddi_get_cdclk_freq(dev_priv), 2000);
} else if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) {
/* Workaround for non-ULT HSW */
switch (index) {
@@ -383,13 +406,46 @@ static uint32_t get_aux_clock_divider(struct intel_dp *intel_dp,
case 1: return 72;
default: return 0;
}
- } else if (HAS_PCH_SPLIT(dev)) {
+ } else {
return index ? 0 : DIV_ROUND_UP(intel_pch_rawclk(dev), 2);
- } else {
- return index ? 0 :intel_hrawclk(dev) / 2;
}
}
+static uint32_t vlv_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
+{
+ return index ? 0 : 100;
+}
+
+static uint32_t i9xx_get_aux_send_ctl(struct intel_dp *intel_dp,
+ bool has_aux_irq,
+ int send_bytes,
+ uint32_t aux_clock_divider)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ uint32_t precharge, timeout;
+
+ if (IS_GEN6(dev))
+ precharge = 3;
+ else
+ precharge = 5;
+
+ if (IS_BROADWELL(dev) && intel_dp->aux_ch_ctl_reg == DPA_AUX_CH_CTL)
+ timeout = DP_AUX_CH_CTL_TIME_OUT_600us;
+ else
+ timeout = DP_AUX_CH_CTL_TIME_OUT_400us;
+
+ return DP_AUX_CH_CTL_SEND_BUSY |
+ DP_AUX_CH_CTL_DONE |
+ (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
+ DP_AUX_CH_CTL_TIME_OUT_ERROR |
+ timeout |
+ DP_AUX_CH_CTL_RECEIVE_ERROR |
+ (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
+ (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
+ (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT);
+}
+
static int
intel_dp_aux_ch(struct intel_dp *intel_dp,
uint8_t *send, int send_bytes,
@@ -403,9 +459,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
uint32_t aux_clock_divider;
int i, ret, recv_bytes;
uint32_t status;
- int try, precharge, clock = 0;
+ int try, clock = 0;
bool has_aux_irq = HAS_AUX_IRQ(dev);
- uint32_t timeout;
+ bool vdd;
+
+ vdd = _edp_panel_vdd_on(intel_dp);
/* dp aux is extremely sensitive to irq latency, hence request the
* lowest possible wakeup latency and so prevent the cpu from going into
@@ -415,16 +473,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
intel_dp_check_edp(intel_dp);
- if (IS_GEN6(dev))
- precharge = 3;
- else
- precharge = 5;
-
- if (IS_BROADWELL(dev) && ch_ctl == DPA_AUX_CH_CTL)
- timeout = DP_AUX_CH_CTL_TIME_OUT_600us;
- else
- timeout = DP_AUX_CH_CTL_TIME_OUT_400us;
-
intel_aux_display_runtime_get(dev_priv);
/* Try to wait for any previous AUX channel activity */
@@ -448,7 +496,12 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
goto out;
}
- while ((aux_clock_divider = get_aux_clock_divider(intel_dp, clock++))) {
+ while ((aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, clock++))) {
+ u32 send_ctl = intel_dp->get_aux_send_ctl(intel_dp,
+ has_aux_irq,
+ send_bytes,
+ aux_clock_divider);
+
/* Must try at least 3 times according to DP spec */
for (try = 0; try < 5; try++) {
/* Load the send data into the aux channel data registers */
@@ -457,16 +510,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
pack_aux(send + i, send_bytes - i));
/* Send the command and wait for it to complete */
- I915_WRITE(ch_ctl,
- DP_AUX_CH_CTL_SEND_BUSY |
- (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) |
- timeout |
- (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
- (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
- (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
- DP_AUX_CH_CTL_DONE |
- DP_AUX_CH_CTL_TIME_OUT_ERROR |
- DP_AUX_CH_CTL_RECEIVE_ERROR);
+ I915_WRITE(ch_ctl, send_ctl);
status = intel_dp_aux_wait_done(intel_dp, has_aux_irq);
@@ -525,246 +569,140 @@ out:
pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE);
intel_aux_display_runtime_put(dev_priv);
+ if (vdd)
+ edp_panel_vdd_off(intel_dp, false);
+
return ret;
}
-/* Write data to the aux channel in native mode */
-static int
-intel_dp_aux_native_write(struct intel_dp *intel_dp,
- uint16_t address, uint8_t *send, int send_bytes)
+#define HEADER_SIZE 4
+static ssize_t
+intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
+ struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux);
+ uint8_t txbuf[20], rxbuf[20];
+ size_t txsize, rxsize;
int ret;
- uint8_t msg[20];
- int msg_bytes;
- uint8_t ack;
- int retry;
- if (WARN_ON(send_bytes > 16))
- return -E2BIG;
+ txbuf[0] = msg->request << 4;
+ txbuf[1] = msg->address >> 8;
+ txbuf[2] = msg->address & 0xff;
+ txbuf[3] = msg->size - 1;
- intel_dp_check_edp(intel_dp);
- msg[0] = DP_AUX_NATIVE_WRITE << 4;
- msg[1] = address >> 8;
- msg[2] = address & 0xff;
- msg[3] = send_bytes - 1;
- memcpy(&msg[4], send, send_bytes);
- msg_bytes = send_bytes + 4;
- for (retry = 0; retry < 7; retry++) {
- ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, &ack, 1);
- if (ret < 0)
- return ret;
- ack >>= 4;
- if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK)
- return send_bytes;
- else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER)
- usleep_range(400, 500);
- else
- return -EIO;
- }
+ switch (msg->request & ~DP_AUX_I2C_MOT) {
+ case DP_AUX_NATIVE_WRITE:
+ case DP_AUX_I2C_WRITE:
+ txsize = HEADER_SIZE + msg->size;
+ rxsize = 1;
- DRM_ERROR("too many retries, giving up\n");
- return -EIO;
-}
+ if (WARN_ON(txsize > 20))
+ return -E2BIG;
-/* Write a single byte to the aux channel in native mode */
-static int
-intel_dp_aux_native_write_1(struct intel_dp *intel_dp,
- uint16_t address, uint8_t byte)
-{
- return intel_dp_aux_native_write(intel_dp, address, &byte, 1);
-}
+ memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
-/* read bytes from a native aux channel */
-static int
-intel_dp_aux_native_read(struct intel_dp *intel_dp,
- uint16_t address, uint8_t *recv, int recv_bytes)
-{
- uint8_t msg[4];
- int msg_bytes;
- uint8_t reply[20];
- int reply_bytes;
- uint8_t ack;
- int ret;
- int retry;
+ ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
+ if (ret > 0) {
+ msg->reply = rxbuf[0] >> 4;
+
+ /* Return payload size. */
+ ret = msg->size;
+ }
+ break;
- if (WARN_ON(recv_bytes > 19))
- return -E2BIG;
+ case DP_AUX_NATIVE_READ:
+ case DP_AUX_I2C_READ:
+ txsize = HEADER_SIZE;
+ rxsize = msg->size + 1;
- intel_dp_check_edp(intel_dp);
- msg[0] = DP_AUX_NATIVE_READ << 4;
- msg[1] = address >> 8;
- msg[2] = address & 0xff;
- msg[3] = recv_bytes - 1;
-
- msg_bytes = 4;
- reply_bytes = recv_bytes + 1;
-
- for (retry = 0; retry < 7; retry++) {
- ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes,
- reply, reply_bytes);
- if (ret == 0)
- return -EPROTO;
- if (ret < 0)
- return ret;
- ack = reply[0] >> 4;
- if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) {
- memcpy(recv, reply + 1, ret - 1);
- return ret - 1;
+ if (WARN_ON(rxsize > 20))
+ return -E2BIG;
+
+ ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
+ if (ret > 0) {
+ msg->reply = rxbuf[0] >> 4;
+ /*
+ * Assume happy day, and copy the data. The caller is
+ * expected to check msg->reply before touching it.
+ *
+ * Return payload size.
+ */
+ ret--;
+ memcpy(msg->buffer, rxbuf + 1, ret);
}
- else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER)
- usleep_range(400, 500);
- else
- return -EIO;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
}
- DRM_ERROR("too many retries, giving up\n");
- return -EIO;
+ return ret;
}
-static int
-intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
- uint8_t write_byte, uint8_t *read_byte)
-{
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
- struct intel_dp *intel_dp = container_of(adapter,
- struct intel_dp,
- adapter);
- uint16_t address = algo_data->address;
- uint8_t msg[5];
- uint8_t reply[2];
- unsigned retry;
- int msg_bytes;
- int reply_bytes;
+static void
+intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
+{
+ struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ enum port port = intel_dig_port->port;
+ const char *name = NULL;
int ret;
- ironlake_edp_panel_vdd_on(intel_dp);
- intel_dp_check_edp(intel_dp);
- /* Set up the command byte */
- if (mode & MODE_I2C_READ)
- msg[0] = DP_AUX_I2C_READ << 4;
- else
- msg[0] = DP_AUX_I2C_WRITE << 4;
-
- if (!(mode & MODE_I2C_STOP))
- msg[0] |= DP_AUX_I2C_MOT << 4;
-
- msg[1] = address >> 8;
- msg[2] = address;
-
- switch (mode) {
- case MODE_I2C_WRITE:
- msg[3] = 0;
- msg[4] = write_byte;
- msg_bytes = 5;
- reply_bytes = 1;
+ switch (port) {
+ case PORT_A:
+ intel_dp->aux_ch_ctl_reg = DPA_AUX_CH_CTL;
+ name = "DPDDC-A";
+ break;
+ case PORT_B:
+ intel_dp->aux_ch_ctl_reg = PCH_DPB_AUX_CH_CTL;
+ name = "DPDDC-B";
+ break;
+ case PORT_C:
+ intel_dp->aux_ch_ctl_reg = PCH_DPC_AUX_CH_CTL;
+ name = "DPDDC-C";
break;
- case MODE_I2C_READ:
- msg[3] = 0;
- msg_bytes = 4;
- reply_bytes = 2;
+ case PORT_D:
+ intel_dp->aux_ch_ctl_reg = PCH_DPD_AUX_CH_CTL;
+ name = "DPDDC-D";
break;
default:
- msg_bytes = 3;
- reply_bytes = 1;
- break;
+ BUG();
}
- /*
- * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device is
- * required to retry at least seven times upon receiving AUX_DEFER
- * before giving up the AUX transaction.
- */
- for (retry = 0; retry < 7; retry++) {
- ret = intel_dp_aux_ch(intel_dp,
- msg, msg_bytes,
- reply, reply_bytes);
- if (ret < 0) {
- DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
- goto out;
- }
+ if (!HAS_DDI(dev))
+ intel_dp->aux_ch_ctl_reg = intel_dp->output_reg + 0x10;
- switch ((reply[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK) {
- case DP_AUX_NATIVE_REPLY_ACK:
- /* I2C-over-AUX Reply field is only valid
- * when paired with AUX ACK.
- */
- break;
- case DP_AUX_NATIVE_REPLY_NACK:
- DRM_DEBUG_KMS("aux_ch native nack\n");
- ret = -EREMOTEIO;
- goto out;
- case DP_AUX_NATIVE_REPLY_DEFER:
- /*
- * For now, just give more slack to branch devices. We
- * could check the DPCD for I2C bit rate capabilities,
- * and if available, adjust the interval. We could also
- * be more careful with DP-to-Legacy adapters where a
- * long legacy cable may force very low I2C bit rates.
- */
- if (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
- DP_DWN_STRM_PORT_PRESENT)
- usleep_range(500, 600);
- else
- usleep_range(300, 400);
- continue;
- default:
- DRM_ERROR("aux_ch invalid native reply 0x%02x\n",
- reply[0]);
- ret = -EREMOTEIO;
- goto out;
- }
+ intel_dp->aux.name = name;
+ intel_dp->aux.dev = dev->dev;
+ intel_dp->aux.transfer = intel_dp_aux_transfer;
- switch ((reply[0] >> 4) & DP_AUX_I2C_REPLY_MASK) {
- case DP_AUX_I2C_REPLY_ACK:
- if (mode == MODE_I2C_READ) {
- *read_byte = reply[1];
- }
- ret = reply_bytes - 1;
- goto out;
- case DP_AUX_I2C_REPLY_NACK:
- DRM_DEBUG_KMS("aux_i2c nack\n");
- ret = -EREMOTEIO;
- goto out;
- case DP_AUX_I2C_REPLY_DEFER:
- DRM_DEBUG_KMS("aux_i2c defer\n");
- udelay(100);
- break;
- default:
- DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]);
- ret = -EREMOTEIO;
- goto out;
- }
- }
+ DRM_DEBUG_KMS("registering %s bus for %s\n", name,
+ connector->base.kdev->kobj.name);
- DRM_ERROR("too many retries, giving up\n");
- ret = -EREMOTEIO;
+ ret = drm_dp_aux_register_i2c_bus(&intel_dp->aux);
+ if (ret < 0) {
+ DRM_ERROR("drm_dp_aux_register_i2c_bus() for %s failed (%d)\n",
+ name, ret);
+ return;
+ }
-out:
- ironlake_edp_panel_vdd_off(intel_dp, false);
- return ret;
+ ret = sysfs_create_link(&connector->base.kdev->kobj,
+ &intel_dp->aux.ddc.dev.kobj,
+ intel_dp->aux.ddc.dev.kobj.name);
+ if (ret < 0) {
+ DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret);
+ drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+ }
}
-static int
-intel_dp_i2c_init(struct intel_dp *intel_dp,
- struct intel_connector *intel_connector, const char *name)
+static void
+intel_dp_connector_unregister(struct intel_connector *intel_connector)
{
- int ret;
-
- DRM_DEBUG_KMS("i2c_init %s\n", name);
- intel_dp->algo.running = false;
- intel_dp->algo.address = 0;
- intel_dp->algo.aux_ch = intel_dp_i2c_aux_ch;
-
- memset(&intel_dp->adapter, '\0', sizeof(intel_dp->adapter));
- intel_dp->adapter.owner = THIS_MODULE;
- intel_dp->adapter.class = I2C_CLASS_DDC;
- strncpy(intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
- intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
- intel_dp->adapter.algo_data = &intel_dp->algo;
- intel_dp->adapter.dev.parent = intel_connector->base.kdev;
+ struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
- ret = i2c_dp_aux_add_bus(&intel_dp->adapter);
- return ret;
+ sysfs_remove_link(&intel_connector->base.kdev->kobj,
+ intel_dp->aux.ddc.dev.kobj.name);
+ intel_connector_unregister(intel_connector);
}
static void
@@ -812,9 +750,10 @@ intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_connector *intel_connector = intel_dp->attached_connector;
int lane_count, clock;
int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
- int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0;
+ /* Conveniently, the link BW constants become indices with a shift...*/
+ int max_clock = intel_dp_max_link_bw(intel_dp) >> 3;
int bpp, mode_rate;
- static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
+ static int bws[] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7, DP_LINK_BW_5_4 };
int link_avail, link_clock;
if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev) && port != PORT_A)
@@ -855,8 +794,8 @@ intel_dp_compute_config(struct intel_encoder *encoder,
mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock,
bpp);
- for (clock = 0; clock <= max_clock; clock++) {
- for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
+ for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
+ for (clock = 0; clock <= max_clock; clock++) {
link_clock = drm_dp_bw_code_to_link_rate(bws[clock]);
link_avail = intel_dp_max_data_rate(link_clock,
lane_count);
@@ -1015,16 +954,16 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
ironlake_set_pll_cpu_edp(intel_dp);
}
-#define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
-#define IDLE_ON_VALUE (PP_ON | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE)
+#define IDLE_ON_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
+#define IDLE_ON_VALUE (PP_ON | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE)
-#define IDLE_OFF_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
-#define IDLE_OFF_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
+#define IDLE_OFF_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | 0)
+#define IDLE_OFF_VALUE (0 | PP_SEQUENCE_NONE | 0 | 0)
-#define IDLE_CYCLE_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK)
-#define IDLE_CYCLE_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
+#define IDLE_CYCLE_MASK (PP_ON | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK)
+#define IDLE_CYCLE_VALUE (0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
-static void ironlake_wait_panel_status(struct intel_dp *intel_dp,
+static void wait_panel_status(struct intel_dp *intel_dp,
u32 mask,
u32 value)
{
@@ -1049,24 +988,41 @@ static void ironlake_wait_panel_status(struct intel_dp *intel_dp,
DRM_DEBUG_KMS("Wait complete\n");
}
-static void ironlake_wait_panel_on(struct intel_dp *intel_dp)
+static void wait_panel_on(struct intel_dp *intel_dp)
{
DRM_DEBUG_KMS("Wait for panel power on\n");
- ironlake_wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE);
+ wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE);
}
-static void ironlake_wait_panel_off(struct intel_dp *intel_dp)
+static void wait_panel_off(struct intel_dp *intel_dp)
{
DRM_DEBUG_KMS("Wait for panel power off time\n");
- ironlake_wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE);
+ wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE);
}
-static void ironlake_wait_panel_power_cycle(struct intel_dp *intel_dp)
+static void wait_panel_power_cycle(struct intel_dp *intel_dp)
{
DRM_DEBUG_KMS("Wait for panel power cycle\n");
- ironlake_wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE);
+
+ /* When we disable the VDD override bit last we have to do the manual
+ * wait. */
+ wait_remaining_ms_from_jiffies(intel_dp->last_power_cycle,
+ intel_dp->panel_power_cycle_delay);
+
+ wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE);
+}
+
+static void wait_backlight_on(struct intel_dp *intel_dp)
+{
+ wait_remaining_ms_from_jiffies(intel_dp->last_power_on,
+ intel_dp->backlight_on_delay);
}
+static void edp_wait_backlight_off(struct intel_dp *intel_dp)
+{
+ wait_remaining_ms_from_jiffies(intel_dp->last_backlight_off,
+ intel_dp->backlight_off_delay);
+}
/* Read the current pp_control value, unlocking the register if it
* is locked
@@ -1084,30 +1040,28 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
return control;
}
-void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
+static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
u32 pp_stat_reg, pp_ctrl_reg;
+ bool need_to_disable = !intel_dp->want_panel_vdd;
if (!is_edp(intel_dp))
- return;
-
- WARN(intel_dp->want_panel_vdd,
- "eDP VDD already requested on\n");
+ return false;
intel_dp->want_panel_vdd = true;
- if (ironlake_edp_have_panel_vdd(intel_dp))
- return;
+ if (edp_have_panel_vdd(intel_dp))
+ return need_to_disable;
intel_runtime_pm_get(dev_priv);
DRM_DEBUG_KMS("Turning eDP VDD on\n");
- if (!ironlake_edp_have_panel_power(intel_dp))
- ironlake_wait_panel_power_cycle(intel_dp);
+ if (!edp_have_panel_power(intel_dp))
+ wait_panel_power_cycle(intel_dp);
pp = ironlake_get_pp_control(intel_dp);
pp |= EDP_FORCE_VDD;
@@ -1122,13 +1076,24 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
/*
* If the panel wasn't on, delay before accessing aux channel
*/
- if (!ironlake_edp_have_panel_power(intel_dp)) {
+ if (!edp_have_panel_power(intel_dp)) {
DRM_DEBUG_KMS("eDP was not running\n");
msleep(intel_dp->panel_power_up_delay);
}
+
+ return need_to_disable;
+}
+
+void intel_edp_panel_vdd_on(struct intel_dp *intel_dp)
+{
+ if (is_edp(intel_dp)) {
+ bool vdd = _edp_panel_vdd_on(intel_dp);
+
+ WARN(!vdd, "eDP VDD already requested on\n");
+ }
}
-static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
+static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1137,7 +1102,7 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
- if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {
+ if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) {
DRM_DEBUG_KMS("Turning eDP VDD off\n");
pp = ironlake_get_pp_control(intel_dp);
@@ -1154,24 +1119,24 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg));
if ((pp & POWER_TARGET_ON) == 0)
- msleep(intel_dp->panel_power_cycle_delay);
+ intel_dp->last_power_cycle = jiffies;
intel_runtime_pm_put(dev_priv);
}
}
-static void ironlake_panel_vdd_work(struct work_struct *__work)
+static void edp_panel_vdd_work(struct work_struct *__work)
{
struct intel_dp *intel_dp = container_of(to_delayed_work(__work),
struct intel_dp, panel_vdd_work);
struct drm_device *dev = intel_dp_to_dev(intel_dp);
mutex_lock(&dev->mode_config.mutex);
- ironlake_panel_vdd_off_sync(intel_dp);
+ edp_panel_vdd_off_sync(intel_dp);
mutex_unlock(&dev->mode_config.mutex);
}
-void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
+static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
{
if (!is_edp(intel_dp))
return;
@@ -1181,7 +1146,7 @@ void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
intel_dp->want_panel_vdd = false;
if (sync) {
- ironlake_panel_vdd_off_sync(intel_dp);
+ edp_panel_vdd_off_sync(intel_dp);
} else {
/*
* Queue the timer to fire a long
@@ -1193,7 +1158,7 @@ void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
}
}
-void ironlake_edp_panel_on(struct intel_dp *intel_dp)
+void intel_edp_panel_on(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1205,12 +1170,12 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp)
DRM_DEBUG_KMS("Turn eDP power on\n");
- if (ironlake_edp_have_panel_power(intel_dp)) {
+ if (edp_have_panel_power(intel_dp)) {
DRM_DEBUG_KMS("eDP power already on\n");
return;
}
- ironlake_wait_panel_power_cycle(intel_dp);
+ wait_panel_power_cycle(intel_dp);
pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
pp = ironlake_get_pp_control(intel_dp);
@@ -1228,7 +1193,8 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp)
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
- ironlake_wait_panel_on(intel_dp);
+ wait_panel_on(intel_dp);
+ intel_dp->last_power_on = jiffies;
if (IS_GEN5(dev)) {
pp |= PANEL_POWER_RESET; /* restore panel reset bit */
@@ -1237,7 +1203,7 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp)
}
}
-void ironlake_edp_panel_off(struct intel_dp *intel_dp)
+void intel_edp_panel_off(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1249,27 +1215,31 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp)
DRM_DEBUG_KMS("Turn eDP power off\n");
+ edp_wait_backlight_off(intel_dp);
+
WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
pp = ironlake_get_pp_control(intel_dp);
/* We need to switch off panel power _and_ force vdd, for otherwise some
* panels get very unhappy and cease to work. */
- pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE);
+ pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
+ EDP_BLC_ENABLE);
pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
+ intel_dp->want_panel_vdd = false;
+
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
- intel_dp->want_panel_vdd = false;
-
- ironlake_wait_panel_off(intel_dp);
+ intel_dp->last_power_cycle = jiffies;
+ wait_panel_off(intel_dp);
/* We got a reference when we enabled the VDD. */
intel_runtime_pm_put(dev_priv);
}
-void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
+void intel_edp_backlight_on(struct intel_dp *intel_dp)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_device *dev = intel_dig_port->base.base.dev;
@@ -1287,7 +1257,7 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
* link. So delay a bit to make sure the image is solid before
* allowing it to appear.
*/
- msleep(intel_dp->backlight_on_delay);
+ wait_backlight_on(intel_dp);
pp = ironlake_get_pp_control(intel_dp);
pp |= EDP_BLC_ENABLE;
@@ -1299,7 +1269,7 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
intel_panel_enable_backlight(intel_dp->attached_connector);
}
-void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
+void intel_edp_backlight_off(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1319,7 +1289,7 @@ void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
- msleep(intel_dp->backlight_off_delay);
+ intel_dp->last_backlight_off = jiffies;
}
static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
@@ -1383,8 +1353,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
return;
if (mode != DRM_MODE_DPMS_ON) {
- ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER,
- DP_SET_POWER_D3);
+ ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER,
+ DP_SET_POWER_D3);
if (ret != 1)
DRM_DEBUG_DRIVER("failed to write sink power state\n");
} else {
@@ -1393,9 +1363,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
* time to wake up.
*/
for (i = 0; i < 3; i++) {
- ret = intel_dp_aux_native_write_1(intel_dp,
- DP_SET_POWER,
- DP_SET_POWER_D0);
+ ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER,
+ DP_SET_POWER_D0);
if (ret == 1)
break;
msleep(1);
@@ -1410,7 +1379,14 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
enum port port = dp_to_dig_port(intel_dp)->port;
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 tmp = I915_READ(intel_dp->output_reg);
+ enum intel_display_power_domain power_domain;
+ u32 tmp;
+
+ power_domain = intel_display_port_power_domain(encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
+ tmp = I915_READ(intel_dp->output_reg);
if (!(tmp & DP_PORT_EN))
return false;
@@ -1604,19 +1580,19 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t aux_clock_divider = get_aux_clock_divider(intel_dp, 0);
+ uint32_t aux_clock_divider;
int precharge = 0x3;
int msg_size = 5; /* Header(4) + Message(1) */
+ aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0);
+
/* Enable PSR in sink */
if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT)
- intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
- DP_PSR_ENABLE &
- ~DP_PSR_MAIN_LINK_ACTIVE);
+ drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
+ DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE);
else
- intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
- DP_PSR_ENABLE |
- DP_PSR_MAIN_LINK_ACTIVE);
+ drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
+ DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE);
/* Setup AUX registers */
I915_WRITE(EDP_PSR_AUX_DATA1(dev), EDP_PSR_DPCD_COMMAND);
@@ -1659,7 +1635,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dig_port->base.base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj;
+ struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->primary->fb)->obj;
struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
dev_priv->psr.source_ok = false;
@@ -1675,7 +1651,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
return false;
}
- if (!i915_enable_psr) {
+ if (!i915.enable_psr) {
DRM_DEBUG_KMS("PSR disable by flag\n");
return false;
}
@@ -1692,7 +1668,7 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
return false;
}
- obj = to_intel_framebuffer(crtc->fb)->obj;
+ obj = to_intel_framebuffer(crtc->primary->fb)->obj;
if (obj->tiling_mode != I915_TILING_X ||
obj->fence_reg == I915_FENCE_REG_NONE) {
DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n");
@@ -1791,10 +1767,10 @@ static void intel_disable_dp(struct intel_encoder *encoder)
/* Make sure the panel is off before trying to change the mode. But also
* ensure that we have vdd while we switch off the panel. */
- ironlake_edp_panel_vdd_on(intel_dp);
- ironlake_edp_backlight_off(intel_dp);
+ intel_edp_panel_vdd_on(intel_dp);
+ intel_edp_backlight_off(intel_dp);
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
- ironlake_edp_panel_off(intel_dp);
+ intel_edp_panel_off(intel_dp);
/* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */
if (!(port == PORT_A || IS_VALLEYVIEW(dev)))
@@ -1824,11 +1800,11 @@ static void intel_enable_dp(struct intel_encoder *encoder)
if (WARN_ON(dp_reg & DP_PORT_EN))
return;
- ironlake_edp_panel_vdd_on(intel_dp);
+ intel_edp_panel_vdd_on(intel_dp);
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_start_link_train(intel_dp);
- ironlake_edp_panel_on(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp, true);
+ intel_edp_panel_on(intel_dp);
+ edp_panel_vdd_off(intel_dp, true);
intel_dp_complete_link_train(intel_dp);
intel_dp_stop_link_train(intel_dp);
}
@@ -1838,14 +1814,14 @@ static void g4x_enable_dp(struct intel_encoder *encoder)
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
intel_enable_dp(encoder);
- ironlake_edp_backlight_on(intel_dp);
+ intel_edp_backlight_on(intel_dp);
}
static void vlv_enable_dp(struct intel_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
- ironlake_edp_backlight_on(intel_dp);
+ intel_edp_backlight_on(intel_dp);
}
static void g4x_pre_enable_dp(struct intel_encoder *encoder)
@@ -1927,26 +1903,25 @@ static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
/*
* Native read with retry for link status and receiver capability reads for
* cases where the sink may still be asleep.
+ *
+ * Sinks are *supposed* to come up within 1ms from an off state, but we're also
+ * supposed to retry 3 times per the spec.
*/
-static bool
-intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address,
- uint8_t *recv, int recv_bytes)
+static ssize_t
+intel_dp_dpcd_read_wake(struct drm_dp_aux *aux, unsigned int offset,
+ void *buffer, size_t size)
{
- int ret, i;
+ ssize_t ret;
+ int i;
- /*
- * Sinks are *supposed* to come up within 1ms from an off state,
- * but we're also supposed to retry 3 times per the spec.
- */
for (i = 0; i < 3; i++) {
- ret = intel_dp_aux_native_read(intel_dp, address, recv,
- recv_bytes);
- if (ret == recv_bytes)
- return true;
+ ret = drm_dp_dpcd_read(aux, offset, buffer, size);
+ if (ret == size)
+ return ret;
msleep(1);
}
- return false;
+ return ret;
}
/*
@@ -1956,10 +1931,10 @@ intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address,
static bool
intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
{
- return intel_dp_aux_native_read_retry(intel_dp,
- DP_LANE0_1_STATUS,
- link_status,
- DP_LINK_STATUS_SIZE);
+ return intel_dp_dpcd_read_wake(&intel_dp->aux,
+ DP_LANE0_1_STATUS,
+ link_status,
+ DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE;
}
/*
@@ -2473,8 +2448,8 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
len = intel_dp->lane_count + 1;
}
- ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_PATTERN_SET,
- buf, len);
+ ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET,
+ buf, len);
return ret == len;
}
@@ -2503,9 +2478,8 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP,
I915_WRITE(intel_dp->output_reg, *DP);
POSTING_READ(intel_dp->output_reg);
- ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_LANE0_SET,
- intel_dp->train_set,
- intel_dp->lane_count);
+ ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
+ intel_dp->train_set, intel_dp->lane_count);
return ret == intel_dp->lane_count;
}
@@ -2561,11 +2535,11 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
link_config[1] = intel_dp->lane_count;
if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
- intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, link_config, 2);
+ drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
link_config[0] = 0;
link_config[1] = DP_SET_ANSI_8B10B;
- intel_dp_aux_native_write(intel_dp, DP_DOWNSPREAD_CTRL, link_config, 2);
+ drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
DP |= DP_PORT_EN;
@@ -2638,10 +2612,15 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
bool channel_eq = false;
int tries, cr_tries;
uint32_t DP = intel_dp->DP;
+ uint32_t training_pattern = DP_TRAINING_PATTERN_2;
+
+ /* Training Pattern 3 for HBR2 ot 1.2 devices that support it*/
+ if (intel_dp->link_bw == DP_LINK_BW_5_4 || intel_dp->use_tps3)
+ training_pattern = DP_TRAINING_PATTERN_3;
/* channel equalization */
if (!intel_dp_set_link_train(intel_dp, &DP,
- DP_TRAINING_PATTERN_2 |
+ training_pattern |
DP_LINK_SCRAMBLING_DISABLE)) {
DRM_ERROR("failed to start channel equalization\n");
return;
@@ -2668,7 +2647,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
intel_dp_start_link_train(intel_dp);
intel_dp_set_link_train(intel_dp, &DP,
- DP_TRAINING_PATTERN_2 |
+ training_pattern |
DP_LINK_SCRAMBLING_DISABLE);
cr_tries++;
continue;
@@ -2684,7 +2663,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
intel_dp_link_down(intel_dp);
intel_dp_start_link_train(intel_dp);
intel_dp_set_link_train(intel_dp, &DP,
- DP_TRAINING_PATTERN_2 |
+ training_pattern |
DP_LINK_SCRAMBLING_DISABLE);
tries = 0;
cr_tries++;
@@ -2803,8 +2782,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3];
- if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
- sizeof(intel_dp->dpcd)) == 0)
+ if (intel_dp_dpcd_read_wake(&intel_dp->aux, 0x000, intel_dp->dpcd,
+ sizeof(intel_dp->dpcd)) < 0)
return false; /* aux transfer failed */
hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd),
@@ -2817,15 +2796,23 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
/* Check if the panel supports PSR */
memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd));
if (is_edp(intel_dp)) {
- intel_dp_aux_native_read_retry(intel_dp, DP_PSR_SUPPORT,
- intel_dp->psr_dpcd,
- sizeof(intel_dp->psr_dpcd));
+ intel_dp_dpcd_read_wake(&intel_dp->aux, DP_PSR_SUPPORT,
+ intel_dp->psr_dpcd,
+ sizeof(intel_dp->psr_dpcd));
if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
dev_priv->psr.sink_support = true;
DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
}
}
+ /* Training Pattern 3 support */
+ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x12 &&
+ intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED) {
+ intel_dp->use_tps3 = true;
+ DRM_DEBUG_KMS("Displayport TPS3 supported");
+ } else
+ intel_dp->use_tps3 = false;
+
if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
DP_DWN_STRM_PORT_PRESENT))
return true; /* native DP sink */
@@ -2833,9 +2820,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
if (intel_dp->dpcd[DP_DPCD_REV] == 0x10)
return true; /* no per-port downstream info */
- if (intel_dp_aux_native_read_retry(intel_dp, DP_DOWNSTREAM_PORT_0,
- intel_dp->downstream_ports,
- DP_MAX_DOWNSTREAM_PORTS) == 0)
+ if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_DOWNSTREAM_PORT_0,
+ intel_dp->downstream_ports,
+ DP_MAX_DOWNSTREAM_PORTS) < 0)
return false; /* downstream port status fetch failed */
return true;
@@ -2849,38 +2836,61 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
if (!(intel_dp->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
return;
- ironlake_edp_panel_vdd_on(intel_dp);
+ intel_edp_panel_vdd_on(intel_dp);
- if (intel_dp_aux_native_read_retry(intel_dp, DP_SINK_OUI, buf, 3))
+ if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_SINK_OUI, buf, 3) == 3)
DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
- if (intel_dp_aux_native_read_retry(intel_dp, DP_BRANCH_OUI, buf, 3))
+ if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_BRANCH_OUI, buf, 3) == 3)
DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
- ironlake_edp_panel_vdd_off(intel_dp, false);
+ edp_panel_vdd_off(intel_dp, false);
}
-static bool
-intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
+int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
{
- int ret;
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct intel_crtc *intel_crtc =
+ to_intel_crtc(intel_dig_port->base.base.crtc);
+ u8 buf[1];
- ret = intel_dp_aux_native_read_retry(intel_dp,
- DP_DEVICE_SERVICE_IRQ_VECTOR,
- sink_irq_vector, 1);
- if (!ret)
- return false;
+ if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, buf) < 0)
+ return -EAGAIN;
- return true;
+ if (!(buf[0] & DP_TEST_CRC_SUPPORTED))
+ return -ENOTTY;
+
+ if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK,
+ DP_TEST_SINK_START) < 0)
+ return -EAGAIN;
+
+ /* Wait 2 vblanks to be sure we will have the correct CRC value */
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+ intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+ if (drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_CRC_R_CR, crc, 6) < 0)
+ return -EAGAIN;
+
+ drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, 0);
+ return 0;
+}
+
+static bool
+intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
+{
+ return intel_dp_dpcd_read_wake(&intel_dp->aux,
+ DP_DEVICE_SERVICE_IRQ_VECTOR,
+ sink_irq_vector, 1) == 1;
}
static void
intel_dp_handle_test_request(struct intel_dp *intel_dp)
{
/* NAK by default */
- intel_dp_aux_native_write_1(intel_dp, DP_TEST_RESPONSE, DP_TEST_NAK);
+ drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK);
}
/*
@@ -2919,9 +2929,9 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) {
/* Clear interrupt source */
- intel_dp_aux_native_write_1(intel_dp,
- DP_DEVICE_SERVICE_IRQ_VECTOR,
- sink_irq_vector);
+ drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_DEVICE_SERVICE_IRQ_VECTOR,
+ sink_irq_vector);
if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST)
intel_dp_handle_test_request(intel_dp);
@@ -2956,15 +2966,17 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
intel_dp->downstream_ports[0] & DP_DS_PORT_HPD) {
uint8_t reg;
- if (!intel_dp_aux_native_read_retry(intel_dp, DP_SINK_COUNT,
- &reg, 1))
+
+ if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_SINK_COUNT,
+ &reg, 1) < 0)
return connector_status_unknown;
+
return DP_GET_SINK_COUNT(reg) ? connector_status_connected
: connector_status_disconnected;
}
/* If no HPD, poke DDC gently */
- if (drm_probe_ddc(&intel_dp->adapter))
+ if (drm_probe_ddc(&intel_dp->aux.ddc))
return connector_status_connected;
/* Well we tried, say unknown for unreliable port types */
@@ -3106,10 +3118,14 @@ intel_dp_detect(struct drm_connector *connector, bool force)
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
enum drm_connector_status status;
+ enum intel_display_power_domain power_domain;
struct edid *edid = NULL;
intel_runtime_pm_get(dev_priv);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, drm_get_connector_name(connector));
@@ -3128,7 +3144,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
} else {
- edid = intel_dp_get_edid(connector, &intel_dp->adapter);
+ edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc);
if (edid) {
intel_dp->has_audio = drm_detect_monitor_audio(edid);
kfree(edid);
@@ -3140,21 +3156,32 @@ intel_dp_detect(struct drm_connector *connector, bool force)
status = connector_status_connected;
out:
+ intel_display_power_put(dev_priv, power_domain);
+
intel_runtime_pm_put(dev_priv);
+
return status;
}
static int intel_dp_get_modes(struct drm_connector *connector)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *intel_encoder = &intel_dig_port->base;
struct intel_connector *intel_connector = to_intel_connector(connector);
struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
int ret;
/* We should parse the EDID data and find out if it has an audio sink
*/
- ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ ret = intel_dp_get_edid_modes(connector, &intel_dp->aux.ddc);
+ intel_display_power_put(dev_priv, power_domain);
if (ret)
return ret;
@@ -3175,15 +3202,25 @@ static bool
intel_dp_detect_audio(struct drm_connector *connector)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *intel_encoder = &intel_dig_port->base;
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
struct edid *edid;
bool has_audio = false;
- edid = intel_dp_get_edid(connector, &intel_dp->adapter);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc);
if (edid) {
has_audio = drm_detect_monitor_audio(edid);
kfree(edid);
}
+ intel_display_power_put(dev_priv, power_domain);
+
return has_audio;
}
@@ -3298,12 +3335,12 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
struct intel_dp *intel_dp = &intel_dig_port->dp;
struct drm_device *dev = intel_dp_to_dev(intel_dp);
- i2c_del_adapter(&intel_dp->adapter);
+ drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
drm_encoder_cleanup(encoder);
if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
mutex_lock(&dev->mode_config.mutex);
- ironlake_panel_vdd_off_sync(intel_dp);
+ edp_panel_vdd_off_sync(intel_dp);
mutex_unlock(&dev->mode_config.mutex);
}
kfree(intel_dig_port);
@@ -3402,6 +3439,13 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
}
}
+static void intel_dp_init_panel_power_timestamps(struct intel_dp *intel_dp)
+{
+ intel_dp->last_power_cycle = jiffies;
+ intel_dp->last_power_on = jiffies;
+ intel_dp->last_backlight_off = jiffies;
+}
+
static void
intel_dp_init_panel_power_sequencer(struct drm_device *dev,
struct intel_dp *intel_dp,
@@ -3524,10 +3568,17 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe);
}
- /* And finally store the new values in the power sequencer. */
+ /*
+ * And finally store the new values in the power sequencer. The
+ * backlight delays are set to 1 because we do manual waits on them. For
+ * T8, even BSpec recommends doing it. For T9, if we don't do this,
+ * we'll end up waiting for the backlight off delay twice: once when we
+ * do the manual sleep, and once when we disable the panel and wait for
+ * the PP_STATUS bit to become zero.
+ */
pp_on = (seq->t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) |
- (seq->t8 << PANEL_LIGHT_ON_DELAY_SHIFT);
- pp_off = (seq->t9 << PANEL_LIGHT_OFF_DELAY_SHIFT) |
+ (1 << PANEL_LIGHT_ON_DELAY_SHIFT);
+ pp_off = (1 << PANEL_LIGHT_OFF_DELAY_SHIFT) |
(seq->t10 << PANEL_POWER_DOWN_DELAY_SHIFT);
/* Compute the divisor for the pp clock, simply match the Bspec
* formula. */
@@ -3562,14 +3613,14 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
}
static bool intel_edp_init_connector(struct intel_dp *intel_dp,
- struct intel_connector *intel_connector)
+ struct intel_connector *intel_connector,
+ struct edp_power_seq *power_seq)
{
struct drm_connector *connector = &intel_connector->base;
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_display_mode *fixed_mode = NULL;
- struct edp_power_seq power_seq = { 0 };
bool has_dpcd;
struct drm_display_mode *scan;
struct edid *edid;
@@ -3577,12 +3628,10 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
if (!is_edp(intel_dp))
return true;
- intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
-
/* Cache DPCD and EDID for edp. */
- ironlake_edp_panel_vdd_on(intel_dp);
+ intel_edp_panel_vdd_on(intel_dp);
has_dpcd = intel_dp_get_dpcd(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp, false);
+ edp_panel_vdd_off(intel_dp, false);
if (has_dpcd) {
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
@@ -3596,10 +3645,10 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
}
/* We now know it's not a ghost, init power sequence regs. */
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
- &power_seq);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq);
- edid = drm_get_edid(connector, &intel_dp->adapter);
+ mutex_lock(&dev->mode_config.mutex);
+ edid = drm_get_edid(connector, &intel_dp->aux.ddc);
if (edid) {
if (drm_add_edid_modes(connector, edid)) {
drm_mode_connector_update_edid_property(connector,
@@ -3629,8 +3678,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
if (fixed_mode)
fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
}
+ mutex_unlock(&dev->mode_config.mutex);
- intel_panel_init(&intel_connector->panel, fixed_mode);
+ intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
intel_panel_setup_backlight(connector);
return true;
@@ -3646,8 +3696,20 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
enum port port = intel_dig_port->port;
- const char *name = NULL;
- int type, error;
+ struct edp_power_seq power_seq = { 0 };
+ int type;
+
+ /* intel_dp vfuncs */
+ if (IS_VALLEYVIEW(dev))
+ intel_dp->get_aux_clock_divider = vlv_get_aux_clock_divider;
+ else if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+ intel_dp->get_aux_clock_divider = hsw_get_aux_clock_divider;
+ else if (HAS_PCH_SPLIT(dev))
+ intel_dp->get_aux_clock_divider = ilk_get_aux_clock_divider;
+ else
+ intel_dp->get_aux_clock_divider = i9xx_get_aux_clock_divider;
+
+ intel_dp->get_aux_send_ctl = i9xx_get_aux_send_ctl;
/* Preserve the current hw state. */
intel_dp->DP = I915_READ(intel_dp->output_reg);
@@ -3677,7 +3739,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
connector->doublescan_allowed = 0;
INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
- ironlake_panel_vdd_work);
+ edp_panel_vdd_work);
intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector);
@@ -3686,61 +3748,41 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
else
intel_connector->get_hw_state = intel_connector_get_hw_state;
+ intel_connector->unregister = intel_dp_connector_unregister;
- intel_dp->aux_ch_ctl_reg = intel_dp->output_reg + 0x10;
- if (HAS_DDI(dev)) {
- switch (intel_dig_port->port) {
- case PORT_A:
- intel_dp->aux_ch_ctl_reg = DPA_AUX_CH_CTL;
- break;
- case PORT_B:
- intel_dp->aux_ch_ctl_reg = PCH_DPB_AUX_CH_CTL;
- break;
- case PORT_C:
- intel_dp->aux_ch_ctl_reg = PCH_DPC_AUX_CH_CTL;
- break;
- case PORT_D:
- intel_dp->aux_ch_ctl_reg = PCH_DPD_AUX_CH_CTL;
- break;
- default:
- BUG();
- }
- }
-
- /* Set up the DDC bus. */
+ /* Set up the hotplug pin. */
switch (port) {
case PORT_A:
intel_encoder->hpd_pin = HPD_PORT_A;
- name = "DPDDC-A";
break;
case PORT_B:
intel_encoder->hpd_pin = HPD_PORT_B;
- name = "DPDDC-B";
break;
case PORT_C:
intel_encoder->hpd_pin = HPD_PORT_C;
- name = "DPDDC-C";
break;
case PORT_D:
intel_encoder->hpd_pin = HPD_PORT_D;
- name = "DPDDC-D";
break;
default:
BUG();
}
- error = intel_dp_i2c_init(intel_dp, intel_connector, name);
- WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n",
- error, port_name(port));
+ if (is_edp(intel_dp)) {
+ intel_dp_init_panel_power_timestamps(intel_dp);
+ intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+ }
+
+ intel_dp_aux_init(intel_dp, intel_connector);
intel_dp->psr_setup_done = false;
- if (!intel_edp_init_connector(intel_dp, intel_connector)) {
- i2c_del_adapter(&intel_dp->adapter);
+ if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
+ drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
mutex_lock(&dev->mode_config.mutex);
- ironlake_panel_vdd_off_sync(intel_dp);
+ edp_panel_vdd_off_sync(intel_dp);
mutex_unlock(&dev->mode_config.mutex);
}
drm_sysfs_connector_remove(connector);
@@ -3806,7 +3848,7 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 0;
intel_encoder->hot_plug = intel_dp_hot_plug;
if (!intel_dp_init_connector(intel_dig_port, intel_connector)) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index fbfaaba5cc3b..0542de982260 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -78,6 +78,12 @@
#define MAX_OUTPUTS 6
/* maximum connectors per crtcs in the mode set */
+/* Maximum cursor sizes */
+#define GEN2_CURSOR_WIDTH 64
+#define GEN2_CURSOR_HEIGHT 64
+#define CURSOR_WIDTH 256
+#define CURSOR_HEIGHT 256
+
#define INTEL_I2C_BUS_DVO 1
#define INTEL_I2C_BUS_SDVO 2
@@ -110,9 +116,10 @@ struct intel_framebuffer {
struct intel_fbdev {
struct drm_fb_helper helper;
- struct intel_framebuffer ifb;
+ struct intel_framebuffer *fb;
struct list_head fbdev_list;
struct drm_display_mode *our_mode;
+ int preferred_bpp;
};
struct intel_encoder {
@@ -124,11 +131,7 @@ struct intel_encoder {
struct intel_crtc *new_crtc;
int type;
- /*
- * Intel hw has only one MUX where encoders could be clone, hence a
- * simple flag is enough to compute the possible_clones mask.
- */
- bool cloneable;
+ unsigned int cloneable;
bool connectors_active;
void (*hot_plug)(struct intel_encoder *);
bool (*compute_config)(struct intel_encoder *,
@@ -187,6 +190,14 @@ struct intel_connector {
* and active (i.e. dpms ON state). */
bool (*get_hw_state)(struct intel_connector *);
+ /*
+ * Removes all interfaces through which the connector is accessible
+ * - like sysfs, debugfs entries -, so that no new operations can be
+ * started on the connector. Also makes sure all currently pending
+ * operations finish before returing.
+ */
+ void (*unregister)(struct intel_connector *);
+
/* Panel info for eDP and LVDS */
struct intel_panel panel;
@@ -210,6 +221,12 @@ typedef struct dpll {
int p;
} intel_clock_t;
+struct intel_plane_config {
+ bool tiled;
+ int size;
+ u32 base;
+};
+
struct intel_crtc_config {
/**
* quirks - bitfield with hw state readout quirks
@@ -356,9 +373,13 @@ struct intel_crtc {
uint32_t cursor_addr;
int16_t cursor_x, cursor_y;
int16_t cursor_width, cursor_height;
+ int16_t max_cursor_width, max_cursor_height;
bool cursor_visible;
+ struct intel_plane_config plane_config;
struct intel_crtc_config config;
+ struct intel_crtc_config *new_config;
+ bool new_enabled;
uint32_t ddi_pll_sel;
@@ -475,8 +496,7 @@ struct intel_dp {
uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
- struct i2c_adapter adapter;
- struct i2c_algo_dp_aux_data algo;
+ struct drm_dp_aux aux;
uint8_t train_set[4];
int panel_power_up_delay;
int panel_power_down_delay;
@@ -485,8 +505,22 @@ struct intel_dp {
int backlight_off_delay;
struct delayed_work panel_vdd_work;
bool want_panel_vdd;
+ unsigned long last_power_cycle;
+ unsigned long last_power_on;
+ unsigned long last_backlight_off;
bool psr_setup_done;
+ bool use_tps3;
struct intel_connector *attached_connector;
+
+ uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
+ /*
+ * This function returns the value we have to program the AUX_CTL
+ * register with to kick off an AUX transaction.
+ */
+ uint32_t (*get_aux_send_ctl)(struct intel_dp *dp,
+ bool has_aux_irq,
+ int send_bytes,
+ uint32_t aux_clock_divider);
};
struct intel_digital_port {
@@ -540,6 +574,7 @@ struct intel_unpin_work {
struct intel_set_config {
struct drm_encoder **save_connector_encoders;
struct drm_crtc **save_encoder_crtcs;
+ bool *save_crtc_enabled;
bool fb_changed;
bool mode_changed;
@@ -584,6 +619,8 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
/* i915_irq.c */
bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
enum pipe pipe, bool enable);
+bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe, bool enable);
bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
bool enable);
@@ -591,8 +628,8 @@ void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void hsw_pc8_disable_interrupts(struct drm_device *dev);
-void hsw_pc8_restore_interrupts(struct drm_device *dev);
+void hsw_runtime_pm_disable_interrupts(struct drm_device *dev);
+void hsw_runtime_pm_restore_interrupts(struct drm_device *dev);
/* intel_crt.c */
@@ -664,11 +701,10 @@ int intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
struct intel_ring_buffer *pipelined);
void intel_unpin_fb_obj(struct drm_i915_gem_object *obj);
-int intel_framebuffer_init(struct drm_device *dev,
- struct intel_framebuffer *ifb,
+struct drm_framebuffer *
+__intel_framebuffer_create(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_i915_gem_object *obj);
-void intel_framebuffer_fini(struct intel_framebuffer *fb);
void intel_prepare_page_flip(struct drm_device *dev, int plane);
void intel_finish_page_flip(struct drm_device *dev, int pipe);
void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
@@ -696,9 +732,8 @@ unsigned long intel_gen4_compute_page_offset(int *x, int *y,
unsigned int bpp,
unsigned int pitch);
void intel_display_handle_reset(struct drm_device *dev);
-void hsw_enable_pc8_work(struct work_struct *__work);
-void hsw_enable_package_c8(struct drm_i915_private *dev_priv);
-void hsw_disable_package_c8(struct drm_i915_private *dev_priv);
+void hsw_enable_pc8(struct drm_i915_private *dev_priv);
+void hsw_disable_pc8(struct drm_i915_private *dev_priv);
void intel_dp_get_m_n(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config);
int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n);
@@ -708,8 +743,13 @@ ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config,
bool intel_crtc_active(struct drm_crtc *crtc);
void hsw_enable_ips(struct intel_crtc *crtc);
void hsw_disable_ips(struct intel_crtc *crtc);
-void intel_display_set_init_power(struct drm_device *dev, bool enable);
+void intel_display_set_init_power(struct drm_i915_private *dev, bool enable);
+enum intel_display_power_domain
+intel_display_port_power_domain(struct intel_encoder *intel_encoder);
int valleyview_get_vco(struct drm_i915_private *dev_priv);
+void intel_mode_from_pipe_config(struct drm_display_mode *mode,
+ struct intel_crtc_config *pipe_config);
+int intel_format_to_fourcc(int format);
/* intel_dp.c */
void intel_dp_init(struct drm_device *dev, int output_reg, enum port port);
@@ -721,15 +761,15 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp);
void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
void intel_dp_encoder_destroy(struct drm_encoder *encoder);
void intel_dp_check_link_status(struct intel_dp *intel_dp);
+int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc);
bool intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config);
bool intel_dp_is_edp(struct drm_device *dev, enum port port);
-void ironlake_edp_backlight_on(struct intel_dp *intel_dp);
-void ironlake_edp_backlight_off(struct intel_dp *intel_dp);
-void ironlake_edp_panel_on(struct intel_dp *intel_dp);
-void ironlake_edp_panel_off(struct intel_dp *intel_dp);
-void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp);
-void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
+void intel_edp_backlight_on(struct intel_dp *intel_dp);
+void intel_edp_backlight_off(struct intel_dp *intel_dp);
+void intel_edp_panel_vdd_on(struct intel_dp *intel_dp);
+void intel_edp_panel_on(struct intel_dp *intel_dp);
+void intel_edp_panel_off(struct intel_dp *intel_dp);
void intel_edp_psr_enable(struct intel_dp *intel_dp);
void intel_edp_psr_disable(struct intel_dp *intel_dp);
void intel_edp_psr_update(struct drm_device *dev);
@@ -808,7 +848,8 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
/* intel_panel.c */
int intel_panel_init(struct intel_panel *panel,
- struct drm_display_mode *fixed_mode);
+ struct drm_display_mode *fixed_mode,
+ struct drm_display_mode *downclock_mode);
void intel_panel_fini(struct intel_panel *panel);
void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode);
@@ -845,18 +886,19 @@ bool intel_fbc_enabled(struct drm_device *dev);
void intel_update_fbc(struct drm_device *dev);
void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
void intel_gpu_ips_teardown(void);
-int intel_power_domains_init(struct drm_device *dev);
-void intel_power_domains_remove(struct drm_device *dev);
-bool intel_display_power_enabled(struct drm_device *dev,
+int intel_power_domains_init(struct drm_i915_private *);
+void intel_power_domains_remove(struct drm_i915_private *);
+bool intel_display_power_enabled(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain);
-bool intel_display_power_enabled_sw(struct drm_device *dev,
+bool intel_display_power_enabled_sw(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain);
-void intel_display_power_get(struct drm_device *dev,
+void intel_display_power_get(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain);
-void intel_display_power_put(struct drm_device *dev,
+void intel_display_power_put(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain);
-void intel_power_domains_init_hw(struct drm_device *dev);
-void intel_set_power_well(struct drm_device *dev, bool enable);
+void intel_power_domains_init_hw(struct drm_i915_private *dev_priv);
+void intel_init_gt_powersave(struct drm_device *dev);
+void intel_cleanup_gt_powersave(struct drm_device *dev);
void intel_enable_gt_powersave(struct drm_device *dev);
void intel_disable_gt_powersave(struct drm_device *dev);
void ironlake_teardown_rc6(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index fabbf0d895cf..33656647f8bc 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -243,11 +243,16 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ enum intel_display_power_domain power_domain;
u32 port, func;
enum pipe p;
DRM_DEBUG_KMS("\n");
+ power_domain = intel_display_port_power_domain(encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
/* XXX: this only works for one DSI output */
for (p = PIPE_A; p <= PIPE_B; p++) {
port = I915_READ(MIPI_PORT_CTRL(p));
@@ -488,8 +493,19 @@ static enum drm_connector_status
intel_dsi_detect(struct drm_connector *connector, bool force)
{
struct intel_dsi *intel_dsi = intel_attached_dsi(connector);
+ struct intel_encoder *intel_encoder = &intel_dsi->base;
+ enum intel_display_power_domain power_domain;
+ enum drm_connector_status connector_status;
+ struct drm_i915_private *dev_priv = intel_encoder->base.dev->dev_private;
+
DRM_DEBUG_KMS("\n");
- return intel_dsi->dev.dev_ops->detect(&intel_dsi->dev);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+
+ intel_display_power_get(dev_priv, power_domain);
+ connector_status = intel_dsi->dev.dev_ops->detect(&intel_dsi->dev);
+ intel_display_power_put(dev_priv, power_domain);
+
+ return connector_status;
}
static int intel_dsi_get_modes(struct drm_connector *connector)
@@ -586,6 +602,7 @@ bool intel_dsi_init(struct drm_device *dev)
intel_encoder->get_config = intel_dsi_get_config;
intel_connector->get_hw_state = intel_connector_get_hw_state;
+ intel_connector->unregister = intel_connector_unregister;
for (i = 0; i < ARRAY_SIZE(intel_dsi_devices); i++) {
dsi = &intel_dsi_devices[i];
@@ -603,7 +620,7 @@ bool intel_dsi_init(struct drm_device *dev)
intel_encoder->type = INTEL_OUTPUT_DSI;
intel_encoder->crtc_mask = (1 << 0); /* XXX */
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 0;
drm_connector_init(dev, connector, &intel_dsi_connector_funcs,
DRM_MODE_CONNECTOR_DSI);
@@ -624,7 +641,7 @@ bool intel_dsi_init(struct drm_device *dev)
}
fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
- intel_panel_init(&intel_connector->panel, fixed_mode);
+ intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
return true;
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index eeff998e52ef..7fe3feedfe03 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -477,6 +477,7 @@ void intel_dvo_init(struct drm_device *dev)
intel_encoder->compute_config = intel_dvo_compute_config;
intel_encoder->mode_set = intel_dvo_mode_set;
intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
+ intel_connector->unregister = intel_connector_unregister;
/* Now, try to find a controller */
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
@@ -521,14 +522,15 @@ void intel_dvo_init(struct drm_device *dev)
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
switch (dvo->type) {
case INTEL_DVO_CHIP_TMDS:
- intel_encoder->cloneable = true;
+ intel_encoder->cloneable = (1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO);
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_DVII);
encoder_type = DRM_MODE_ENCODER_TMDS;
break;
case INTEL_DVO_CHIP_LVDS:
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 0;
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 39eac9937a4a..b4d44e62f0c7 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -62,6 +62,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper,
{
struct intel_fbdev *ifbdev =
container_of(helper, struct intel_fbdev, helper);
+ struct drm_framebuffer *fb;
struct drm_device *dev = helper->dev;
struct drm_mode_fb_cmd2 mode_cmd = {};
struct drm_i915_gem_object *obj;
@@ -93,18 +94,22 @@ static int intelfb_alloc(struct drm_fb_helper *helper,
/* Flush everything out, we'll be doing GTT only from now on */
ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
if (ret) {
- DRM_ERROR("failed to pin fb: %d\n", ret);
+ DRM_ERROR("failed to pin obj: %d\n", ret);
goto out_unref;
}
- ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj);
- if (ret)
+ fb = __intel_framebuffer_create(dev, &mode_cmd, obj);
+ if (IS_ERR(fb)) {
+ ret = PTR_ERR(fb);
goto out_unpin;
+ }
+
+ ifbdev->fb = to_intel_framebuffer(fb);
return 0;
out_unpin:
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
out_unref:
drm_gem_object_unreference(&obj->base);
out:
@@ -116,23 +121,26 @@ static int intelfb_create(struct drm_fb_helper *helper,
{
struct intel_fbdev *ifbdev =
container_of(helper, struct intel_fbdev, helper);
- struct intel_framebuffer *intel_fb = &ifbdev->ifb;
+ struct intel_framebuffer *intel_fb = ifbdev->fb;
struct drm_device *dev = helper->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct fb_info *info;
struct drm_framebuffer *fb;
struct drm_i915_gem_object *obj;
int size, ret;
+ bool prealloc = false;
mutex_lock(&dev->struct_mutex);
- if (!intel_fb->obj) {
+ if (!intel_fb || WARN_ON(!intel_fb->obj)) {
DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n");
ret = intelfb_alloc(helper, sizes);
if (ret)
goto out_unlock;
+ intel_fb = ifbdev->fb;
} else {
DRM_DEBUG_KMS("re-using BIOS fb\n");
+ prealloc = true;
sizes->fb_width = intel_fb->base.width;
sizes->fb_height = intel_fb->base.height;
}
@@ -148,7 +156,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
info->par = helper;
- fb = &ifbdev->ifb.base;
+ fb = &ifbdev->fb->base;
ifbdev->helper.fb = fb;
ifbdev->helper.fbdev = info;
@@ -194,7 +202,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
* If the object is stolen however, it will be full of whatever
* garbage was left in there.
*/
- if (ifbdev->ifb.obj->stolen)
+ if (ifbdev->fb->obj->stolen && !prealloc)
memset_io(info->screen_base, 0, info->screen_size);
/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
@@ -208,7 +216,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
return 0;
out_unpin:
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
drm_gem_object_unreference(&obj->base);
out_unlock:
mutex_unlock(&dev->struct_mutex);
@@ -236,7 +244,193 @@ static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
*blue = intel_crtc->lut_b[regno] << 8;
}
+static struct drm_fb_helper_crtc *
+intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
+{
+ int i;
+
+ for (i = 0; i < fb_helper->crtc_count; i++)
+ if (fb_helper->crtc_info[i].mode_set.crtc == crtc)
+ return &fb_helper->crtc_info[i];
+
+ return NULL;
+}
+
+/*
+ * Try to read the BIOS display configuration and use it for the initial
+ * fb configuration.
+ *
+ * The BIOS or boot loader will generally create an initial display
+ * configuration for us that includes some set of active pipes and displays.
+ * This routine tries to figure out which pipes and connectors are active
+ * and stuffs them into the crtcs and modes array given to us by the
+ * drm_fb_helper code.
+ *
+ * The overall sequence is:
+ * intel_fbdev_init - from driver load
+ * intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data
+ * drm_fb_helper_init - build fb helper structs
+ * drm_fb_helper_single_add_all_connectors - more fb helper structs
+ * intel_fbdev_initial_config - apply the config
+ * drm_fb_helper_initial_config - call ->probe then register_framebuffer()
+ * drm_setup_crtcs - build crtc config for fbdev
+ * intel_fb_initial_config - find active connectors etc
+ * drm_fb_helper_single_fb_probe - set up fbdev
+ * intelfb_create - re-use or alloc fb, build out fbdev structs
+ *
+ * Note that we don't make special consideration whether we could actually
+ * switch to the selected modes without a full modeset. E.g. when the display
+ * is in VGA mode we need to recalculate watermarks and set a new high-res
+ * framebuffer anyway.
+ */
+static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
+ struct drm_fb_helper_crtc **crtcs,
+ struct drm_display_mode **modes,
+ bool *enabled, int width, int height)
+{
+ struct drm_device *dev = fb_helper->dev;
+ int i, j;
+ bool *save_enabled;
+ bool fallback = true;
+ int num_connectors_enabled = 0;
+ int num_connectors_detected = 0;
+
+ /*
+ * If the user specified any force options, just bail here
+ * and use that config.
+ */
+ for (i = 0; i < fb_helper->connector_count; i++) {
+ struct drm_fb_helper_connector *fb_conn;
+ struct drm_connector *connector;
+
+ fb_conn = fb_helper->connector_info[i];
+ connector = fb_conn->connector;
+
+ if (!enabled[i])
+ continue;
+
+ if (connector->force != DRM_FORCE_UNSPECIFIED)
+ return false;
+ }
+
+ save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool),
+ GFP_KERNEL);
+ if (!save_enabled)
+ return false;
+
+ memcpy(save_enabled, enabled, dev->mode_config.num_connector);
+
+ for (i = 0; i < fb_helper->connector_count; i++) {
+ struct drm_fb_helper_connector *fb_conn;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_fb_helper_crtc *new_crtc;
+
+ fb_conn = fb_helper->connector_info[i];
+ connector = fb_conn->connector;
+
+ if (connector->status == connector_status_connected)
+ num_connectors_detected++;
+
+ if (!enabled[i]) {
+ DRM_DEBUG_KMS("connector %d not enabled, skipping\n",
+ connector->base.id);
+ continue;
+ }
+
+ encoder = connector->encoder;
+ if (!encoder || WARN_ON(!encoder->crtc)) {
+ DRM_DEBUG_KMS("connector %d has no encoder or crtc, skipping\n",
+ connector->base.id);
+ enabled[i] = false;
+ continue;
+ }
+
+ num_connectors_enabled++;
+
+ new_crtc = intel_fb_helper_crtc(fb_helper, encoder->crtc);
+
+ /*
+ * Make sure we're not trying to drive multiple connectors
+ * with a single CRTC, since our cloning support may not
+ * match the BIOS.
+ */
+ for (j = 0; j < fb_helper->connector_count; j++) {
+ if (crtcs[j] == new_crtc) {
+ DRM_DEBUG_KMS("fallback: cloned configuration\n");
+ fallback = true;
+ goto out;
+ }
+ }
+
+ DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
+ fb_conn->connector->base.id);
+
+ /* go for command line mode first */
+ modes[i] = drm_pick_cmdline_mode(fb_conn, width, height);
+
+ /* try for preferred next */
+ if (!modes[i]) {
+ DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
+ fb_conn->connector->base.id);
+ modes[i] = drm_has_preferred_mode(fb_conn, width,
+ height);
+ }
+
+ /* last resort: use current mode */
+ if (!modes[i]) {
+ /*
+ * IMPORTANT: We want to use the adjusted mode (i.e.
+ * after the panel fitter upscaling) as the initial
+ * config, not the input mode, which is what crtc->mode
+ * usually contains. But since our current fastboot
+ * code puts a mode derived from the post-pfit timings
+ * into crtc->mode this works out correctly. We don't
+ * use hwmode anywhere right now, so use it for this
+ * since the fb helper layer wants a pointer to
+ * something we own.
+ */
+ intel_mode_from_pipe_config(&encoder->crtc->hwmode,
+ &to_intel_crtc(encoder->crtc)->config);
+ modes[i] = &encoder->crtc->hwmode;
+ }
+ crtcs[i] = new_crtc;
+
+ DRM_DEBUG_KMS("connector %s on crtc %d: %s\n",
+ drm_get_connector_name(connector),
+ encoder->crtc->base.id,
+ modes[i]->name);
+
+ fallback = false;
+ }
+
+ /*
+ * If the BIOS didn't enable everything it could, fall back to have the
+ * same user experiencing of lighting up as much as possible like the
+ * fbdev helper library.
+ */
+ if (num_connectors_enabled != num_connectors_detected &&
+ num_connectors_enabled < INTEL_INFO(dev)->num_pipes) {
+ DRM_DEBUG_KMS("fallback: Not all outputs enabled\n");
+ DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled,
+ num_connectors_detected);
+ fallback = true;
+ }
+
+out:
+ if (fallback) {
+ DRM_DEBUG_KMS("Not using firmware configuration\n");
+ memcpy(enabled, save_enabled, dev->mode_config.num_connector);
+ kfree(save_enabled);
+ return false;
+ }
+
+ kfree(save_enabled);
+ return true;
+}
+
static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+ .initial_config = intel_fb_initial_config,
.gamma_set = intel_crtc_fb_gamma_set,
.gamma_get = intel_crtc_fb_gamma_get,
.fb_probe = intelfb_create,
@@ -258,8 +452,139 @@ static void intel_fbdev_destroy(struct drm_device *dev,
drm_fb_helper_fini(&ifbdev->helper);
- drm_framebuffer_unregister_private(&ifbdev->ifb.base);
- intel_framebuffer_fini(&ifbdev->ifb);
+ drm_framebuffer_unregister_private(&ifbdev->fb->base);
+ drm_framebuffer_remove(&ifbdev->fb->base);
+}
+
+/*
+ * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible.
+ * The core display code will have read out the current plane configuration,
+ * so we use that to figure out if there's an object for us to use as the
+ * fb, and if so, we re-use it for the fbdev configuration.
+ *
+ * Note we only support a single fb shared across pipes for boot (mostly for
+ * fbcon), so we just find the biggest and use that.
+ */
+static bool intel_fbdev_init_bios(struct drm_device *dev,
+ struct intel_fbdev *ifbdev)
+{
+ struct intel_framebuffer *fb = NULL;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ struct intel_plane_config *plane_config = NULL;
+ unsigned int max_size = 0;
+
+ if (!i915.fastboot)
+ return false;
+
+ /* Find the largest fb */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ intel_crtc = to_intel_crtc(crtc);
+
+ if (!intel_crtc->active || !crtc->primary->fb) {
+ DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n",
+ pipe_name(intel_crtc->pipe));
+ continue;
+ }
+
+ if (intel_crtc->plane_config.size > max_size) {
+ DRM_DEBUG_KMS("found possible fb from plane %c\n",
+ pipe_name(intel_crtc->pipe));
+ plane_config = &intel_crtc->plane_config;
+ fb = to_intel_framebuffer(crtc->primary->fb);
+ max_size = plane_config->size;
+ }
+ }
+
+ if (!fb) {
+ DRM_DEBUG_KMS("no active fbs found, not using BIOS config\n");
+ goto out;
+ }
+
+ /* Now make sure all the pipes will fit into it */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ unsigned int cur_size;
+
+ intel_crtc = to_intel_crtc(crtc);
+
+ if (!intel_crtc->active) {
+ DRM_DEBUG_KMS("pipe %c not active, skipping\n",
+ pipe_name(intel_crtc->pipe));
+ continue;
+ }
+
+ DRM_DEBUG_KMS("checking plane %c for BIOS fb\n",
+ pipe_name(intel_crtc->pipe));
+
+ /*
+ * See if the plane fb we found above will fit on this
+ * pipe. Note we need to use the selected fb's pitch and bpp
+ * rather than the current pipe's, since they differ.
+ */
+ cur_size = intel_crtc->config.adjusted_mode.crtc_hdisplay;
+ cur_size = cur_size * fb->base.bits_per_pixel / 8;
+ if (fb->base.pitches[0] < cur_size) {
+ DRM_DEBUG_KMS("fb not wide enough for plane %c (%d vs %d)\n",
+ pipe_name(intel_crtc->pipe),
+ cur_size, fb->base.pitches[0]);
+ plane_config = NULL;
+ fb = NULL;
+ break;
+ }
+
+ cur_size = intel_crtc->config.adjusted_mode.crtc_vdisplay;
+ cur_size = ALIGN(cur_size, plane_config->tiled ? (IS_GEN2(dev) ? 16 : 8) : 1);
+ cur_size *= fb->base.pitches[0];
+ DRM_DEBUG_KMS("pipe %c area: %dx%d, bpp: %d, size: %d\n",
+ pipe_name(intel_crtc->pipe),
+ intel_crtc->config.adjusted_mode.crtc_hdisplay,
+ intel_crtc->config.adjusted_mode.crtc_vdisplay,
+ fb->base.bits_per_pixel,
+ cur_size);
+
+ if (cur_size > max_size) {
+ DRM_DEBUG_KMS("fb not big enough for plane %c (%d vs %d)\n",
+ pipe_name(intel_crtc->pipe),
+ cur_size, max_size);
+ plane_config = NULL;
+ fb = NULL;
+ break;
+ }
+
+ DRM_DEBUG_KMS("fb big enough for plane %c (%d >= %d)\n",
+ pipe_name(intel_crtc->pipe),
+ max_size, cur_size);
+ }
+
+ if (!fb) {
+ DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n");
+ goto out;
+ }
+
+ ifbdev->preferred_bpp = fb->base.bits_per_pixel;
+ ifbdev->fb = fb;
+
+ drm_framebuffer_reference(&ifbdev->fb->base);
+
+ /* Final pass to check if any active pipes don't have fbs */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ intel_crtc = to_intel_crtc(crtc);
+
+ if (!intel_crtc->active)
+ continue;
+
+ WARN(!crtc->primary->fb,
+ "re-used BIOS config but lost an fb on crtc %d\n",
+ crtc->base.id);
+ }
+
+
+ DRM_DEBUG_KMS("using BIOS fb for initial console\n");
+ return true;
+
+out:
+
+ return false;
}
int intel_fbdev_init(struct drm_device *dev)
@@ -268,21 +593,25 @@ int intel_fbdev_init(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);
- if (!ifbdev)
+ if (WARN_ON(INTEL_INFO(dev)->num_pipes == 0))
+ return -ENODEV;
+
+ ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL);
+ if (ifbdev == NULL)
return -ENOMEM;
- dev_priv->fbdev = ifbdev;
ifbdev->helper.funcs = &intel_fb_helper_funcs;
+ if (!intel_fbdev_init_bios(dev, ifbdev))
+ ifbdev->preferred_bpp = 32;
ret = drm_fb_helper_init(dev, &ifbdev->helper,
- INTEL_INFO(dev)->num_pipes,
- 4);
+ INTEL_INFO(dev)->num_pipes, 4);
if (ret) {
kfree(ifbdev);
return ret;
}
+ dev_priv->fbdev = ifbdev;
drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
return 0;
@@ -291,9 +620,10 @@ int intel_fbdev_init(struct drm_device *dev)
void intel_fbdev_initial_config(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_fbdev *ifbdev = dev_priv->fbdev;
/* Due to peculiar init order wrt to hpd handling this is separate. */
- drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32);
+ drm_fb_helper_initial_config(&ifbdev->helper, ifbdev->preferred_bpp);
}
void intel_fbdev_fini(struct drm_device *dev)
@@ -322,7 +652,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
* been restored from swap. If the object is stolen however, it will be
* full of whatever garbage was left in there.
*/
- if (state == FBINFO_STATE_RUNNING && ifbdev->ifb.obj->stolen)
+ if (state == FBINFO_STATE_RUNNING && ifbdev->fb->obj->stolen)
memset_io(info->screen_base, 0, info->screen_size);
fb_set_suspend(info, state);
@@ -331,7 +661,8 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
void intel_fbdev_output_poll_changed(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
+ if (dev_priv->fbdev)
+ drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
}
void intel_fbdev_restore_mode(struct drm_device *dev)
@@ -339,7 +670,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
int ret;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (INTEL_INFO(dev)->num_pipes == 0)
+ if (!dev_priv->fbdev)
return;
drm_modeset_lock_all(dev);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index ee3181ebcc92..b0413e190625 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -113,7 +113,8 @@ static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type)
}
static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type,
- enum transcoder cpu_transcoder)
+ enum transcoder cpu_transcoder,
+ struct drm_i915_private *dev_priv)
{
switch (type) {
case HDMI_INFOFRAME_TYPE_AVI:
@@ -296,7 +297,8 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
u32 val = I915_READ(ctl_reg);
data_reg = hsw_infoframe_data_reg(type,
- intel_crtc->config.cpu_transcoder);
+ intel_crtc->config.cpu_transcoder,
+ dev_priv);
if (data_reg == 0)
return;
@@ -423,7 +425,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
u32 reg = VIDEO_DIP_CTL;
u32 val = I915_READ(reg);
- u32 port;
+ u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
assert_hdmi_port_disabled(intel_hdmi);
@@ -447,18 +449,6 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
return;
}
- switch (intel_dig_port->port) {
- case PORT_B:
- port = VIDEO_DIP_PORT_B;
- break;
- case PORT_C:
- port = VIDEO_DIP_PORT_C;
- break;
- default:
- BUG();
- return;
- }
-
if (port != (val & VIDEO_DIP_PORT_MASK)) {
if (val & VIDEO_DIP_ENABLE) {
val &= ~VIDEO_DIP_ENABLE;
@@ -489,7 +479,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
u32 val = I915_READ(reg);
- u32 port;
+ u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
assert_hdmi_port_disabled(intel_hdmi);
@@ -505,21 +495,6 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
return;
}
- switch (intel_dig_port->port) {
- case PORT_B:
- port = VIDEO_DIP_PORT_B;
- break;
- case PORT_C:
- port = VIDEO_DIP_PORT_C;
- break;
- case PORT_D:
- port = VIDEO_DIP_PORT_D;
- break;
- default:
- BUG();
- return;
- }
-
if (port != (val & VIDEO_DIP_PORT_MASK)) {
if (val & VIDEO_DIP_ENABLE) {
val &= ~VIDEO_DIP_ENABLE;
@@ -692,8 +667,13 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+ enum intel_display_power_domain power_domain;
u32 tmp;
+ power_domain = intel_display_port_power_domain(encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
tmp = I915_READ(intel_hdmi->hdmi_reg);
if (!(tmp & SDVO_ENABLE))
@@ -868,6 +848,30 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
+static bool hdmi_12bpc_possible(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct intel_encoder *encoder;
+ int count = 0, count_hdmi = 0;
+
+ if (!HAS_PCH_SPLIT(dev))
+ return false;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ if (encoder->new_crtc != crtc)
+ continue;
+
+ count_hdmi += encoder->type == INTEL_OUTPUT_HDMI;
+ count++;
+ }
+
+ /*
+ * HDMI 12bpc affects the clocks, so it's only possible
+ * when not cloning with other encoder types.
+ */
+ return count_hdmi > 0 && count_hdmi == count;
+}
+
bool intel_hdmi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config)
{
@@ -900,7 +904,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
* within limits.
*/
if (pipe_config->pipe_bpp > 8*3 && intel_hdmi->has_hdmi_sink &&
- clock_12bpc <= portclock_limit && HAS_PCH_SPLIT(dev)) {
+ clock_12bpc <= portclock_limit &&
+ hdmi_12bpc_possible(encoder->new_crtc)) {
DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
desired_bpp = 12*3;
@@ -934,11 +939,15 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
struct intel_encoder *intel_encoder = &intel_dig_port->base;
struct drm_i915_private *dev_priv = dev->dev_private;
struct edid *edid;
+ enum intel_display_power_domain power_domain;
enum drm_connector_status status = connector_status_disconnected;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, drm_get_connector_name(connector));
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
intel_hdmi->has_hdmi_sink = false;
intel_hdmi->has_audio = false;
intel_hdmi->rgb_quant_range_selectable = false;
@@ -966,31 +975,48 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
intel_encoder->type = INTEL_OUTPUT_HDMI;
}
+ intel_display_power_put(dev_priv, power_domain);
+
return status;
}
static int intel_hdmi_get_modes(struct drm_connector *connector)
{
- struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+ struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ enum intel_display_power_domain power_domain;
+ int ret;
/* We should parse the EDID data and find out if it's an HDMI sink so
* we can send audio to it.
*/
- return intel_ddc_get_modes(connector,
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ ret = intel_ddc_get_modes(connector,
intel_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus));
+
+ intel_display_power_put(dev_priv, power_domain);
+
+ return ret;
}
static bool
intel_hdmi_detect_audio(struct drm_connector *connector)
{
- struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+ struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ enum intel_display_power_domain power_domain;
struct edid *edid;
bool has_audio = false;
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
edid = drm_get_edid(connector,
intel_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus));
@@ -1000,6 +1026,8 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
kfree(edid);
}
+ intel_display_power_put(dev_priv, power_domain);
+
return has_audio;
}
@@ -1261,6 +1289,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
else
intel_connector->get_hw_state = intel_connector_get_hw_state;
+ intel_connector->unregister = intel_connector_unregister;
intel_hdmi_add_properties(intel_hdmi, connector);
@@ -1314,7 +1343,14 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
intel_encoder->type = INTEL_OUTPUT_HDMI;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG;
+ /*
+ * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems
+ * to work on real hardware. And since g4x can send infoframes to
+ * only one port anyway, nothing is lost by allowing it.
+ */
+ if (IS_G4X(dev))
+ intel_encoder->cloneable |= 1 << INTEL_OUTPUT_HDMI;
intel_dig_port->port = port;
intel_dig_port->hdmi.hdmi_reg = hdmi_reg;
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 8bcb93a2a9f6..f1ecf916474a 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -848,8 +848,8 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
struct drm_i915_private *dev_priv = dev->dev_private;
/* use the module option value if specified */
- if (i915_lvds_channel_mode > 0)
- return i915_lvds_channel_mode == 2;
+ if (i915.lvds_channel_mode > 0)
+ return i915.lvds_channel_mode == 2;
if (dmi_check_system(intel_dual_link_lvds))
return true;
@@ -899,6 +899,7 @@ void intel_lvds_init(struct drm_device *dev)
struct drm_encoder *encoder;
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_display_mode *fixed_mode = NULL;
+ struct drm_display_mode *downclock_mode = NULL;
struct edid *edid;
struct drm_crtc *crtc;
u32 lvds;
@@ -957,11 +958,12 @@ void intel_lvds_init(struct drm_device *dev)
intel_encoder->get_hw_state = intel_lvds_get_hw_state;
intel_encoder->get_config = intel_lvds_get_config;
intel_connector->get_hw_state = intel_connector_get_hw_state;
+ intel_connector->unregister = intel_connector_unregister;
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_LVDS;
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 0;
if (HAS_PCH_SPLIT(dev))
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
else if (IS_GEN4(dev))
@@ -1000,6 +1002,7 @@ void intel_lvds_init(struct drm_device *dev)
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
+ mutex_lock(&dev->mode_config.mutex);
edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, pin));
if (edid) {
if (drm_add_edid_modes(connector, edid)) {
@@ -1032,15 +1035,14 @@ void intel_lvds_init(struct drm_device *dev)
fixed_mode = drm_mode_duplicate(dev, scan);
if (fixed_mode) {
- intel_connector->panel.downclock_mode =
+ downclock_mode =
intel_find_panel_downclock(dev,
fixed_mode, connector);
- if (intel_connector->panel.downclock_mode !=
- NULL && i915_lvds_downclock) {
+ if (downclock_mode != NULL &&
+ i915.lvds_downclock) {
/* We found the downclock for LVDS. */
dev_priv->lvds_downclock_avail = true;
dev_priv->lvds_downclock =
- intel_connector->panel.
downclock_mode->clock;
DRM_DEBUG_KMS("LVDS downclock is found"
" in EDID. Normal clock %dKhz, "
@@ -1094,6 +1096,8 @@ void intel_lvds_init(struct drm_device *dev)
goto failed;
out:
+ mutex_unlock(&dev->mode_config.mutex);
+
lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
lvds_encoder->is_dual_link ? "dual" : "single");
@@ -1116,17 +1120,17 @@ out:
}
drm_sysfs_connector_add(connector);
- intel_panel_init(&intel_connector->panel, fixed_mode);
+ intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
intel_panel_setup_backlight(connector);
return;
failed:
+ mutex_unlock(&dev->mode_config.mutex);
+
DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
drm_connector_cleanup(connector);
drm_encoder_cleanup(encoder);
- if (fixed_mode)
- drm_mode_destroy(dev, fixed_mode);
kfree(lvds_encoder);
kfree(lvds_connector);
return;
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index a759ecdb7a6e..d8adc9104dca 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -189,7 +189,7 @@ struct intel_overlay {
static struct overlay_registers __iomem *
intel_overlay_map_regs(struct intel_overlay *overlay)
{
- drm_i915_private_t *dev_priv = overlay->dev->dev_private;
+ struct drm_i915_private *dev_priv = overlay->dev->dev_private;
struct overlay_registers __iomem *regs;
if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
@@ -212,7 +212,7 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
void (*tail)(struct intel_overlay *))
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
@@ -262,7 +262,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
bool load_polyphase_filter)
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
u32 flip_addr = overlay->flip_addr;
u32 tmp;
@@ -293,7 +293,7 @@ static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
{
struct drm_i915_gem_object *obj = overlay->old_vid_bo;
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
drm_gem_object_unreference(&obj->base);
overlay->old_vid_bo = NULL;
@@ -306,7 +306,7 @@ static void intel_overlay_off_tail(struct intel_overlay *overlay)
/* never have the overlay hw on without showing a frame */
BUG_ON(!overlay->vid_bo);
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
drm_gem_object_unreference(&obj->base);
overlay->vid_bo = NULL;
@@ -362,7 +362,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
@@ -388,7 +388,7 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
@@ -606,14 +606,14 @@ static void update_colorkey(struct intel_overlay *overlay,
{
u32 key = overlay->color_key;
- switch (overlay->crtc->base.fb->bits_per_pixel) {
+ switch (overlay->crtc->base.primary->fb->bits_per_pixel) {
case 8:
iowrite32(0, &regs->DCLRKV);
iowrite32(CLK_RGB8I_MASK | DST_KEY_ENABLE, &regs->DCLRKM);
break;
case 16:
- if (overlay->crtc->base.fb->depth == 15) {
+ if (overlay->crtc->base.primary->fb->depth == 15) {
iowrite32(RGB15_TO_COLORKEY(key), &regs->DCLRKV);
iowrite32(CLK_RGB15_MASK | DST_KEY_ENABLE,
&regs->DCLRKM);
@@ -782,7 +782,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
return 0;
out_unpin:
- i915_gem_object_unpin(new_bo);
+ i915_gem_object_ggtt_unpin(new_bo);
return ret;
}
@@ -834,7 +834,7 @@ static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 pfit_control = I915_READ(PFIT_CONTROL);
u32 ratio;
@@ -1026,7 +1026,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_intel_overlay_put_image *put_image_rec = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct drm_mode_object *drmmode_obj;
struct intel_crtc *crtc;
@@ -1076,7 +1076,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
mutex_lock(&dev->struct_mutex);
if (new_bo->tiling_mode) {
- DRM_ERROR("buffer used for overlay image can not be tiled\n");
+ DRM_DEBUG_KMS("buffer used for overlay image can not be tiled\n");
ret = -EINVAL;
goto out_unlock;
}
@@ -1226,7 +1226,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_intel_overlay_attrs *attrs = data;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct overlay_registers __iomem *regs;
int ret;
@@ -1311,7 +1311,7 @@ out_unlock:
void intel_setup_overlay(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
struct drm_i915_gem_object *reg_bo;
struct overlay_registers __iomem *regs;
@@ -1349,7 +1349,7 @@ void intel_setup_overlay(struct drm_device *dev)
}
overlay->flip_addr = reg_bo->phys_obj->handle->busaddr;
} else {
- ret = i915_gem_obj_ggtt_pin(reg_bo, PAGE_SIZE, true, false);
+ ret = i915_gem_obj_ggtt_pin(reg_bo, PAGE_SIZE, PIN_MAPPABLE);
if (ret) {
DRM_ERROR("failed to pin overlay register bo\n");
goto out_free_bo;
@@ -1386,7 +1386,7 @@ void intel_setup_overlay(struct drm_device *dev)
out_unpin_bo:
if (!OVERLAY_NEEDS_PHYSICAL(dev))
- i915_gem_object_unpin(reg_bo);
+ i915_gem_object_ggtt_unpin(reg_bo);
out_free_bo:
drm_gem_object_unreference(&reg_bo->base);
out_free:
@@ -1397,7 +1397,7 @@ out_free:
void intel_cleanup_overlay(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (!dev_priv->overlay)
return;
@@ -1421,7 +1421,7 @@ struct intel_overlay_error_state {
static struct overlay_registers __iomem *
intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
{
- drm_i915_private_t *dev_priv = overlay->dev->dev_private;
+ struct drm_i915_private *dev_priv = overlay->dev->dev_private;
struct overlay_registers __iomem *regs;
if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
@@ -1447,7 +1447,7 @@ static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay,
struct intel_overlay_error_state *
intel_overlay_capture_error_state(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_overlay *overlay = dev_priv->overlay;
struct intel_overlay_error_state *error;
struct overlay_registers __iomem *regs;
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 079ea38f14d9..cb058408c70e 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -33,8 +33,6 @@
#include <linux/moduleparam.h>
#include "intel_drv.h"
-#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
-
void
intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode)
@@ -325,13 +323,6 @@ out:
pipe_config->gmch_pfit.lvds_border_bits = border;
}
-static int i915_panel_invert_brightness;
-MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness "
- "(-1 force normal, 0 machine defaults, 1 force inversion), please "
- "report PCI device ID, subsystem vendor and subsystem device ID "
- "to dri-devel@lists.freedesktop.org, if your machine needs it. "
- "It will then be included in an upcoming module version.");
-module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600);
static u32 intel_panel_compute_brightness(struct intel_connector *connector,
u32 val)
{
@@ -341,10 +332,10 @@ static u32 intel_panel_compute_brightness(struct intel_connector *connector,
WARN_ON(panel->backlight.max == 0);
- if (i915_panel_invert_brightness < 0)
+ if (i915.invert_brightness < 0)
return val;
- if (i915_panel_invert_brightness > 0 ||
+ if (i915.invert_brightness > 0 ||
dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
return panel->backlight.max - val;
}
@@ -810,13 +801,13 @@ intel_panel_detect(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
/* Assume that the BIOS does not lie through the OpRegion... */
- if (!i915_panel_ignore_lid && dev_priv->opregion.lid_state) {
+ if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) {
return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
connector_status_connected :
connector_status_disconnected;
}
- switch (i915_panel_ignore_lid) {
+ switch (i915.panel_ignore_lid) {
case -2:
return connector_status_connected;
case -1:
@@ -1199,9 +1190,11 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev)
}
int intel_panel_init(struct intel_panel *panel,
- struct drm_display_mode *fixed_mode)
+ struct drm_display_mode *fixed_mode,
+ struct drm_display_mode *downclock_mode)
{
panel->fixed_mode = fixed_mode;
+ panel->downclock_mode = downclock_mode;
return 0;
}
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index e1fc35a72656..5874716774a7 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -92,12 +92,12 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int cfb_pitch;
- int plane, i;
+ int i;
u32 fbc_ctl;
cfb_pitch = dev_priv->fbc.size / FBC_LL_SIZE;
@@ -109,7 +109,6 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc)
cfb_pitch = (cfb_pitch / 32) - 1;
else
cfb_pitch = (cfb_pitch / 64) - 1;
- plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
/* Clear old tags */
for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
@@ -120,7 +119,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc)
/* Set it up... */
fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE;
- fbc_ctl2 |= plane;
+ fbc_ctl2 |= FBC_CTL_PLANE(intel_crtc->plane);
I915_WRITE(FBC_CONTROL2, fbc_ctl2);
I915_WRITE(FBC_FENCE_OFF, crtc->y);
}
@@ -135,7 +134,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc)
fbc_ctl |= obj->fence_reg;
I915_WRITE(FBC_CONTROL, fbc_ctl);
- DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c, ",
+ DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c\n",
cfb_pitch, crtc->y, plane_name(intel_crtc->plane));
}
@@ -150,21 +149,23 @@ static void g4x_enable_fbc(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
u32 dpfc_ctl;
- dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
+ dpfc_ctl = DPFC_CTL_PLANE(intel_crtc->plane) | DPFC_SR_EN;
+ if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
+ dpfc_ctl |= DPFC_CTL_LIMIT_2X;
+ else
+ dpfc_ctl |= DPFC_CTL_LIMIT_1X;
dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
- I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
I915_WRITE(DPFC_FENCE_YOFF, crtc->y);
/* enable it... */
- I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN);
+ I915_WRITE(DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane));
}
@@ -220,22 +221,20 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
u32 dpfc_ctl;
- dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
- dpfc_ctl &= DPFC_RESERVED;
- dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
- /* Set persistent mode for front-buffer rendering, ala X. */
- dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
+ dpfc_ctl = DPFC_CTL_PLANE(intel_crtc->plane);
+ if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
+ dpfc_ctl |= DPFC_CTL_LIMIT_2X;
+ else
+ dpfc_ctl |= DPFC_CTL_LIMIT_1X;
dpfc_ctl |= DPFC_CTL_FENCE_EN;
if (IS_GEN5(dev))
dpfc_ctl |= obj->fence_reg;
- I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y);
I915_WRITE(ILK_FBC_RT_BASE, i915_gem_obj_ggtt_offset(obj) | ILK_FBC_RT_VALID);
@@ -278,24 +277,31 @@ static void gen7_enable_fbc(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ u32 dpfc_ctl;
- I915_WRITE(IVB_FBC_RT_BASE, i915_gem_obj_ggtt_offset(obj));
+ dpfc_ctl = IVB_DPFC_CTL_PLANE(intel_crtc->plane);
+ if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
+ dpfc_ctl |= DPFC_CTL_LIMIT_2X;
+ else
+ dpfc_ctl |= DPFC_CTL_LIMIT_1X;
+ dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN;
- I915_WRITE(ILK_DPFC_CONTROL, DPFC_CTL_EN | DPFC_CTL_LIMIT_1X |
- IVB_DPFC_CTL_FENCE_EN |
- intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT);
+ I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
if (IS_IVYBRIDGE(dev)) {
/* WaFbcAsynchFlipDisableFbcQueue:ivb */
- I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS);
+ I915_WRITE(ILK_DISPLAY_CHICKEN1,
+ I915_READ(ILK_DISPLAY_CHICKEN1) |
+ ILK_FBCQ_DIS);
} else {
- /* WaFbcAsynchFlipDisableFbcQueue:hsw */
- I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe),
- HSW_BYPASS_FBC_QUEUE);
+ /* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */
+ I915_WRITE(CHICKEN_PIPESL_1(intel_crtc->pipe),
+ I915_READ(CHICKEN_PIPESL_1(intel_crtc->pipe)) |
+ HSW_FBCQ_DIS);
}
I915_WRITE(SNB_DPFC_CTL_SA,
@@ -330,11 +336,11 @@ static void intel_fbc_work_fn(struct work_struct *__work)
/* Double check that we haven't switched fb without cancelling
* the prior work.
*/
- if (work->crtc->fb == work->fb) {
+ if (work->crtc->primary->fb == work->fb) {
dev_priv->display.enable_fbc(work->crtc);
dev_priv->fbc.plane = to_intel_crtc(work->crtc)->plane;
- dev_priv->fbc.fb_id = work->crtc->fb->base.id;
+ dev_priv->fbc.fb_id = work->crtc->primary->fb->base.id;
dev_priv->fbc.y = work->crtc->y;
}
@@ -387,7 +393,7 @@ static void intel_enable_fbc(struct drm_crtc *crtc)
}
work->crtc = crtc;
- work->fb = crtc->fb;
+ work->fb = crtc->primary->fb;
INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
dev_priv->fbc.fbc_work = work;
@@ -466,7 +472,7 @@ void intel_update_fbc(struct drm_device *dev)
return;
}
- if (!i915_powersave) {
+ if (!i915.powersave) {
if (set_no_fbc_reason(dev_priv, FBC_MODULE_PARAM))
DRM_DEBUG_KMS("fbc disabled per module param\n");
return;
@@ -493,25 +499,25 @@ void intel_update_fbc(struct drm_device *dev)
}
}
- if (!crtc || crtc->fb == NULL) {
+ if (!crtc || crtc->primary->fb == NULL) {
if (set_no_fbc_reason(dev_priv, FBC_NO_OUTPUT))
DRM_DEBUG_KMS("no output, disabling\n");
goto out_disable;
}
intel_crtc = to_intel_crtc(crtc);
- fb = crtc->fb;
+ fb = crtc->primary->fb;
intel_fb = to_intel_framebuffer(fb);
obj = intel_fb->obj;
adjusted_mode = &intel_crtc->config.adjusted_mode;
- if (i915_enable_fbc < 0 &&
+ if (i915.enable_fbc < 0 &&
INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) {
if (set_no_fbc_reason(dev_priv, FBC_CHIP_DEFAULT))
DRM_DEBUG_KMS("disabled per chip default\n");
goto out_disable;
}
- if (!i915_enable_fbc) {
+ if (!i915.enable_fbc) {
if (set_no_fbc_reason(dev_priv, FBC_MODULE_PARAM))
DRM_DEBUG_KMS("fbc disabled per module param\n");
goto out_disable;
@@ -537,7 +543,7 @@ void intel_update_fbc(struct drm_device *dev)
DRM_DEBUG_KMS("mode too large for compression, disabling\n");
goto out_disable;
}
- if ((INTEL_INFO(dev)->gen < 4 || IS_HASWELL(dev)) &&
+ if ((INTEL_INFO(dev)->gen < 4 || HAS_DDI(dev)) &&
intel_crtc->plane != PLANE_A) {
if (set_no_fbc_reason(dev_priv, FBC_BAD_PLANE))
DRM_DEBUG_KMS("plane not A, disabling compression\n");
@@ -617,7 +623,7 @@ out_disable:
static void i915_pineview_get_mem_freq(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp;
tmp = I915_READ(CLKCFG);
@@ -656,7 +662,7 @@ static void i915_pineview_get_mem_freq(struct drm_device *dev)
static void i915_ironlake_get_mem_freq(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u16 ddrpll, csipll;
ddrpll = I915_READ16(DDRMPLL1);
@@ -1035,7 +1041,7 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc)
crtc = single_enabled_crtc(dev);
if (crtc) {
const struct drm_display_mode *adjusted_mode;
- int pixel_size = crtc->fb->bits_per_pixel / 8;
+ int pixel_size = crtc->primary->fb->bits_per_pixel / 8;
int clock;
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
@@ -1115,7 +1121,7 @@ static bool g4x_compute_wm0(struct drm_device *dev,
clock = adjusted_mode->crtc_clock;
htotal = adjusted_mode->crtc_htotal;
hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
- pixel_size = crtc->fb->bits_per_pixel / 8;
+ pixel_size = crtc->primary->fb->bits_per_pixel / 8;
/* Use the small buffer method to calculate plane watermark */
entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
@@ -1128,9 +1134,9 @@ static bool g4x_compute_wm0(struct drm_device *dev,
*plane_wm = display->max_wm;
/* Use the large buffer method to calculate cursor watermark */
- line_time_us = ((htotal * 1000) / clock);
+ line_time_us = max(htotal * 1000 / clock, 1);
line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
- entries = line_count * 64 * pixel_size;
+ entries = line_count * to_intel_crtc(crtc)->cursor_width * pixel_size;
tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8;
if (tlb_miss > 0)
entries += tlb_miss;
@@ -1202,9 +1208,9 @@ static bool g4x_compute_srwm(struct drm_device *dev,
clock = adjusted_mode->crtc_clock;
htotal = adjusted_mode->crtc_htotal;
hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
- pixel_size = crtc->fb->bits_per_pixel / 8;
+ pixel_size = crtc->primary->fb->bits_per_pixel / 8;
- line_time_us = (htotal * 1000) / clock;
+ line_time_us = max(htotal * 1000 / clock, 1);
line_count = (latency_ns / line_time_us + 1000) / 1000;
line_size = hdisplay * pixel_size;
@@ -1216,7 +1222,7 @@ static bool g4x_compute_srwm(struct drm_device *dev,
*display_wm = entries + display->guard_size;
/* calculate the self-refresh watermark for display cursor */
- entries = line_count * pixel_size * 64;
+ entries = line_count * pixel_size * to_intel_crtc(crtc)->cursor_width;
entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
*cursor_wm = entries + cursor->guard_size;
@@ -1241,7 +1247,7 @@ static bool vlv_compute_drain_latency(struct drm_device *dev,
return false;
clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
- pixel_size = crtc->fb->bits_per_pixel / 8; /* BPP */
+ pixel_size = crtc->primary->fb->bits_per_pixel / 8; /* BPP */
entries = (clock / 1000) * pixel_size;
*plane_prec_mult = (entries > 256) ?
@@ -1433,11 +1439,11 @@ static void i965_update_wm(struct drm_crtc *unused_crtc)
int clock = adjusted_mode->crtc_clock;
int htotal = adjusted_mode->crtc_htotal;
int hdisplay = to_intel_crtc(crtc)->config.pipe_src_w;
- int pixel_size = crtc->fb->bits_per_pixel / 8;
+ int pixel_size = crtc->primary->fb->bits_per_pixel / 8;
unsigned long line_time_us;
int entries;
- line_time_us = ((htotal * 1000) / clock);
+ line_time_us = max(htotal * 1000 / clock, 1);
/* Use ns/us then divide to preserve precision */
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
@@ -1451,7 +1457,7 @@ static void i965_update_wm(struct drm_crtc *unused_crtc)
entries, srwm);
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
- pixel_size * 64;
+ pixel_size * to_intel_crtc(crtc)->cursor_width;
entries = DIV_ROUND_UP(entries,
i965_cursor_wm_info.cacheline_size);
cursor_sr = i965_cursor_wm_info.fifo_size -
@@ -1506,7 +1512,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
crtc = intel_get_crtc_for_plane(dev, 0);
if (intel_crtc_active(crtc)) {
const struct drm_display_mode *adjusted_mode;
- int cpp = crtc->fb->bits_per_pixel / 8;
+ int cpp = crtc->primary->fb->bits_per_pixel / 8;
if (IS_GEN2(dev))
cpp = 4;
@@ -1522,7 +1528,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
crtc = intel_get_crtc_for_plane(dev, 1);
if (intel_crtc_active(crtc)) {
const struct drm_display_mode *adjusted_mode;
- int cpp = crtc->fb->bits_per_pixel / 8;
+ int cpp = crtc->primary->fb->bits_per_pixel / 8;
if (IS_GEN2(dev))
cpp = 4;
@@ -1559,11 +1565,11 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
int clock = adjusted_mode->crtc_clock;
int htotal = adjusted_mode->crtc_htotal;
int hdisplay = to_intel_crtc(enabled)->config.pipe_src_w;
- int pixel_size = enabled->fb->bits_per_pixel / 8;
+ int pixel_size = enabled->primary->fb->bits_per_pixel / 8;
unsigned long line_time_us;
int entries;
- line_time_us = (htotal * 1000) / clock;
+ line_time_us = max(htotal * 1000 / clock, 1);
/* Use ns/us then divide to preserve precision */
entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
@@ -1886,7 +1892,7 @@ static unsigned int ilk_cursor_wm_max(const struct drm_device *dev,
}
/* Calculate the maximum FBC watermark */
-static unsigned int ilk_fbc_wm_max(struct drm_device *dev)
+static unsigned int ilk_fbc_wm_max(const struct drm_device *dev)
{
/* max that registers can hold */
if (INTEL_INFO(dev)->gen >= 8)
@@ -1895,7 +1901,7 @@ static unsigned int ilk_fbc_wm_max(struct drm_device *dev)
return 15;
}
-static void ilk_compute_wm_maximums(struct drm_device *dev,
+static void ilk_compute_wm_maximums(const struct drm_device *dev,
int level,
const struct intel_wm_config *config,
enum intel_ddb_partitioning ddb_partitioning,
@@ -1948,7 +1954,7 @@ static bool ilk_validate_wm_level(int level,
return ret;
}
-static void ilk_compute_wm_level(struct drm_i915_private *dev_priv,
+static void ilk_compute_wm_level(const struct drm_i915_private *dev_priv,
int level,
const struct ilk_pipe_wm_parameters *p,
struct intel_wm_level *result)
@@ -2079,7 +2085,7 @@ static void intel_print_wm_latency(struct drm_device *dev,
}
}
-static void intel_setup_wm_latency(struct drm_device *dev)
+static void ilk_setup_wm_latency(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2111,10 +2117,10 @@ static void ilk_compute_wm_parameters(struct drm_crtc *crtc,
if (p->active) {
p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal;
p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
- p->pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8;
+ p->pri.bytes_per_pixel = crtc->primary->fb->bits_per_pixel / 8;
p->cur.bytes_per_pixel = 4;
p->pri.horiz_pixels = intel_crtc->config.pipe_src_w;
- p->cur.horiz_pixels = 64;
+ p->cur.horiz_pixels = intel_crtc->cursor_width;
/* TODO: for now, assume primary and cursor planes are always enabled. */
p->pri.enabled = true;
p->cur.enabled = true;
@@ -2123,7 +2129,7 @@ static void ilk_compute_wm_parameters(struct drm_crtc *crtc,
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
config->num_pipes_active += intel_crtc_active(crtc);
- list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
struct intel_plane *intel_plane = to_intel_plane(plane);
if (intel_plane->pipe == pipe)
@@ -2140,7 +2146,7 @@ static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
struct intel_pipe_wm *pipe_wm)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
+ const struct drm_i915_private *dev_priv = dev->dev_private;
int level, max_level = ilk_wm_max_level(dev);
/* LP0 watermark maximums depend on this pipe alone */
struct intel_wm_config config = {
@@ -2738,7 +2744,7 @@ intel_alloc_context_page(struct drm_device *dev)
return NULL;
}
- ret = i915_gem_obj_ggtt_pin(ctx, 4096, true, false);
+ ret = i915_gem_obj_ggtt_pin(ctx, 4096, 0);
if (ret) {
DRM_ERROR("failed to pin power context: %d\n", ret);
goto err_unref;
@@ -2753,7 +2759,7 @@ intel_alloc_context_page(struct drm_device *dev)
return ctx;
err_unpin:
- i915_gem_object_unpin(ctx);
+ i915_gem_object_ggtt_unpin(ctx);
err_unref:
drm_gem_object_unreference(&ctx->base);
return NULL;
@@ -2901,9 +2907,9 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val)
* the hw runs at the minimal clock before selecting the desired
* frequency, if the down threshold expires in that window we will not
* receive a down interrupt. */
- limits = dev_priv->rps.max_delay << 24;
- if (val <= dev_priv->rps.min_delay)
- limits |= dev_priv->rps.min_delay << 16;
+ limits = dev_priv->rps.max_freq_softlimit << 24;
+ if (val <= dev_priv->rps.min_freq_softlimit)
+ limits |= dev_priv->rps.min_freq_softlimit << 16;
return limits;
}
@@ -2915,26 +2921,26 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val)
new_power = dev_priv->rps.power;
switch (dev_priv->rps.power) {
case LOW_POWER:
- if (val > dev_priv->rps.rpe_delay + 1 && val > dev_priv->rps.cur_delay)
+ if (val > dev_priv->rps.efficient_freq + 1 && val > dev_priv->rps.cur_freq)
new_power = BETWEEN;
break;
case BETWEEN:
- if (val <= dev_priv->rps.rpe_delay && val < dev_priv->rps.cur_delay)
+ if (val <= dev_priv->rps.efficient_freq && val < dev_priv->rps.cur_freq)
new_power = LOW_POWER;
- else if (val >= dev_priv->rps.rp0_delay && val > dev_priv->rps.cur_delay)
+ else if (val >= dev_priv->rps.rp0_freq && val > dev_priv->rps.cur_freq)
new_power = HIGH_POWER;
break;
case HIGH_POWER:
- if (val < (dev_priv->rps.rp1_delay + dev_priv->rps.rp0_delay) >> 1 && val < dev_priv->rps.cur_delay)
+ if (val < (dev_priv->rps.rp1_freq + dev_priv->rps.rp0_freq) >> 1 && val < dev_priv->rps.cur_freq)
new_power = BETWEEN;
break;
}
/* Max/min bins are special */
- if (val == dev_priv->rps.min_delay)
+ if (val == dev_priv->rps.min_freq_softlimit)
new_power = LOW_POWER;
- if (val == dev_priv->rps.max_delay)
+ if (val == dev_priv->rps.max_freq_softlimit)
new_power = HIGH_POWER;
if (new_power == dev_priv->rps.power)
return;
@@ -3000,41 +3006,113 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val)
dev_priv->rps.last_adj = 0;
}
+static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val)
+{
+ u32 mask = 0;
+
+ if (val > dev_priv->rps.min_freq_softlimit)
+ mask |= GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT;
+ if (val < dev_priv->rps.max_freq_softlimit)
+ mask |= GEN6_PM_RP_UP_THRESHOLD;
+
+ /* IVB and SNB hard hangs on looping batchbuffer
+ * if GEN6_PM_UP_EI_EXPIRED is masked.
+ */
+ if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev))
+ mask |= GEN6_PM_RP_UP_EI_EXPIRED;
+
+ return ~mask;
+}
+
+/* gen6_set_rps is called to update the frequency request, but should also be
+ * called when the range (min_delay and max_delay) is modified so that we can
+ * update the GEN6_RP_INTERRUPT_LIMITS register accordingly. */
void gen6_set_rps(struct drm_device *dev, u8 val)
{
struct drm_i915_private *dev_priv = dev->dev_private;
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
- WARN_ON(val > dev_priv->rps.max_delay);
- WARN_ON(val < dev_priv->rps.min_delay);
-
- if (val == dev_priv->rps.cur_delay)
- return;
+ WARN_ON(val > dev_priv->rps.max_freq_softlimit);
+ WARN_ON(val < dev_priv->rps.min_freq_softlimit);
- gen6_set_rps_thresholds(dev_priv, val);
+ /* min/max delay may still have been modified so be sure to
+ * write the limits value.
+ */
+ if (val != dev_priv->rps.cur_freq) {
+ gen6_set_rps_thresholds(dev_priv, val);
- if (IS_HASWELL(dev))
- I915_WRITE(GEN6_RPNSWREQ,
- HSW_FREQUENCY(val));
- else
- I915_WRITE(GEN6_RPNSWREQ,
- GEN6_FREQUENCY(val) |
- GEN6_OFFSET(0) |
- GEN6_AGGRESSIVE_TURBO);
+ if (IS_HASWELL(dev))
+ I915_WRITE(GEN6_RPNSWREQ,
+ HSW_FREQUENCY(val));
+ else
+ I915_WRITE(GEN6_RPNSWREQ,
+ GEN6_FREQUENCY(val) |
+ GEN6_OFFSET(0) |
+ GEN6_AGGRESSIVE_TURBO);
+ }
/* Make sure we continue to get interrupts
* until we hit the minimum or maximum frequencies.
*/
- I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
- gen6_rps_limits(dev_priv, val));
+ I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, gen6_rps_limits(dev_priv, val));
+ I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val));
POSTING_READ(GEN6_RPNSWREQ);
- dev_priv->rps.cur_delay = val;
-
+ dev_priv->rps.cur_freq = val;
trace_intel_gpu_freq_change(val * 50);
}
+/* vlv_set_rps_idle: Set the frequency to Rpn if Gfx clocks are down
+ *
+ * * If Gfx is Idle, then
+ * 1. Mask Turbo interrupts
+ * 2. Bring up Gfx clock
+ * 3. Change the freq to Rpn and wait till P-Unit updates freq
+ * 4. Clear the Force GFX CLK ON bit so that Gfx can down
+ * 5. Unmask Turbo interrupts
+*/
+static void vlv_set_rps_idle(struct drm_i915_private *dev_priv)
+{
+ /*
+ * When we are idle. Drop to min voltage state.
+ */
+
+ if (dev_priv->rps.cur_freq <= dev_priv->rps.min_freq_softlimit)
+ return;
+
+ /* Mask turbo interrupt so that they will not come in between */
+ I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
+
+ /* Bring up the Gfx clock */
+ I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG) |
+ VLV_GFX_CLK_FORCE_ON_BIT);
+
+ if (wait_for(((VLV_GFX_CLK_STATUS_BIT &
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG)) != 0), 5)) {
+ DRM_ERROR("GFX_CLK_ON request timed out\n");
+ return;
+ }
+
+ dev_priv->rps.cur_freq = dev_priv->rps.min_freq_softlimit;
+
+ vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ,
+ dev_priv->rps.min_freq_softlimit);
+
+ if (wait_for(((vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS))
+ & GENFREQSTATUS) == 0, 5))
+ DRM_ERROR("timed out waiting for Punit\n");
+
+ /* Release the Gfx clock */
+ I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG) &
+ ~VLV_GFX_CLK_FORCE_ON_BIT);
+
+ I915_WRITE(GEN6_PMINTRMSK,
+ gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq));
+}
+
void gen6_rps_idle(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
@@ -3042,9 +3120,9 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->rps.hw_lock);
if (dev_priv->rps.enabled) {
if (IS_VALLEYVIEW(dev))
- valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+ vlv_set_rps_idle(dev_priv);
else
- gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+ gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit);
dev_priv->rps.last_adj = 0;
}
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -3057,9 +3135,9 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->rps.hw_lock);
if (dev_priv->rps.enabled) {
if (IS_VALLEYVIEW(dev))
- valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_delay);
+ valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit);
else
- gen6_set_rps(dev_priv->dev, dev_priv->rps.max_delay);
+ gen6_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit);
dev_priv->rps.last_adj = 0;
}
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -3070,21 +3148,20 @@ void valleyview_set_rps(struct drm_device *dev, u8 val)
struct drm_i915_private *dev_priv = dev->dev_private;
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
- WARN_ON(val > dev_priv->rps.max_delay);
- WARN_ON(val < dev_priv->rps.min_delay);
+ WARN_ON(val > dev_priv->rps.max_freq_softlimit);
+ WARN_ON(val < dev_priv->rps.min_freq_softlimit);
DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.cur_delay),
- dev_priv->rps.cur_delay,
+ vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
+ dev_priv->rps.cur_freq,
vlv_gpu_freq(dev_priv, val), val);
- if (val == dev_priv->rps.cur_delay)
- return;
-
- vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val);
+ if (val != dev_priv->rps.cur_freq)
+ vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val);
- dev_priv->rps.cur_delay = val;
+ I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val));
+ dev_priv->rps.cur_freq = val;
trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv, val));
}
@@ -3093,7 +3170,8 @@ static void gen6_disable_rps_interrupts(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
- I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) & ~GEN6_PM_RPS_EVENTS);
+ I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) &
+ ~dev_priv->pm_rps_events);
/* Complete PM interrupt masking here doesn't race with the rps work
* item again unmasking PM interrupts because that is using a different
* register (PMIMR) to mask PM interrupts. The only risk is in leaving
@@ -3103,7 +3181,7 @@ static void gen6_disable_rps_interrupts(struct drm_device *dev)
dev_priv->rps.pm_iir = 0;
spin_unlock_irq(&dev_priv->irq_lock);
- I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
+ I915_WRITE(GEN6_PMIIR, dev_priv->pm_rps_events);
}
static void gen6_disable_rps(struct drm_device *dev)
@@ -3123,25 +3201,14 @@ static void valleyview_disable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RC_CONTROL, 0);
gen6_disable_rps_interrupts(dev);
-
- if (dev_priv->vlv_pctx) {
- drm_gem_object_unreference(&dev_priv->vlv_pctx->base);
- dev_priv->vlv_pctx = NULL;
- }
}
static void intel_print_rc6_info(struct drm_device *dev, u32 mode)
{
- if (IS_GEN6(dev))
- DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
-
- if (IS_HASWELL(dev))
- DRM_DEBUG_DRIVER("Haswell: only RC6 available\n");
-
DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
- (mode & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
- (mode & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
- (mode & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
+ (mode & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
+ (mode & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
+ (mode & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
}
int intel_enable_rc6(const struct drm_device *dev)
@@ -3151,44 +3218,28 @@ int intel_enable_rc6(const struct drm_device *dev)
return 0;
/* Respect the kernel parameter if it is set */
- if (i915_enable_rc6 >= 0)
- return i915_enable_rc6;
+ if (i915.enable_rc6 >= 0)
+ return i915.enable_rc6;
/* Disable RC6 on Ironlake */
if (INTEL_INFO(dev)->gen == 5)
return 0;
- if (IS_HASWELL(dev))
- return INTEL_RC6_ENABLE;
-
- /* snb/ivb have more than one rc6 state. */
- if (INTEL_INFO(dev)->gen == 6)
- return INTEL_RC6_ENABLE;
+ if (IS_IVYBRIDGE(dev))
+ return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
- return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
+ return INTEL_RC6_ENABLE;
}
static void gen6_enable_rps_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 enabled_intrs;
spin_lock_irq(&dev_priv->irq_lock);
WARN_ON(dev_priv->rps.pm_iir);
- snb_enable_pm_irq(dev_priv, GEN6_PM_RPS_EVENTS);
- I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
+ snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+ I915_WRITE(GEN6_PMIIR, dev_priv->pm_rps_events);
spin_unlock_irq(&dev_priv->irq_lock);
-
- /* only unmask PM interrupts we need. Mask all others. */
- enabled_intrs = GEN6_PM_RPS_EVENTS;
-
- /* IVB and SNB hard hangs on looping batchbuffer
- * if GEN6_PM_UP_EI_EXPIRED is masked.
- */
- if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
- enabled_intrs |= GEN6_PM_RP_UP_EI_EXPIRED;
-
- I915_WRITE(GEN6_PMINTRMSK, ~enabled_intrs);
}
static void gen8_enable_rps(struct drm_device *dev)
@@ -3222,10 +3273,10 @@ static void gen8_enable_rps(struct drm_device *dev)
/* 3: Enable RC6 */
if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
rc6_mask = GEN6_RC_CTL_RC6_ENABLE;
- DRM_INFO("RC6 %s\n", (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off");
+ intel_print_rc6_info(dev, rc6_mask);
I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE |
- GEN6_RC_CTL_EI_MODE(1) |
- rc6_mask);
+ GEN6_RC_CTL_EI_MODE(1) |
+ rc6_mask);
/* 4 Program defaults and thresholds for RPS*/
I915_WRITE(GEN6_RPNSWREQ, HSW_FREQUENCY(10)); /* Request 500 MHz */
@@ -3235,8 +3286,8 @@ static void gen8_enable_rps(struct drm_device *dev)
/* Docs recommend 900MHz, and 300 MHz respectively */
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
- dev_priv->rps.max_delay << 24 |
- dev_priv->rps.min_delay << 16);
+ dev_priv->rps.max_freq_softlimit << 24 |
+ dev_priv->rps.min_freq_softlimit << 16);
I915_WRITE(GEN6_RP_UP_THRESHOLD, 7600000 / 128); /* 76ms busyness per EI, 90% */
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 31300000 / 128); /* 313ms busyness per EI, 70%*/
@@ -3269,7 +3320,7 @@ static void gen6_enable_rps(struct drm_device *dev)
struct intel_ring_buffer *ring;
u32 rp_state_cap;
u32 gt_perf_status;
- u32 rc6vids, pcu_mbox, rc6_mask = 0;
+ u32 rc6vids, pcu_mbox = 0, rc6_mask = 0;
u32 gtfifodbg;
int rc6_mode;
int i, ret;
@@ -3295,13 +3346,23 @@ static void gen6_enable_rps(struct drm_device *dev)
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
- /* In units of 50MHz */
- dev_priv->rps.hw_max = dev_priv->rps.max_delay = rp_state_cap & 0xff;
- dev_priv->rps.min_delay = (rp_state_cap >> 16) & 0xff;
- dev_priv->rps.rp1_delay = (rp_state_cap >> 8) & 0xff;
- dev_priv->rps.rp0_delay = (rp_state_cap >> 0) & 0xff;
- dev_priv->rps.rpe_delay = dev_priv->rps.rp1_delay;
- dev_priv->rps.cur_delay = 0;
+ /* All of these values are in units of 50MHz */
+ dev_priv->rps.cur_freq = 0;
+ /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */
+ dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff;
+ dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff;
+ dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff;
+ /* XXX: only BYT has a special efficient freq */
+ dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq;
+ /* hw_max = RP0 until we check for overclocking */
+ dev_priv->rps.max_freq = dev_priv->rps.rp0_freq;
+
+ /* Preserve min/max settings in case of re-init */
+ if (dev_priv->rps.max_freq_softlimit == 0)
+ dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+
+ if (dev_priv->rps.min_freq_softlimit == 0)
+ dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
/* disable the counters and set deterministic thresholds */
I915_WRITE(GEN6_RC_CONTROL, 0);
@@ -3350,21 +3411,19 @@ static void gen6_enable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
ret = sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_MIN_FREQ_TABLE, 0);
- if (!ret) {
- pcu_mbox = 0;
- ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox);
- if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */
- DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n",
- (dev_priv->rps.max_delay & 0xff) * 50,
- (pcu_mbox & 0xff) * 50);
- dev_priv->rps.hw_max = pcu_mbox & 0xff;
- }
- } else {
+ if (ret)
DRM_DEBUG_DRIVER("Failed to set the min frequency\n");
+
+ ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox);
+ if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */
+ DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n",
+ (dev_priv->rps.max_freq_softlimit & 0xff) * 50,
+ (pcu_mbox & 0xff) * 50);
+ dev_priv->rps.max_freq = pcu_mbox & 0xff;
}
dev_priv->rps.power = HIGH_POWER; /* force a reset */
- gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+ gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit);
gen6_enable_rps_interrupts(dev);
@@ -3420,9 +3479,9 @@ void gen6_update_ring_freq(struct drm_device *dev)
* to use for memory access. We do this by specifying the IA frequency
* the PCU should use as a reference to determine the ring frequency.
*/
- for (gpu_freq = dev_priv->rps.max_delay; gpu_freq >= dev_priv->rps.min_delay;
+ for (gpu_freq = dev_priv->rps.max_freq_softlimit; gpu_freq >= dev_priv->rps.min_freq_softlimit;
gpu_freq--) {
- int diff = dev_priv->rps.max_delay - gpu_freq;
+ int diff = dev_priv->rps.max_freq_softlimit - gpu_freq;
unsigned int ia_freq = 0, ring_freq = 0;
if (INTEL_INFO(dev)->gen >= 8) {
@@ -3485,6 +3544,15 @@ int valleyview_rps_min_freq(struct drm_i915_private *dev_priv)
return vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM) & 0xff;
}
+/* Check that the pctx buffer wasn't move under us. */
+static void valleyview_check_pctx(struct drm_i915_private *dev_priv)
+{
+ unsigned long pctx_addr = I915_READ(VLV_PCBR) & ~4095;
+
+ WARN_ON(pctx_addr != dev_priv->mm.stolen_base +
+ dev_priv->vlv_pctx->stolen->start);
+}
+
static void valleyview_setup_pctx(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3529,6 +3597,17 @@ out:
dev_priv->vlv_pctx = pctx;
}
+static void valleyview_cleanup_pctx(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (WARN_ON(!dev_priv->vlv_pctx))
+ return;
+
+ drm_gem_object_unreference(&dev_priv->vlv_pctx->base);
+ dev_priv->vlv_pctx = NULL;
+}
+
static void valleyview_enable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3538,6 +3617,8 @@ static void valleyview_enable_rps(struct drm_device *dev)
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+ valleyview_check_pctx(dev_priv);
+
if ((gtfifodbg = I915_READ(GTFIFODBG))) {
DRM_DEBUG_DRIVER("GT fifo had a previous error %x\n",
gtfifodbg);
@@ -3588,32 +3669,39 @@ static void valleyview_enable_rps(struct drm_device *dev)
DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no");
DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val);
- dev_priv->rps.cur_delay = (val >> 8) & 0xff;
+ dev_priv->rps.cur_freq = (val >> 8) & 0xff;
DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.cur_delay),
- dev_priv->rps.cur_delay);
+ vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
+ dev_priv->rps.cur_freq);
- dev_priv->rps.max_delay = valleyview_rps_max_freq(dev_priv);
- dev_priv->rps.hw_max = dev_priv->rps.max_delay;
+ dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv);
+ dev_priv->rps.rp0_freq = dev_priv->rps.max_freq;
DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.max_delay),
- dev_priv->rps.max_delay);
+ vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq),
+ dev_priv->rps.max_freq);
- dev_priv->rps.rpe_delay = valleyview_rps_rpe_freq(dev_priv);
+ dev_priv->rps.efficient_freq = valleyview_rps_rpe_freq(dev_priv);
DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.rpe_delay),
- dev_priv->rps.rpe_delay);
+ vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
+ dev_priv->rps.efficient_freq);
- dev_priv->rps.min_delay = valleyview_rps_min_freq(dev_priv);
+ dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv);
DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.min_delay),
- dev_priv->rps.min_delay);
+ vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq),
+ dev_priv->rps.min_freq);
+
+ /* Preserve min/max settings in case of re-init */
+ if (dev_priv->rps.max_freq_softlimit == 0)
+ dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+
+ if (dev_priv->rps.min_freq_softlimit == 0)
+ dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.rpe_delay),
- dev_priv->rps.rpe_delay);
+ vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
+ dev_priv->rps.efficient_freq);
- valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay);
+ valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq);
gen6_enable_rps_interrupts(dev);
@@ -3625,13 +3713,13 @@ void ironlake_teardown_rc6(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
if (dev_priv->ips.renderctx) {
- i915_gem_object_unpin(dev_priv->ips.renderctx);
+ i915_gem_object_ggtt_unpin(dev_priv->ips.renderctx);
drm_gem_object_unreference(&dev_priv->ips.renderctx->base);
dev_priv->ips.renderctx = NULL;
}
if (dev_priv->ips.pwrctx) {
- i915_gem_object_unpin(dev_priv->ips.pwrctx);
+ i915_gem_object_ggtt_unpin(dev_priv->ips.pwrctx);
drm_gem_object_unreference(&dev_priv->ips.pwrctx->base);
dev_priv->ips.pwrctx = NULL;
}
@@ -3823,9 +3911,10 @@ static unsigned long __i915_chipset_val(struct drm_i915_private *dev_priv)
unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
{
+ struct drm_device *dev = dev_priv->dev;
unsigned long val;
- if (dev_priv->info->gen != 5)
+ if (INTEL_INFO(dev)->gen != 5)
return 0;
spin_lock_irq(&mchdev_lock);
@@ -3854,6 +3943,7 @@ unsigned long i915_mch_val(struct drm_i915_private *dev_priv)
static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid)
{
+ struct drm_device *dev = dev_priv->dev;
static const struct v_table {
u16 vd; /* in .1 mil */
u16 vm; /* in .1 mil */
@@ -3987,7 +4077,7 @@ static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid)
{ 16000, 14875, },
{ 16125, 15000, },
};
- if (dev_priv->info->is_mobile)
+ if (INTEL_INFO(dev)->is_mobile)
return v_table[pxvid].vm;
else
return v_table[pxvid].vd;
@@ -4030,7 +4120,9 @@ static void __i915_update_gfx_val(struct drm_i915_private *dev_priv)
void i915_update_gfx_val(struct drm_i915_private *dev_priv)
{
- if (dev_priv->info->gen != 5)
+ struct drm_device *dev = dev_priv->dev;
+
+ if (INTEL_INFO(dev)->gen != 5)
return;
spin_lock_irq(&mchdev_lock);
@@ -4047,7 +4139,7 @@ static unsigned long __i915_gfx_val(struct drm_i915_private *dev_priv)
assert_spin_locked(&mchdev_lock);
- pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_delay * 4));
+ pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_freq * 4));
pxvid = (pxvid >> 24) & 0x7f;
ext_v = pvid_to_extvid(dev_priv, pxvid);
@@ -4079,9 +4171,10 @@ static unsigned long __i915_gfx_val(struct drm_i915_private *dev_priv)
unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
{
+ struct drm_device *dev = dev_priv->dev;
unsigned long val;
- if (dev_priv->info->gen != 5)
+ if (INTEL_INFO(dev)->gen != 5)
return 0;
spin_lock_irq(&mchdev_lock);
@@ -4270,6 +4363,7 @@ void intel_gpu_ips_teardown(void)
i915_mch_dev = NULL;
spin_unlock_irq(&mchdev_lock);
}
+
static void intel_init_emon(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4341,6 +4435,18 @@ static void intel_init_emon(struct drm_device *dev)
dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK);
}
+void intel_init_gt_powersave(struct drm_device *dev)
+{
+ if (IS_VALLEYVIEW(dev))
+ valleyview_setup_pctx(dev);
+}
+
+void intel_cleanup_gt_powersave(struct drm_device *dev)
+{
+ if (IS_VALLEYVIEW(dev))
+ valleyview_cleanup_pctx(dev);
+}
+
void intel_disable_gt_powersave(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4395,8 +4501,6 @@ void intel_enable_gt_powersave(struct drm_device *dev)
ironlake_enable_rc6(dev);
intel_init_emon(dev);
} else if (IS_GEN6(dev) || IS_GEN7(dev)) {
- if (IS_VALLEYVIEW(dev))
- valleyview_setup_pctx(dev);
/*
* PCU communication is slow and this doesn't need to be
* done at any specific time, so do this out of our fast path
@@ -4587,6 +4691,17 @@ static void gen6_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN6_GT_MODE,
_MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE));
+ /*
+ * BSpec recoomends 8x4 when MSAA is used,
+ * however in practice 16x4 seems fastest.
+ *
+ * Note that PS/WM thread counts depend on the WIZ hashing
+ * disable bit, which we don't touch here, but it's good
+ * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
+ */
+ I915_WRITE(GEN6_GT_MODE,
+ GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4);
+
ilk_init_lp_watermarks(dev);
I915_WRITE(CACHE_MODE_0,
@@ -4607,17 +4722,24 @@ static void gen6_init_clock_gating(struct drm_device *dev)
* According to the spec, bit 11 (RCCUNIT) must also be set,
* but we didn't debug actual testcases to find it out.
*
- * Also apply WaDisableVDSUnitClockGating:snb and
- * WaDisableRCPBUnitClockGating:snb.
+ * WaDisableRCCUnitClockGating:snb
+ * WaDisableRCPBUnitClockGating:snb
*/
I915_WRITE(GEN6_UCGCTL2,
- GEN7_VDSUNIT_CLOCK_GATE_DISABLE |
GEN6_RCPBUNIT_CLOCK_GATE_DISABLE |
GEN6_RCCUNIT_CLOCK_GATE_DISABLE);
- /* Bspec says we need to always set all mask bits. */
- I915_WRITE(_3D_CHICKEN3, (0xFFFF << 16) |
- _3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL);
+ /* WaStripsFansDisableFastClipPerformanceFix:snb */
+ I915_WRITE(_3D_CHICKEN3,
+ _MASKED_BIT_ENABLE(_3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL));
+
+ /*
+ * Bspec says:
+ * "This bit must be set if 3DSTATE_CLIP clip mode is set to normal and
+ * 3DSTATE_SF number of SF output attributes is more than 16."
+ */
+ I915_WRITE(_3D_CHICKEN3,
+ _MASKED_BIT_ENABLE(_3D_CHICKEN3_SF_DISABLE_PIPELINED_ATTR_FETCH));
/*
* According to the spec the following bits should be
@@ -4643,11 +4765,6 @@ static void gen6_init_clock_gating(struct drm_device *dev)
g4x_disable_trickle_feed(dev);
- /* The default value should be 0x200 according to docs, but the two
- * platforms I checked have a 0 for this. (Maybe BIOS overrides?) */
- I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_DISABLE(0xffff));
- I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_ENABLE(GEN6_GT_MODE_HI));
-
cpt_init_clock_gating(dev);
gen6_check_mch_setup(dev);
@@ -4657,14 +4774,17 @@ static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv)
{
uint32_t reg = I915_READ(GEN7_FF_THREAD_MODE);
+ /*
+ * WaVSThreadDispatchOverride:ivb,vlv
+ *
+ * This actually overrides the dispatch
+ * mode for all thread types.
+ */
reg &= ~GEN7_FF_SCHED_MASK;
reg |= GEN7_FF_TS_SCHED_HW;
reg |= GEN7_FF_VS_SCHED_HW;
reg |= GEN7_FF_DS_SCHED_HW;
- if (IS_HASWELL(dev_priv->dev))
- reg &= ~GEN7_FF_VS_REF_CNT_FFME;
-
I915_WRITE(GEN7_FF_THREAD_MODE, reg);
}
@@ -4702,7 +4822,7 @@ static void lpt_suspend_hw(struct drm_device *dev)
static void gen8_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- enum pipe i;
+ enum pipe pipe;
I915_WRITE(WM3_LP_ILK, 0);
I915_WRITE(WM2_LP_ILK, 0);
@@ -4711,8 +4831,19 @@ static void gen8_init_clock_gating(struct drm_device *dev)
/* FIXME(BDW): Check all the w/a, some might only apply to
* pre-production hw. */
- WARN(!i915_preliminary_hw_support,
- "GEN8_CENTROID_PIXEL_OPT_DIS not be needed for production\n");
+ /* WaDisablePartialInstShootdown:bdw */
+ I915_WRITE(GEN8_ROW_CHICKEN,
+ _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE));
+
+ /* WaDisableThreadStallDopClockGating:bdw */
+ /* FIXME: Unclear whether we really need this on production bdw. */
+ I915_WRITE(GEN8_ROW_CHICKEN,
+ _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE));
+
+ /*
+ * This GEN8_CENTROID_PIXEL_OPT_DIS W/A is only needed for
+ * pre-production hardware
+ */
I915_WRITE(HALF_SLICE_CHICKEN3,
_MASKED_BIT_ENABLE(GEN8_CENTROID_PIXEL_OPT_DIS));
I915_WRITE(HALF_SLICE_CHICKEN3,
@@ -4736,10 +4867,10 @@ static void gen8_init_clock_gating(struct drm_device *dev)
I915_READ(CHICKEN_PAR1_1) | DPA_MASK_VBLANK_SRD);
/* WaPsrDPRSUnmaskVBlankInSRD:bdw */
- for_each_pipe(i) {
- I915_WRITE(CHICKEN_PIPESL_1(i),
- I915_READ(CHICKEN_PIPESL_1(i) |
- DPRS_MASK_VBLANK_SRD));
+ for_each_pipe(pipe) {
+ I915_WRITE(CHICKEN_PIPESL_1(pipe),
+ I915_READ(CHICKEN_PIPESL_1(pipe)) |
+ BDW_DPRS_MASK_VBLANK_SRD);
}
/* Use Force Non-Coherent whenever executing a 3D context. This is a
@@ -4755,6 +4886,28 @@ static void gen8_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN7_FF_THREAD_MODE,
I915_READ(GEN7_FF_THREAD_MODE) &
~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME));
+
+ /*
+ * BSpec recommends 8x4 when MSAA is used,
+ * however in practice 16x4 seems fastest.
+ *
+ * Note that PS/WM thread counts depend on the WIZ hashing
+ * disable bit, which we don't touch here, but it's good
+ * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
+ */
+ I915_WRITE(GEN7_GT_MODE,
+ GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4);
+
+ I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL,
+ _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE));
+
+ /* WaDisableSDEUnitClockGating:bdw */
+ I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
+ GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
+
+ /* Wa4x4STCOptimizationDisable:bdw */
+ I915_WRITE(CACHE_MODE_1,
+ _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE));
}
static void haswell_init_clock_gating(struct drm_device *dev)
@@ -4763,21 +4916,6 @@ static void haswell_init_clock_gating(struct drm_device *dev)
ilk_init_lp_watermarks(dev);
- /* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
- * This implements the WaDisableRCZUnitClockGating:hsw workaround.
- */
- I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
-
- /* Apply the WaDisableRHWOOptimizationForRenderHang:hsw workaround. */
- I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
- GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
-
- /* WaApplyL3ControlAndL3ChickenMode:hsw */
- I915_WRITE(GEN7_L3CNTLREG1,
- GEN7_WA_FOR_GEN7_L3_CONTROL);
- I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER,
- GEN7_WA_L3_CHICKEN_MODE);
-
/* L3 caching of data atomics doesn't work -- disable it. */
I915_WRITE(HSW_SCRATCH1, HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE);
I915_WRITE(HSW_ROW_CHICKEN3,
@@ -4789,12 +4927,28 @@ static void haswell_init_clock_gating(struct drm_device *dev)
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
/* WaVSRefCountFullforceMissDisable:hsw */
- gen7_setup_fixed_func_scheduler(dev_priv);
+ I915_WRITE(GEN7_FF_THREAD_MODE,
+ I915_READ(GEN7_FF_THREAD_MODE) & ~GEN7_FF_VS_REF_CNT_FFME);
+
+ /* enable HiZ Raw Stall Optimization */
+ I915_WRITE(CACHE_MODE_0_GEN7,
+ _MASKED_BIT_DISABLE(HIZ_RAW_STALL_OPT_DISABLE));
/* WaDisable4x2SubspanOptimization:hsw */
I915_WRITE(CACHE_MODE_1,
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
+ /*
+ * BSpec recommends 8x4 when MSAA is used,
+ * however in practice 16x4 seems fastest.
+ *
+ * Note that PS/WM thread counts depend on the WIZ hashing
+ * disable bit, which we don't touch here, but it's good
+ * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
+ */
+ I915_WRITE(GEN7_GT_MODE,
+ GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4);
+
/* WaSwitchSolVfFArbitrationPriority:hsw */
I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL);
@@ -4827,9 +4981,6 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
if (IS_IVB_GT1(dev))
I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
_MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
- else
- I915_WRITE(GEN7_HALF_SLICE_CHICKEN1_GT2,
- _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
/* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
@@ -4843,31 +4994,24 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
if (IS_IVB_GT1(dev))
I915_WRITE(GEN7_ROW_CHICKEN2,
_MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
- else
+ else {
+ /* must write both registers */
+ I915_WRITE(GEN7_ROW_CHICKEN2,
+ _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
I915_WRITE(GEN7_ROW_CHICKEN2_GT2,
_MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
-
+ }
/* WaForceL3Serialization:ivb */
I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
~L3SQ_URB_READ_CAM_MATCH_DISABLE);
- /* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock
- * gating disable must be set. Failure to set it results in
- * flickering pixels due to Z write ordering failures after
- * some amount of runtime in the Mesa "fire" demo, and Unigine
- * Sanctuary and Tropics, and apparently anything else with
- * alpha test or pixel discard.
- *
- * According to the spec, bit 11 (RCCUNIT) must also be set,
- * but we didn't debug actual testcases to find it out.
- *
+ /*
* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
* This implements the WaDisableRCZUnitClockGating:ivb workaround.
*/
I915_WRITE(GEN6_UCGCTL2,
- GEN6_RCZUNIT_CLOCK_GATE_DISABLE |
- GEN6_RCCUNIT_CLOCK_GATE_DISABLE);
+ GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
/* This is required by WaCatErrorRejectionIssue:ivb */
I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
@@ -4876,13 +5020,29 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
g4x_disable_trickle_feed(dev);
- /* WaVSRefCountFullforceMissDisable:ivb */
gen7_setup_fixed_func_scheduler(dev_priv);
+ if (0) { /* causes HiZ corruption on ivb:gt1 */
+ /* enable HiZ Raw Stall Optimization */
+ I915_WRITE(CACHE_MODE_0_GEN7,
+ _MASKED_BIT_DISABLE(HIZ_RAW_STALL_OPT_DISABLE));
+ }
+
/* WaDisable4x2SubspanOptimization:ivb */
I915_WRITE(CACHE_MODE_1,
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
+ /*
+ * BSpec recommends 8x4 when MSAA is used,
+ * however in practice 16x4 seems fastest.
+ *
+ * Note that PS/WM thread counts depend on the WIZ hashing
+ * disable bit, which we don't touch here, but it's good
+ * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
+ */
+ I915_WRITE(GEN7_GT_MODE,
+ GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4);
+
snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
snpcr &= ~GEN6_MBC_SNPCR_MASK;
snpcr |= GEN6_MBC_SNPCR_MED;
@@ -4904,13 +5064,11 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
mutex_unlock(&dev_priv->rps.hw_lock);
switch ((val >> 6) & 3) {
case 0:
- dev_priv->mem_freq = 800;
- break;
case 1:
- dev_priv->mem_freq = 1066;
+ dev_priv->mem_freq = 800;
break;
case 2:
- dev_priv->mem_freq = 1333;
+ dev_priv->mem_freq = 1066;
break;
case 3:
dev_priv->mem_freq = 1333;
@@ -4929,19 +5087,12 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE |
CHICKEN3_DGMG_DONE_FIX_DISABLE);
+ /* WaPsdDispatchEnable:vlv */
/* WaDisablePSDDualDispatchEnable:vlv */
I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
_MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP |
GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
- /* Apply the WaDisableRHWOOptimizationForRenderHang:vlv workaround. */
- I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
- GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
-
- /* WaApplyL3ControlAndL3ChickenMode:vlv */
- I915_WRITE(GEN7_L3CNTLREG1, I915_READ(GEN7_L3CNTLREG1) | GEN7_L3AGDIS);
- I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE);
-
/* WaForceL3Serialization:vlv */
I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
~L3SQ_URB_READ_CAM_MATCH_DISABLE);
@@ -4955,51 +5106,39 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
- /* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock
- * gating disable must be set. Failure to set it results in
- * flickering pixels due to Z write ordering failures after
- * some amount of runtime in the Mesa "fire" demo, and Unigine
- * Sanctuary and Tropics, and apparently anything else with
- * alpha test or pixel discard.
- *
- * According to the spec, bit 11 (RCCUNIT) must also be set,
- * but we didn't debug actual testcases to find it out.
- *
+ gen7_setup_fixed_func_scheduler(dev_priv);
+
+ /*
* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
* This implements the WaDisableRCZUnitClockGating:vlv workaround.
- *
- * Also apply WaDisableVDSUnitClockGating:vlv and
- * WaDisableRCPBUnitClockGating:vlv.
*/
I915_WRITE(GEN6_UCGCTL2,
- GEN7_VDSUNIT_CLOCK_GATE_DISABLE |
- GEN7_TDLUNIT_CLOCK_GATE_DISABLE |
- GEN6_RCZUNIT_CLOCK_GATE_DISABLE |
- GEN6_RCPBUNIT_CLOCK_GATE_DISABLE |
- GEN6_RCCUNIT_CLOCK_GATE_DISABLE);
+ GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
+ /* WaDisableL3Bank2xClockGate:vlv */
I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE);
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
+ /*
+ * BSpec says this must be set, even though
+ * WaDisable4x2SubspanOptimization isn't listed for VLV.
+ */
I915_WRITE(CACHE_MODE_1,
_MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
/*
+ * WaIncreaseL3CreditsForVLVB0:vlv
+ * This is the hardware default actually.
+ */
+ I915_WRITE(GEN7_L3SQCREG1, VLV_B0_WA_L3SQCREG1_VALUE);
+
+ /*
* WaDisableVLVClockGating_VBIIssue:vlv
* Disable clock gating on th GCFG unit to prevent a delay
* in the reporting of vblank events.
*/
- I915_WRITE(VLV_GUNIT_CLOCK_GATE, 0xffffffff);
-
- /* Conservative clock gating settings for now */
- I915_WRITE(0x9400, 0xffffffff);
- I915_WRITE(0x9404, 0xffffffff);
- I915_WRITE(0x9408, 0xffffffff);
- I915_WRITE(0x940c, 0xffffffff);
- I915_WRITE(0x9410, 0xffffffff);
- I915_WRITE(0x9414, 0xffffffff);
- I915_WRITE(0x9418, 0xffffffff);
+ I915_WRITE(VLV_GUNIT_CLOCK_GATE, GCFG_DIS);
}
static void g4x_init_clock_gating(struct drm_device *dev)
@@ -5114,19 +5253,16 @@ void intel_suspend_hw(struct drm_device *dev)
* enable it, so check if it's enabled and also check if we've requested it to
* be enabled.
*/
-static bool hsw_power_well_enabled(struct drm_device *dev,
+static bool hsw_power_well_enabled(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
return I915_READ(HSW_PWR_WELL_DRIVER) ==
(HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
}
-bool intel_display_power_enabled_sw(struct drm_device *dev,
+bool intel_display_power_enabled_sw(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains;
power_domains = &dev_priv->power_domains;
@@ -5134,15 +5270,17 @@ bool intel_display_power_enabled_sw(struct drm_device *dev,
return power_domains->domain_use_count[domain];
}
-bool intel_display_power_enabled(struct drm_device *dev,
+bool intel_display_power_enabled(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains;
struct i915_power_well *power_well;
bool is_enabled;
int i;
+ if (dev_priv->pm.suspended)
+ return false;
+
power_domains = &dev_priv->power_domains;
is_enabled = true;
@@ -5152,7 +5290,7 @@ bool intel_display_power_enabled(struct drm_device *dev,
if (power_well->always_on)
continue;
- if (!power_well->is_enabled(dev, power_well)) {
+ if (!power_well->ops->is_enabled(dev_priv, power_well)) {
is_enabled = false;
break;
}
@@ -5162,6 +5300,12 @@ bool intel_display_power_enabled(struct drm_device *dev,
return is_enabled;
}
+/*
+ * Starting with Haswell, we have a "Power Down Well" that can be turned off
+ * when not needed anymore. We have 4 registers that can request the power well
+ * to be enabled, and it will only be disabled if none of the registers is
+ * requesting it to be enabled.
+ */
static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
@@ -5198,10 +5342,17 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
}
}
+static void reset_vblank_counter(struct drm_device *dev, enum pipe pipe)
+{
+ assert_spin_locked(&dev->vbl_lock);
+
+ dev->vblank[pipe].last = 0;
+}
+
static void hsw_power_well_post_disable(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
- enum pipe p;
+ enum pipe pipe;
unsigned long irqflags;
/*
@@ -5212,21 +5363,18 @@ static void hsw_power_well_post_disable(struct drm_i915_private *dev_priv)
* FIXME: Should we do this in general in drm_vblank_post_modeset?
*/
spin_lock_irqsave(&dev->vbl_lock, irqflags);
- for_each_pipe(p)
- if (p != PIPE_A)
- dev->vblank[p].last = 0;
+ for_each_pipe(pipe)
+ if (pipe != PIPE_A)
+ reset_vblank_counter(dev, pipe);
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
-static void hsw_set_power_well(struct drm_device *dev,
+static void hsw_set_power_well(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well, bool enable)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
bool is_enabled, enable_requested;
uint32_t tmp;
- WARN_ON(dev_priv->pc8.enabled);
-
tmp = I915_READ(HSW_PWR_WELL_DRIVER);
is_enabled = tmp & HSW_PWR_WELL_STATE_ENABLED;
enable_requested = tmp & HSW_PWR_WELL_ENABLE_REQUEST;
@@ -5255,55 +5403,229 @@ static void hsw_set_power_well(struct drm_device *dev,
}
}
-static void __intel_power_well_get(struct drm_device *dev,
+static void hsw_power_well_sync_hw(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ hsw_set_power_well(dev_priv, power_well, power_well->count > 0);
- if (!power_well->count++ && power_well->set) {
- hsw_disable_package_c8(dev_priv);
- power_well->set(dev, power_well, true);
- }
+ /*
+ * We're taking over the BIOS, so clear any requests made by it since
+ * the driver is in charge now.
+ */
+ if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE_REQUEST)
+ I915_WRITE(HSW_PWR_WELL_BIOS, 0);
+}
+
+static void hsw_power_well_enable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ hsw_set_power_well(dev_priv, power_well, true);
}
-static void __intel_power_well_put(struct drm_device *dev,
+static void hsw_power_well_disable(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ hsw_set_power_well(dev_priv, power_well, false);
+}
+
+static void i9xx_always_on_power_well_noop(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+}
+
+static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ return true;
+}
+
+static void vlv_set_power_well(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well, bool enable)
+{
+ enum punit_power_well power_well_id = power_well->data;
+ u32 mask;
+ u32 state;
+ u32 ctrl;
+
+ mask = PUNIT_PWRGT_MASK(power_well_id);
+ state = enable ? PUNIT_PWRGT_PWR_ON(power_well_id) :
+ PUNIT_PWRGT_PWR_GATE(power_well_id);
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+#define COND \
+ ((vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & mask) == state)
+
+ if (COND)
+ goto out;
+
+ ctrl = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL);
+ ctrl &= ~mask;
+ ctrl |= state;
+ vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, ctrl);
+
+ if (wait_for(COND, 100))
+ DRM_ERROR("timout setting power well state %08x (%08x)\n",
+ state,
+ vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL));
+
+#undef COND
+
+out:
+ mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void vlv_power_well_sync_hw(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ vlv_set_power_well(dev_priv, power_well, power_well->count > 0);
+}
+
+static void vlv_power_well_enable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ vlv_set_power_well(dev_priv, power_well, true);
+}
+
+static void vlv_power_well_disable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ vlv_set_power_well(dev_priv, power_well, false);
+}
+
+static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ int power_well_id = power_well->data;
+ bool enabled = false;
+ u32 mask;
+ u32 state;
+ u32 ctrl;
+
+ mask = PUNIT_PWRGT_MASK(power_well_id);
+ ctrl = PUNIT_PWRGT_PWR_ON(power_well_id);
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+ state = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & mask;
+ /*
+ * We only ever set the power-on and power-gate states, anything
+ * else is unexpected.
+ */
+ WARN_ON(state != PUNIT_PWRGT_PWR_ON(power_well_id) &&
+ state != PUNIT_PWRGT_PWR_GATE(power_well_id));
+ if (state == ctrl)
+ enabled = true;
+
+ /*
+ * A transient state at this point would mean some unexpected party
+ * is poking at the power controls too.
+ */
+ ctrl = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL) & mask;
+ WARN_ON(ctrl != state);
+
+ mutex_unlock(&dev_priv->rps.hw_lock);
+
+ return enabled;
+}
- WARN_ON(!power_well->count);
+static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DISP2D);
+
+ vlv_set_power_well(dev_priv, power_well, true);
+
+ spin_lock_irq(&dev_priv->irq_lock);
+ valleyview_enable_display_irqs(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
+
+ /*
+ * During driver initialization we need to defer enabling hotplug
+ * processing until fbdev is set up.
+ */
+ if (dev_priv->enable_hotplug_processing)
+ intel_hpd_init(dev_priv->dev);
+
+ i915_redisable_vga_power_on(dev_priv->dev);
+}
+
+static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ struct drm_device *dev = dev_priv->dev;
+ enum pipe pipe;
+
+ WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DISP2D);
+
+ spin_lock_irq(&dev_priv->irq_lock);
+ for_each_pipe(pipe)
+ __intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
+
+ valleyview_disable_display_irqs(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
+
+ spin_lock_irq(&dev->vbl_lock);
+ for_each_pipe(pipe)
+ reset_vblank_counter(dev, pipe);
+ spin_unlock_irq(&dev->vbl_lock);
+
+ vlv_set_power_well(dev_priv, power_well, false);
+}
- if (!--power_well->count && power_well->set &&
- i915_disable_power_well) {
- power_well->set(dev, power_well, false);
- hsw_enable_package_c8(dev_priv);
+static void check_power_well_state(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ bool enabled = power_well->ops->is_enabled(dev_priv, power_well);
+
+ if (power_well->always_on || !i915.disable_power_well) {
+ if (!enabled)
+ goto mismatch;
+
+ return;
}
+
+ if (enabled != (power_well->count > 0))
+ goto mismatch;
+
+ return;
+
+mismatch:
+ WARN(1, "state mismatch for '%s' (always_on %d hw state %d use-count %d disable_power_well %d\n",
+ power_well->name, power_well->always_on, enabled,
+ power_well->count, i915.disable_power_well);
}
-void intel_display_power_get(struct drm_device *dev,
+void intel_display_power_get(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains;
struct i915_power_well *power_well;
int i;
+ intel_runtime_pm_get(dev_priv);
+
power_domains = &dev_priv->power_domains;
mutex_lock(&power_domains->lock);
- for_each_power_well(i, power_well, BIT(domain), power_domains)
- __intel_power_well_get(dev, power_well);
+ for_each_power_well(i, power_well, BIT(domain), power_domains) {
+ if (!power_well->count++) {
+ DRM_DEBUG_KMS("enabling %s\n", power_well->name);
+ power_well->ops->enable(dev_priv, power_well);
+ }
+
+ check_power_well_state(dev_priv, power_well);
+ }
power_domains->domain_use_count[domain]++;
mutex_unlock(&power_domains->lock);
}
-void intel_display_power_put(struct drm_device *dev,
+void intel_display_power_put(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains;
struct i915_power_well *power_well;
int i;
@@ -5315,10 +5637,20 @@ void intel_display_power_put(struct drm_device *dev,
WARN_ON(!power_domains->domain_use_count[domain]);
power_domains->domain_use_count[domain]--;
- for_each_power_well_rev(i, power_well, BIT(domain), power_domains)
- __intel_power_well_put(dev, power_well);
+ for_each_power_well_rev(i, power_well, BIT(domain), power_domains) {
+ WARN_ON(!power_well->count);
+
+ if (!--power_well->count && i915.disable_power_well) {
+ DRM_DEBUG_KMS("disabling %s\n", power_well->name);
+ power_well->ops->disable(dev_priv, power_well);
+ }
+
+ check_power_well_state(dev_priv, power_well);
+ }
mutex_unlock(&power_domains->lock);
+
+ intel_runtime_pm_put(dev_priv);
}
static struct i915_power_domains *hsw_pwr;
@@ -5333,7 +5665,7 @@ void i915_request_power_well(void)
dev_priv = container_of(hsw_pwr, struct drm_i915_private,
power_domains);
- intel_display_power_get(dev_priv->dev, POWER_DOMAIN_AUDIO);
+ intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO);
}
EXPORT_SYMBOL_GPL(i915_request_power_well);
@@ -5347,29 +5679,99 @@ void i915_release_power_well(void)
dev_priv = container_of(hsw_pwr, struct drm_i915_private,
power_domains);
- intel_display_power_put(dev_priv->dev, POWER_DOMAIN_AUDIO);
+ intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO);
}
EXPORT_SYMBOL_GPL(i915_release_power_well);
+#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1)
+
+#define HSW_ALWAYS_ON_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_A) | \
+ BIT(POWER_DOMAIN_TRANSCODER_EDP) | \
+ BIT(POWER_DOMAIN_PORT_DDI_A_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_A_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_CRT) | \
+ BIT(POWER_DOMAIN_INIT))
+#define HSW_DISPLAY_POWER_DOMAINS ( \
+ (POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define BDW_ALWAYS_ON_POWER_DOMAINS ( \
+ HSW_ALWAYS_ON_POWER_DOMAINS | \
+ BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER))
+#define BDW_DISPLAY_POWER_DOMAINS ( \
+ (POWER_DOMAIN_MASK & ~BDW_ALWAYS_ON_POWER_DOMAINS) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define VLV_ALWAYS_ON_POWER_DOMAINS BIT(POWER_DOMAIN_INIT)
+#define VLV_DISPLAY_POWER_DOMAINS POWER_DOMAIN_MASK
+
+#define VLV_DPIO_CMN_BC_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_CRT) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+static const struct i915_power_well_ops i9xx_always_on_power_well_ops = {
+ .sync_hw = i9xx_always_on_power_well_noop,
+ .enable = i9xx_always_on_power_well_noop,
+ .disable = i9xx_always_on_power_well_noop,
+ .is_enabled = i9xx_always_on_power_well_enabled,
+};
+
static struct i915_power_well i9xx_always_on_power_well[] = {
{
.name = "always-on",
.always_on = 1,
.domains = POWER_DOMAIN_MASK,
+ .ops = &i9xx_always_on_power_well_ops,
},
};
+static const struct i915_power_well_ops hsw_power_well_ops = {
+ .sync_hw = hsw_power_well_sync_hw,
+ .enable = hsw_power_well_enable,
+ .disable = hsw_power_well_disable,
+ .is_enabled = hsw_power_well_enabled,
+};
+
static struct i915_power_well hsw_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
.domains = HSW_ALWAYS_ON_POWER_DOMAINS,
+ .ops = &i9xx_always_on_power_well_ops,
},
{
.name = "display",
- .domains = POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS,
- .is_enabled = hsw_power_well_enabled,
- .set = hsw_set_power_well,
+ .domains = HSW_DISPLAY_POWER_DOMAINS,
+ .ops = &hsw_power_well_ops,
},
};
@@ -5378,12 +5780,83 @@ static struct i915_power_well bdw_power_wells[] = {
.name = "always-on",
.always_on = 1,
.domains = BDW_ALWAYS_ON_POWER_DOMAINS,
+ .ops = &i9xx_always_on_power_well_ops,
},
{
.name = "display",
- .domains = POWER_DOMAIN_MASK & ~BDW_ALWAYS_ON_POWER_DOMAINS,
- .is_enabled = hsw_power_well_enabled,
- .set = hsw_set_power_well,
+ .domains = BDW_DISPLAY_POWER_DOMAINS,
+ .ops = &hsw_power_well_ops,
+ },
+};
+
+static const struct i915_power_well_ops vlv_display_power_well_ops = {
+ .sync_hw = vlv_power_well_sync_hw,
+ .enable = vlv_display_power_well_enable,
+ .disable = vlv_display_power_well_disable,
+ .is_enabled = vlv_power_well_enabled,
+};
+
+static const struct i915_power_well_ops vlv_dpio_power_well_ops = {
+ .sync_hw = vlv_power_well_sync_hw,
+ .enable = vlv_power_well_enable,
+ .disable = vlv_power_well_disable,
+ .is_enabled = vlv_power_well_enabled,
+};
+
+static struct i915_power_well vlv_power_wells[] = {
+ {
+ .name = "always-on",
+ .always_on = 1,
+ .domains = VLV_ALWAYS_ON_POWER_DOMAINS,
+ .ops = &i9xx_always_on_power_well_ops,
+ },
+ {
+ .name = "display",
+ .domains = VLV_DISPLAY_POWER_DOMAINS,
+ .data = PUNIT_POWER_WELL_DISP2D,
+ .ops = &vlv_display_power_well_ops,
+ },
+ {
+ .name = "dpio-common",
+ .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS,
+ .data = PUNIT_POWER_WELL_DPIO_CMN_BC,
+ .ops = &vlv_dpio_power_well_ops,
+ },
+ {
+ .name = "dpio-tx-b-01",
+ .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_B_LANES_01,
+ },
+ {
+ .name = "dpio-tx-b-23",
+ .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_B_LANES_23,
+ },
+ {
+ .name = "dpio-tx-c-01",
+ .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_01,
+ },
+ {
+ .name = "dpio-tx-c-23",
+ .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_23,
},
};
@@ -5392,9 +5865,8 @@ static struct i915_power_well bdw_power_wells[] = {
(power_domains)->power_well_count = ARRAY_SIZE(__power_wells); \
})
-int intel_power_domains_init(struct drm_device *dev)
+int intel_power_domains_init(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains = &dev_priv->power_domains;
mutex_init(&power_domains->lock);
@@ -5403,12 +5875,14 @@ int intel_power_domains_init(struct drm_device *dev)
* The enabling order will be from lower to higher indexed wells,
* the disabling order is reversed.
*/
- if (IS_HASWELL(dev)) {
+ if (IS_HASWELL(dev_priv->dev)) {
set_power_wells(power_domains, hsw_power_wells);
hsw_pwr = power_domains;
- } else if (IS_BROADWELL(dev)) {
+ } else if (IS_BROADWELL(dev_priv->dev)) {
set_power_wells(power_domains, bdw_power_wells);
hsw_pwr = power_domains;
+ } else if (IS_VALLEYVIEW(dev_priv->dev)) {
+ set_power_wells(power_domains, vlv_power_wells);
} else {
set_power_wells(power_domains, i9xx_always_on_power_well);
}
@@ -5416,58 +5890,38 @@ int intel_power_domains_init(struct drm_device *dev)
return 0;
}
-void intel_power_domains_remove(struct drm_device *dev)
+void intel_power_domains_remove(struct drm_i915_private *dev_priv)
{
hsw_pwr = NULL;
}
-static void intel_power_domains_resume(struct drm_device *dev)
+static void intel_power_domains_resume(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains = &dev_priv->power_domains;
struct i915_power_well *power_well;
int i;
mutex_lock(&power_domains->lock);
- for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) {
- if (power_well->set)
- power_well->set(dev, power_well, power_well->count > 0);
- }
+ for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains)
+ power_well->ops->sync_hw(dev_priv, power_well);
mutex_unlock(&power_domains->lock);
}
-/*
- * Starting with Haswell, we have a "Power Down Well" that can be turned off
- * when not needed anymore. We have 4 registers that can request the power well
- * to be enabled, and it will only be disabled if none of the registers is
- * requesting it to be enabled.
- */
-void intel_power_domains_init_hw(struct drm_device *dev)
+void intel_power_domains_init_hw(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
/* For now, we need the power well to be always enabled. */
- intel_display_set_init_power(dev, true);
- intel_power_domains_resume(dev);
-
- if (!(IS_HASWELL(dev) || IS_BROADWELL(dev)))
- return;
-
- /* We're taking over the BIOS, so clear any requests made by it since
- * the driver is in charge now. */
- if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE_REQUEST)
- I915_WRITE(HSW_PWR_WELL_BIOS, 0);
+ intel_display_set_init_power(dev_priv, true);
+ intel_power_domains_resume(dev_priv);
}
-/* Disables PC8 so we can use the GMBUS and DP AUX interrupts. */
void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv)
{
- hsw_disable_package_c8(dev_priv);
+ intel_runtime_pm_get(dev_priv);
}
void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv)
{
- hsw_enable_package_c8(dev_priv);
+ intel_runtime_pm_put(dev_priv);
}
void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
@@ -5499,8 +5953,6 @@ void intel_init_runtime_pm(struct drm_i915_private *dev_priv)
struct drm_device *dev = dev_priv->dev;
struct device *device = &dev->pdev->dev;
- dev_priv->pm.suspended = false;
-
if (!HAS_RUNTIME_PM(dev))
return;
@@ -5509,6 +5961,8 @@ void intel_init_runtime_pm(struct drm_i915_private *dev_priv)
pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */
pm_runtime_mark_last_busy(device);
pm_runtime_use_autosuspend(device);
+
+ pm_runtime_put_autosuspend(device);
}
void intel_fini_runtime_pm(struct drm_i915_private *dev_priv)
@@ -5560,7 +6014,7 @@ void intel_init_pm(struct drm_device *dev)
/* For FIFO watermark updates */
if (HAS_PCH_SPLIT(dev)) {
- intel_setup_wm_latency(dev);
+ ilk_setup_wm_latency(dev);
if ((IS_GEN5(dev) && dev_priv->wm.pri_latency[1] &&
dev_priv->wm.spr_latency[1] && dev_priv->wm.cur_latency[1]) ||
@@ -5731,13 +6185,9 @@ void intel_pm_setup(struct drm_device *dev)
mutex_init(&dev_priv->rps.hw_lock);
- mutex_init(&dev_priv->pc8.lock);
- dev_priv->pc8.requirements_met = false;
- dev_priv->pc8.gpu_idle = false;
- dev_priv->pc8.irqs_disabled = false;
- dev_priv->pc8.enabled = false;
- dev_priv->pc8.disable_count = 2; /* requirements_met + gpu_idle */
- INIT_DELAYED_WORK(&dev_priv->pc8.enable_work, hsw_enable_pc8_work);
INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work,
intel_gen6_powersave_work);
+
+ dev_priv->pm.suspended = false;
+ dev_priv->pm.irqs_disabled = false;
}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 31b36c5ac894..6bc68bdcf433 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -406,17 +406,24 @@ gen8_render_ring_flush(struct intel_ring_buffer *ring,
static void ring_write_tail(struct intel_ring_buffer *ring,
u32 value)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
I915_WRITE_TAIL(ring, value);
}
-u32 intel_ring_get_active_head(struct intel_ring_buffer *ring)
+u64 intel_ring_get_active_head(struct intel_ring_buffer *ring)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
- u32 acthd_reg = INTEL_INFO(ring->dev)->gen >= 4 ?
- RING_ACTHD(ring->mmio_base) : ACTHD;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ u64 acthd;
+
+ if (INTEL_INFO(ring->dev)->gen >= 8)
+ acthd = I915_READ64_2x32(RING_ACTHD(ring->mmio_base),
+ RING_ACTHD_UDW(ring->mmio_base));
+ else if (INTEL_INFO(ring->dev)->gen >= 4)
+ acthd = I915_READ(RING_ACTHD(ring->mmio_base));
+ else
+ acthd = I915_READ(ACTHD);
- return I915_READ(acthd_reg);
+ return acthd;
}
static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
@@ -433,22 +440,24 @@ static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
static int init_ring_common(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj = ring->obj;
int ret = 0;
u32 head;
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
- if (I915_NEED_GFX_HWS(dev))
- intel_ring_setup_status_page(ring);
- else
- ring_setup_phys_status_page(ring);
-
/* Stop the ring if it's running. */
I915_WRITE_CTL(ring, 0);
I915_WRITE_HEAD(ring, 0);
ring->write_tail(ring, 0);
+ if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000))
+ DRM_ERROR("%s :timed out trying to stop ring\n", ring->name);
+
+ if (I915_NEED_GFX_HWS(dev))
+ intel_ring_setup_status_page(ring);
+ else
+ ring_setup_phys_status_page(ring);
head = I915_READ_HEAD(ring) & HEAD_ADDR;
@@ -531,9 +540,11 @@ init_pipe_control(struct intel_ring_buffer *ring)
goto err;
}
- i915_gem_object_set_cache_level(ring->scratch.obj, I915_CACHE_LLC);
+ ret = i915_gem_object_set_cache_level(ring->scratch.obj, I915_CACHE_LLC);
+ if (ret)
+ goto err_unref;
- ret = i915_gem_obj_ggtt_pin(ring->scratch.obj, 4096, true, false);
+ ret = i915_gem_obj_ggtt_pin(ring->scratch.obj, 4096, 0);
if (ret)
goto err_unref;
@@ -549,7 +560,7 @@ init_pipe_control(struct intel_ring_buffer *ring)
return 0;
err_unpin:
- i915_gem_object_unpin(ring->scratch.obj);
+ i915_gem_object_ggtt_unpin(ring->scratch.obj);
err_unref:
drm_gem_object_unreference(&ring->scratch.obj->base);
err:
@@ -562,14 +573,15 @@ static int init_render_ring(struct intel_ring_buffer *ring)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret = init_ring_common(ring);
- if (INTEL_INFO(dev)->gen > 3)
+ /* WaTimedSingleVertexDispatch:cl,bw,ctg,elk,ilk,snb */
+ if (INTEL_INFO(dev)->gen >= 4 && INTEL_INFO(dev)->gen < 7)
I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(VS_TIMER_DISPATCH));
/* We need to disable the AsyncFlip performance optimisations in order
* to use MI_WAIT_FOR_EVENT within the CS. It should already be
* programmed to '1' on all products.
*
- * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv
+ * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv,bdw
*/
if (INTEL_INFO(dev)->gen >= 6)
I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE));
@@ -625,7 +637,7 @@ static void render_ring_cleanup(struct intel_ring_buffer *ring)
if (INTEL_INFO(dev)->gen >= 5) {
kunmap(sg_page(ring->scratch.obj->pages->sgl));
- i915_gem_object_unpin(ring->scratch.obj);
+ i915_gem_object_ggtt_unpin(ring->scratch.obj);
}
drm_gem_object_unreference(&ring->scratch.obj->base);
@@ -809,8 +821,11 @@ gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
/* 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 (!lazy_coherency)
- intel_ring_get_active_head(ring);
+ if (!lazy_coherency) {
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ POSTING_READ(RING_ACTHD(ring->mmio_base));
+ }
+
return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
}
@@ -842,7 +857,7 @@ static bool
gen5_ring_get_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
if (!dev->irq_enabled)
@@ -860,7 +875,7 @@ static void
gen5_ring_put_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
spin_lock_irqsave(&dev_priv->irq_lock, flags);
@@ -873,7 +888,7 @@ static bool
i9xx_ring_get_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
if (!dev->irq_enabled)
@@ -894,7 +909,7 @@ static void
i9xx_ring_put_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
spin_lock_irqsave(&dev_priv->irq_lock, flags);
@@ -910,7 +925,7 @@ static bool
i8xx_ring_get_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
if (!dev->irq_enabled)
@@ -931,7 +946,7 @@ static void
i8xx_ring_put_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
spin_lock_irqsave(&dev_priv->irq_lock, flags);
@@ -946,7 +961,7 @@ i8xx_ring_put_irq(struct intel_ring_buffer *ring)
void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
u32 mmio = 0;
/* The ring status page addresses are no longer next to the rest of
@@ -977,9 +992,19 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
I915_WRITE(mmio, (u32)ring->status_page.gfx_addr);
POSTING_READ(mmio);
- /* Flush the TLB for this page */
- if (INTEL_INFO(dev)->gen >= 6) {
+ /*
+ * Flush the TLB for this page
+ *
+ * FIXME: These two bits have disappeared on gen8, so a question
+ * arises: do we still need this and if so how should we go about
+ * invalidating the TLB?
+ */
+ if (INTEL_INFO(dev)->gen >= 6 && INTEL_INFO(dev)->gen < 8) {
u32 reg = RING_INSTPM(ring->mmio_base);
+
+ /* ring should be idle before issuing a sync flush*/
+ WARN_ON((I915_READ_MODE(ring) & MODE_IDLE) == 0);
+
I915_WRITE(reg,
_MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE |
INSTPM_SYNC_FLUSH));
@@ -1029,7 +1054,7 @@ static bool
gen6_ring_get_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
if (!dev->irq_enabled)
@@ -1054,7 +1079,7 @@ static void
gen6_ring_put_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
spin_lock_irqsave(&dev_priv->irq_lock, flags);
@@ -1253,7 +1278,7 @@ static void cleanup_status_page(struct intel_ring_buffer *ring)
return;
kunmap(sg_page(obj->pages->sgl));
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
drm_gem_object_unreference(&obj->base);
ring->status_page.obj = NULL;
}
@@ -1271,12 +1296,13 @@ static int init_status_page(struct intel_ring_buffer *ring)
goto err;
}
- i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
+ ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
+ if (ret)
+ goto err_unref;
- ret = i915_gem_obj_ggtt_pin(obj, 4096, true, false);
- if (ret != 0) {
+ ret = i915_gem_obj_ggtt_pin(obj, 4096, 0);
+ if (ret)
goto err_unref;
- }
ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(obj);
ring->status_page.page_addr = kmap(sg_page(obj->pages->sgl));
@@ -1293,7 +1319,7 @@ static int init_status_page(struct intel_ring_buffer *ring)
return 0;
err_unpin:
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
err_unref:
drm_gem_object_unreference(&obj->base);
err:
@@ -1356,7 +1382,7 @@ static int intel_init_ring_buffer(struct drm_device *dev,
ring->obj = obj;
- ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, true, false);
+ ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, PIN_MAPPABLE);
if (ret)
goto err_unref;
@@ -1385,12 +1411,14 @@ static int intel_init_ring_buffer(struct drm_device *dev,
if (IS_I830(ring->dev) || IS_845G(ring->dev))
ring->effective_size -= 128;
+ i915_cmd_parser_init_ring(ring);
+
return 0;
err_unmap:
iounmap(ring->virtual_start);
err_unpin:
- i915_gem_object_unpin(obj);
+ i915_gem_object_ggtt_unpin(obj);
err_unref:
drm_gem_object_unreference(&obj->base);
ring->obj = NULL;
@@ -1418,7 +1446,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
iounmap(ring->virtual_start);
- i915_gem_object_unpin(ring->obj);
+ i915_gem_object_ggtt_unpin(ring->obj);
drm_gem_object_unreference(&ring->obj->base);
ring->obj = NULL;
ring->preallocated_lazy_request = NULL;
@@ -1430,28 +1458,16 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
cleanup_status_page(ring);
}
-static int intel_ring_wait_seqno(struct intel_ring_buffer *ring, u32 seqno)
-{
- int ret;
-
- ret = i915_wait_seqno(ring, seqno);
- if (!ret)
- i915_gem_retire_requests_ring(ring);
-
- return ret;
-}
-
static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
{
struct drm_i915_gem_request *request;
- u32 seqno = 0;
+ u32 seqno = 0, tail;
int ret;
- i915_gem_retire_requests_ring(ring);
-
if (ring->last_retired_head != -1) {
ring->head = ring->last_retired_head;
ring->last_retired_head = -1;
+
ring->space = ring_space(ring);
if (ring->space >= n)
return 0;
@@ -1468,6 +1484,7 @@ static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
space += ring->size;
if (space >= n) {
seqno = request->seqno;
+ tail = request->tail;
break;
}
@@ -1482,15 +1499,11 @@ static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
if (seqno == 0)
return -ENOSPC;
- ret = intel_ring_wait_seqno(ring, seqno);
+ ret = i915_wait_seqno(ring, seqno);
if (ret)
return ret;
- if (WARN_ON(ring->last_retired_head == -1))
- return -ENOSPC;
-
- ring->head = ring->last_retired_head;
- ring->last_retired_head = -1;
+ ring->head = tail;
ring->space = ring_space(ring);
if (WARN_ON(ring->space < n))
return -ENOSPC;
@@ -1528,7 +1541,8 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
return 0;
}
- if (dev->primary->master) {
+ if (!drm_core_check_feature(dev, DRIVER_MODESET) &&
+ dev->primary->master) {
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
if (master_priv->sarea_priv)
master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
@@ -1632,7 +1646,7 @@ static int __intel_ring_prepare(struct intel_ring_buffer *ring,
int intel_ring_begin(struct intel_ring_buffer *ring,
int num_dwords)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
int ret;
ret = i915_gem_check_wedge(&dev_priv->gpu_error,
@@ -1694,7 +1708,7 @@ void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
u32 value)
{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
/* Every tail move must follow the sequence below */
@@ -1869,7 +1883,7 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring,
int intel_init_render_ring_buffer(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
ring->name = "render ring";
@@ -1954,7 +1968,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
return -ENOMEM;
}
- ret = i915_gem_obj_ggtt_pin(obj, 0, true, false);
+ ret = i915_gem_obj_ggtt_pin(obj, 0, 0);
if (ret != 0) {
drm_gem_object_unreference(&obj->base);
DRM_ERROR("Failed to ping batch bo\n");
@@ -1970,7 +1984,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
@@ -2038,7 +2052,7 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
int intel_init_bsd_ring_buffer(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[VCS];
ring->name = "bsd ring";
@@ -2101,7 +2115,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
int intel_init_blt_ring_buffer(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[BCS];
ring->name = "blitter ring";
@@ -2141,7 +2155,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
int intel_init_vebox_ring_buffer(struct drm_device *dev)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[VECS];
ring->name = "video enhancement ring";
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 0b243ce33714..270a6a973438 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -33,6 +33,8 @@ struct intel_hw_status_page {
#define I915_READ_IMR(ring) I915_READ(RING_IMR((ring)->mmio_base))
#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val)
+#define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base))
+
enum intel_ring_hangcheck_action {
HANGCHECK_IDLE = 0,
HANGCHECK_WAIT,
@@ -41,12 +43,14 @@ enum intel_ring_hangcheck_action {
HANGCHECK_HUNG,
};
+#define HANGCHECK_SCORE_RING_HUNG 31
+
struct intel_ring_hangcheck {
- bool deadlock;
+ u64 acthd;
u32 seqno;
- u32 acthd;
int score;
enum intel_ring_hangcheck_action action;
+ bool deadlock;
};
struct intel_ring_buffer {
@@ -162,6 +166,38 @@ struct intel_ring_buffer {
u32 gtt_offset;
volatile u32 *cpu_page;
} scratch;
+
+ /*
+ * Tables of commands the command parser needs to know about
+ * for this ring.
+ */
+ const struct drm_i915_cmd_table *cmd_tables;
+ int cmd_table_count;
+
+ /*
+ * Table of registers allowed in commands that read/write registers.
+ */
+ const u32 *reg_table;
+ int reg_count;
+
+ /*
+ * Table of registers allowed in commands that read/write registers, but
+ * only from the DRM master.
+ */
+ const u32 *master_reg_table;
+ int master_reg_count;
+
+ /*
+ * Returns the bitmask for the length field of the specified command.
+ * Return 0 for an unrecognized/invalid command.
+ *
+ * If the command parser finds an entry for a command in the ring's
+ * cmd_tables, it gets the command's length based on the table entry.
+ * If not, it calls this function to determine the per-ring length field
+ * encoding for the command (i.e. certain opcode ranges use certain bits
+ * to encode the command length in the header).
+ */
+ u32 (*get_cmd_length_mask)(u32 cmd_header);
};
static inline bool
@@ -256,7 +292,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev);
int intel_init_blt_ring_buffer(struct drm_device *dev);
int intel_init_vebox_ring_buffer(struct drm_device *dev);
-u32 intel_ring_get_active_head(struct intel_ring_buffer *ring);
+u64 intel_ring_get_active_head(struct intel_ring_buffer *ring);
void intel_ring_setup_status_page(struct intel_ring_buffer *ring);
static inline u32 intel_ring_get_tail(struct intel_ring_buffer *ring)
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 95bdfb3c431c..d27155adf5db 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -1461,7 +1461,7 @@ static void intel_enable_sdvo(struct intel_encoder *encoder)
u32 temp;
bool input1, input2;
int i;
- u8 status;
+ bool success;
temp = I915_READ(intel_sdvo->sdvo_reg);
if ((temp & SDVO_ENABLE) == 0) {
@@ -1475,12 +1475,12 @@ static void intel_enable_sdvo(struct intel_encoder *encoder)
for (i = 0; i < 2; i++)
intel_wait_for_vblank(dev, intel_crtc->pipe);
- status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
+ success = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
/* Warn if the device reported failure to sync.
* A lot of SDVO devices fail to notify of sync, but it's
* a given it the status is a success, we succeeded.
*/
- if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+ if (success && !input1) {
DRM_DEBUG_KMS("First %s output reported failure to "
"sync\n", SDVO_NAME(intel_sdvo));
}
@@ -2382,24 +2382,62 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, struct intel_sdvo *sdvo)
}
static void
+intel_sdvo_connector_unregister(struct intel_connector *intel_connector)
+{
+ struct drm_connector *drm_connector;
+ struct intel_sdvo *sdvo_encoder;
+
+ drm_connector = &intel_connector->base;
+ sdvo_encoder = intel_attached_sdvo(&intel_connector->base);
+
+ sysfs_remove_link(&drm_connector->kdev->kobj,
+ sdvo_encoder->ddc.dev.kobj.name);
+ intel_connector_unregister(intel_connector);
+}
+
+static int
intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
struct intel_sdvo *encoder)
{
- drm_connector_init(encoder->base.base.dev,
- &connector->base.base,
+ struct drm_connector *drm_connector;
+ int ret;
+
+ drm_connector = &connector->base.base;
+ ret = drm_connector_init(encoder->base.base.dev,
+ drm_connector,
&intel_sdvo_connector_funcs,
connector->base.base.connector_type);
+ if (ret < 0)
+ return ret;
- drm_connector_helper_add(&connector->base.base,
+ drm_connector_helper_add(drm_connector,
&intel_sdvo_connector_helper_funcs);
connector->base.base.interlace_allowed = 1;
connector->base.base.doublescan_allowed = 0;
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
+ connector->base.unregister = intel_sdvo_connector_unregister;
intel_connector_attach_encoder(&connector->base, &encoder->base);
- drm_sysfs_connector_add(&connector->base.base);
+ ret = drm_sysfs_connector_add(drm_connector);
+ if (ret < 0)
+ goto err1;
+
+ ret = sysfs_create_link(&encoder->ddc.dev.kobj,
+ &drm_connector->kdev->kobj,
+ encoder->ddc.dev.kobj.name);
+ if (ret < 0)
+ goto err2;
+
+ return 0;
+
+err2:
+ drm_sysfs_connector_remove(drm_connector);
+err1:
+ drm_connector_cleanup(drm_connector);
+
+ return ret;
}
static void
@@ -2459,7 +2497,11 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
intel_sdvo->is_hdmi = true;
}
- intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+ kfree(intel_sdvo_connector);
+ return false;
+ }
+
if (intel_sdvo->is_hdmi)
intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector);
@@ -2490,7 +2532,10 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
intel_sdvo->is_tv = true;
- intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+ kfree(intel_sdvo_connector);
+ return false;
+ }
if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
goto err;
@@ -2534,8 +2579,11 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
}
- intel_sdvo_connector_init(intel_sdvo_connector,
- intel_sdvo);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+ kfree(intel_sdvo_connector);
+ return false;
+ }
+
return true;
}
@@ -2566,7 +2614,11 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
}
- intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
+ kfree(intel_sdvo_connector);
+ return false;
+ }
+
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
goto err;
@@ -2980,7 +3032,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
* simplistic anyway to express such constraints, so just give up on
* cloning for SDVO encoders.
*/
- intel_sdvo->base.cloneable = false;
+ intel_sdvo->base.cloneable = 0;
intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 716a3c9c0751..336ae6c602f2 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -124,9 +124,6 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
crtc_w--;
crtc_h--;
- I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
- I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
-
linear_offset = y * fb->pitches[0] + x * pixel_size;
sprsurf_offset = intel_gen4_compute_page_offset(&x, &y,
obj->tiling_mode,
@@ -134,6 +131,9 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
fb->pitches[0]);
linear_offset -= sprsurf_offset;
+ I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
+ I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
+
if (obj->tiling_mode != I915_TILING_NONE)
I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);
else
@@ -293,15 +293,15 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (crtc_w != src_w || crtc_h != src_h)
sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
- I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
- I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
-
linear_offset = y * fb->pitches[0] + x * pixel_size;
sprsurf_offset =
intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,
pixel_size, fb->pitches[0]);
linear_offset -= sprsurf_offset;
+ I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
+ I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
+
/* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET
* register */
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
@@ -472,15 +472,15 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (crtc_w != src_w || crtc_h != src_h)
dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
- I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
- I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
-
linear_offset = y * fb->pitches[0] + x * pixel_size;
dvssurf_offset =
intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,
pixel_size, fb->pitches[0]);
linear_offset -= dvssurf_offset;
+ I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
+ I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
+
if (obj->tiling_mode != I915_TILING_NONE)
I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);
else
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 22cf0f4ba248..bafe92e317d5 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1189,8 +1189,8 @@ intel_tv_detect_type(struct intel_tv *intel_tv,
if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
i915_disable_pipestat(dev_priv, 0,
- PIPE_HOTPLUG_INTERRUPT_ENABLE |
- PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
+ PIPE_HOTPLUG_INTERRUPT_STATUS |
+ PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -1266,8 +1266,8 @@ intel_tv_detect_type(struct intel_tv *intel_tv,
if (connector->polled & DRM_CONNECTOR_POLL_HPD) {
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
i915_enable_pipestat(dev_priv, 0,
- PIPE_HOTPLUG_INTERRUPT_ENABLE |
- PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
+ PIPE_HOTPLUG_INTERRUPT_STATUS |
+ PIPE_HOTPLUG_TV_INTERRUPT_STATUS);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -1536,9 +1536,14 @@ static int tv_is_present_in_vbt(struct drm_device *dev)
/*
* If the device type is not TV, continue.
*/
- if (p_child->old.device_type != DEVICE_TYPE_INT_TV &&
- p_child->old.device_type != DEVICE_TYPE_TV)
+ switch (p_child->old.device_type) {
+ case DEVICE_TYPE_INT_TV:
+ case DEVICE_TYPE_TV:
+ case DEVICE_TYPE_TV_SVIDEO_COMPOSITE:
+ break;
+ default:
continue;
+ }
/* Only when the addin_offset is non-zero, it is regarded
* as present.
*/
@@ -1634,13 +1639,13 @@ intel_tv_init(struct drm_device *dev)
intel_encoder->disable = intel_disable_tv;
intel_encoder->get_hw_state = intel_tv_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
+ intel_connector->unregister = intel_connector_unregister;
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_TVOUT;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
- intel_encoder->cloneable = false;
+ intel_encoder->cloneable = 0;
intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
- intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
/* BIOS margin values */
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 87df68f5f504..f729dc71d5be 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -40,6 +40,12 @@
#define __raw_posting_read(dev_priv__, reg__) (void)__raw_i915_read32(dev_priv__, reg__)
+static void
+assert_device_not_suspended(struct drm_i915_private *dev_priv)
+{
+ WARN(HAS_RUNTIME_PM(dev_priv->dev) && dev_priv->pm.suspended,
+ "Device suspended\n");
+}
static void __gen6_gt_wait_for_thread_c0(struct drm_i915_private *dev_priv)
{
@@ -83,14 +89,14 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv,
__gen6_gt_wait_for_thread_c0(dev_priv);
}
-static void __gen6_gt_force_wake_mt_reset(struct drm_i915_private *dev_priv)
+static void __gen7_gt_force_wake_mt_reset(struct drm_i915_private *dev_priv)
{
__raw_i915_write32(dev_priv, FORCEWAKE_MT, _MASKED_BIT_DISABLE(0xffff));
/* something from same cacheline, but !FORCEWAKE_MT */
__raw_posting_read(dev_priv, ECOBUS);
}
-static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv,
+static void __gen7_gt_force_wake_mt_get(struct drm_i915_private *dev_priv,
int fw_engine)
{
u32 forcewake_ack;
@@ -136,14 +142,16 @@ static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv,
gen6_gt_check_fifodbg(dev_priv);
}
-static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv,
+static void __gen7_gt_force_wake_mt_put(struct drm_i915_private *dev_priv,
int fw_engine)
{
__raw_i915_write32(dev_priv, FORCEWAKE_MT,
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
/* something from same cacheline, but !FORCEWAKE_MT */
__raw_posting_read(dev_priv, ECOBUS);
- gen6_gt_check_fifodbg(dev_priv);
+
+ if (IS_GEN7(dev_priv->dev))
+ gen6_gt_check_fifodbg(dev_priv);
}
static int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
@@ -251,16 +259,16 @@ void vlv_force_wake_get(struct drm_i915_private *dev_priv,
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
- if (FORCEWAKE_RENDER & fw_engine) {
- if (dev_priv->uncore.fw_rendercount++ == 0)
- dev_priv->uncore.funcs.force_wake_get(dev_priv,
- FORCEWAKE_RENDER);
- }
- if (FORCEWAKE_MEDIA & fw_engine) {
- if (dev_priv->uncore.fw_mediacount++ == 0)
- dev_priv->uncore.funcs.force_wake_get(dev_priv,
- FORCEWAKE_MEDIA);
- }
+
+ if (fw_engine & FORCEWAKE_RENDER &&
+ dev_priv->uncore.fw_rendercount++ != 0)
+ fw_engine &= ~FORCEWAKE_RENDER;
+ if (fw_engine & FORCEWAKE_MEDIA &&
+ dev_priv->uncore.fw_mediacount++ != 0)
+ fw_engine &= ~FORCEWAKE_MEDIA;
+
+ if (fw_engine)
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, fw_engine);
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
@@ -272,46 +280,89 @@ void vlv_force_wake_put(struct drm_i915_private *dev_priv,
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
- if (FORCEWAKE_RENDER & fw_engine) {
- WARN_ON(dev_priv->uncore.fw_rendercount == 0);
- if (--dev_priv->uncore.fw_rendercount == 0)
- dev_priv->uncore.funcs.force_wake_put(dev_priv,
- FORCEWAKE_RENDER);
+ if (fw_engine & FORCEWAKE_RENDER) {
+ WARN_ON(!dev_priv->uncore.fw_rendercount);
+ if (--dev_priv->uncore.fw_rendercount != 0)
+ fw_engine &= ~FORCEWAKE_RENDER;
}
- if (FORCEWAKE_MEDIA & fw_engine) {
- WARN_ON(dev_priv->uncore.fw_mediacount == 0);
- if (--dev_priv->uncore.fw_mediacount == 0)
- dev_priv->uncore.funcs.force_wake_put(dev_priv,
- FORCEWAKE_MEDIA);
+ if (fw_engine & FORCEWAKE_MEDIA) {
+ WARN_ON(!dev_priv->uncore.fw_mediacount);
+ if (--dev_priv->uncore.fw_mediacount != 0)
+ fw_engine &= ~FORCEWAKE_MEDIA;
}
+ if (fw_engine)
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, fw_engine);
+
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
-static void gen6_force_wake_work(struct work_struct *work)
+static void gen6_force_wake_timer(unsigned long arg)
{
- struct drm_i915_private *dev_priv =
- container_of(work, typeof(*dev_priv), uncore.force_wake_work.work);
+ struct drm_i915_private *dev_priv = (void *)arg;
unsigned long irqflags;
+ assert_device_not_suspended(dev_priv);
+
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+ WARN_ON(!dev_priv->uncore.forcewake_count);
+
if (--dev_priv->uncore.forcewake_count == 0)
dev_priv->uncore.funcs.force_wake_put(dev_priv, FORCEWAKE_ALL);
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
+
+ intel_runtime_pm_put(dev_priv);
}
-static void intel_uncore_forcewake_reset(struct drm_device *dev)
+static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long irqflags;
- if (IS_VALLEYVIEW(dev)) {
+ del_timer_sync(&dev_priv->uncore.force_wake_timer);
+
+ /* Hold uncore.lock across reset to prevent any register access
+ * with forcewake not set correctly
+ */
+ spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+
+ if (IS_VALLEYVIEW(dev))
vlv_force_wake_reset(dev_priv);
- } else if (INTEL_INFO(dev)->gen >= 6) {
+ else if (IS_GEN6(dev) || IS_GEN7(dev))
__gen6_gt_force_wake_reset(dev_priv);
- if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
- __gen6_gt_force_wake_mt_reset(dev_priv);
+
+ if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_GEN8(dev))
+ __gen7_gt_force_wake_mt_reset(dev_priv);
+
+ if (restore) { /* If reset with a user forcewake, try to restore */
+ unsigned fw = 0;
+
+ if (IS_VALLEYVIEW(dev)) {
+ if (dev_priv->uncore.fw_rendercount)
+ fw |= FORCEWAKE_RENDER;
+
+ if (dev_priv->uncore.fw_mediacount)
+ fw |= FORCEWAKE_MEDIA;
+ } else {
+ if (dev_priv->uncore.forcewake_count)
+ fw = FORCEWAKE_ALL;
+ }
+
+ if (fw)
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, fw);
+
+ if (IS_GEN6(dev) || IS_GEN7(dev))
+ dev_priv->uncore.fifo_count =
+ __raw_i915_read32(dev_priv, GTFIFOCTL) &
+ GT_FIFO_FREE_ENTRIES_MASK;
+ } else {
+ dev_priv->uncore.forcewake_count = 0;
+ dev_priv->uncore.fw_rendercount = 0;
+ dev_priv->uncore.fw_mediacount = 0;
}
+
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
void intel_uncore_early_sanitize(struct drm_device *dev)
@@ -337,7 +388,7 @@ void intel_uncore_early_sanitize(struct drm_device *dev)
__raw_i915_write32(dev_priv, GTFIFODBG,
__raw_i915_read32(dev_priv, GTFIFODBG));
- intel_uncore_forcewake_reset(dev);
+ intel_uncore_forcewake_reset(dev, false);
}
void intel_uncore_sanitize(struct drm_device *dev)
@@ -354,7 +405,9 @@ void intel_uncore_sanitize(struct drm_device *dev)
mutex_lock(&dev_priv->rps.hw_lock);
reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
- if (reg_val & (RENDER_PWRGT | MEDIA_PWRGT | DISP2D_PWRGT))
+ if (reg_val & (PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_RENDER) |
+ PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_MEDIA) |
+ PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_DISP2D)))
vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0);
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -393,25 +446,40 @@ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine)
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine)
{
unsigned long irqflags;
+ bool delayed = false;
if (!dev_priv->uncore.funcs.force_wake_put)
return;
/* Redirect to VLV specific routine */
- if (IS_VALLEYVIEW(dev_priv->dev))
- return vlv_force_wake_put(dev_priv, fw_engine);
+ if (IS_VALLEYVIEW(dev_priv->dev)) {
+ vlv_force_wake_put(dev_priv, fw_engine);
+ goto out;
+ }
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+ WARN_ON(!dev_priv->uncore.forcewake_count);
+
if (--dev_priv->uncore.forcewake_count == 0) {
dev_priv->uncore.forcewake_count++;
- mod_delayed_work(dev_priv->wq,
- &dev_priv->uncore.force_wake_work,
- 1);
+ delayed = true;
+ mod_timer_pinned(&dev_priv->uncore.force_wake_timer,
+ jiffies + 1);
}
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
- intel_runtime_pm_put(dev_priv);
+out:
+ if (!delayed)
+ intel_runtime_pm_put(dev_priv);
+}
+
+void assert_force_wake_inactive(struct drm_i915_private *dev_priv)
+{
+ if (!dev_priv->uncore.funcs.force_wake_get)
+ return;
+
+ WARN_ON(dev_priv->uncore.forcewake_count > 0);
}
/* We give fast paths for the really cool registers */
@@ -446,16 +514,10 @@ hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
}
}
-static void
-assert_device_not_suspended(struct drm_i915_private *dev_priv)
-{
- WARN(HAS_RUNTIME_PM(dev_priv->dev) && dev_priv->pm.suspended,
- "Device suspended\n");
-}
-
#define REG_READ_HEADER(x) \
unsigned long irqflags; \
u##x val = 0; \
+ assert_device_not_suspended(dev_priv); \
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags)
#define REG_READ_FOOTER \
@@ -484,14 +546,13 @@ gen5_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
static u##x \
gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
REG_READ_HEADER(x); \
- if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
- if (dev_priv->uncore.forcewake_count == 0) \
- dev_priv->uncore.funcs.force_wake_get(dev_priv, \
- FORCEWAKE_ALL); \
+ if (dev_priv->uncore.forcewake_count == 0 && \
+ NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, \
+ FORCEWAKE_ALL); \
val = __raw_i915_read##x(dev_priv, reg); \
- if (dev_priv->uncore.forcewake_count == 0) \
- dev_priv->uncore.funcs.force_wake_put(dev_priv, \
- FORCEWAKE_ALL); \
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, \
+ FORCEWAKE_ALL); \
} else { \
val = __raw_i915_read##x(dev_priv, reg); \
} \
@@ -502,27 +563,19 @@ gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
static u##x \
vlv_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
unsigned fwengine = 0; \
- unsigned *fwcount; \
REG_READ_HEADER(x); \
- if (FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg)) { \
- fwengine = FORCEWAKE_RENDER; \
- fwcount = &dev_priv->uncore.fw_rendercount; \
- } \
- else if (FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)) { \
- fwengine = FORCEWAKE_MEDIA; \
- fwcount = &dev_priv->uncore.fw_mediacount; \
+ if (FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_rendercount == 0) \
+ fwengine = FORCEWAKE_RENDER; \
+ } else if (FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_mediacount == 0) \
+ fwengine = FORCEWAKE_MEDIA; \
} \
- if (fwengine != 0) { \
- if ((*fwcount)++ == 0) \
- (dev_priv)->uncore.funcs.force_wake_get(dev_priv, \
- fwengine); \
- val = __raw_i915_read##x(dev_priv, reg); \
- if (--(*fwcount) == 0) \
- (dev_priv)->uncore.funcs.force_wake_put(dev_priv, \
- fwengine); \
- } else { \
- val = __raw_i915_read##x(dev_priv, reg); \
- } \
+ if (fwengine) \
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, fwengine); \
+ val = __raw_i915_read##x(dev_priv, reg); \
+ if (fwengine) \
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, fwengine); \
REG_READ_FOOTER; \
}
@@ -554,6 +607,7 @@ __gen4_read(64)
#define REG_WRITE_HEADER \
unsigned long irqflags; \
trace_i915_reg_rw(true, reg, val, sizeof(val), trace); \
+ assert_device_not_suspended(dev_priv); \
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags)
#define REG_WRITE_FOOTER \
@@ -584,7 +638,6 @@ gen6_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
__fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
} \
- assert_device_not_suspended(dev_priv); \
__raw_i915_write##x(dev_priv, reg, val); \
if (unlikely(__fifo_ret)) { \
gen6_gt_check_fifodbg(dev_priv); \
@@ -600,7 +653,6 @@ hsw_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace)
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
__fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
} \
- assert_device_not_suspended(dev_priv); \
hsw_unclaimed_reg_clear(dev_priv, reg); \
__raw_i915_write##x(dev_priv, reg, val); \
if (unlikely(__fifo_ret)) { \
@@ -634,16 +686,17 @@ static bool is_gen8_shadowed(struct drm_i915_private *dev_priv, u32 reg)
#define __gen8_write(x) \
static void \
gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
- bool __needs_put = reg < 0x40000 && !is_gen8_shadowed(dev_priv, reg); \
REG_WRITE_HEADER; \
- if (__needs_put) { \
- dev_priv->uncore.funcs.force_wake_get(dev_priv, \
- FORCEWAKE_ALL); \
- } \
- __raw_i915_write##x(dev_priv, reg, val); \
- if (__needs_put) { \
- dev_priv->uncore.funcs.force_wake_put(dev_priv, \
- FORCEWAKE_ALL); \
+ if (reg < 0x40000 && !is_gen8_shadowed(dev_priv, reg)) { \
+ if (dev_priv->uncore.forcewake_count == 0) \
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, \
+ FORCEWAKE_ALL); \
+ __raw_i915_write##x(dev_priv, reg, val); \
+ if (dev_priv->uncore.forcewake_count == 0) \
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, \
+ FORCEWAKE_ALL); \
+ } else { \
+ __raw_i915_write##x(dev_priv, reg, val); \
} \
REG_WRITE_FOOTER; \
}
@@ -681,15 +734,17 @@ void intel_uncore_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- INIT_DELAYED_WORK(&dev_priv->uncore.force_wake_work,
- gen6_force_wake_work);
+ setup_timer(&dev_priv->uncore.force_wake_timer,
+ gen6_force_wake_timer, (unsigned long)dev_priv);
+
+ intel_uncore_early_sanitize(dev);
if (IS_VALLEYVIEW(dev)) {
dev_priv->uncore.funcs.force_wake_get = __vlv_force_wake_get;
dev_priv->uncore.funcs.force_wake_put = __vlv_force_wake_put;
} else if (IS_HASWELL(dev) || IS_GEN8(dev)) {
- dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get;
- dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put;
+ dev_priv->uncore.funcs.force_wake_get = __gen7_gt_force_wake_mt_get;
+ dev_priv->uncore.funcs.force_wake_put = __gen7_gt_force_wake_mt_put;
} else if (IS_IVYBRIDGE(dev)) {
u32 ecobus;
@@ -703,16 +758,16 @@ void intel_uncore_init(struct drm_device *dev)
* forcewake being disabled.
*/
mutex_lock(&dev->struct_mutex);
- __gen6_gt_force_wake_mt_get(dev_priv, FORCEWAKE_ALL);
+ __gen7_gt_force_wake_mt_get(dev_priv, FORCEWAKE_ALL);
ecobus = __raw_i915_read32(dev_priv, ECOBUS);
- __gen6_gt_force_wake_mt_put(dev_priv, FORCEWAKE_ALL);
+ __gen7_gt_force_wake_mt_put(dev_priv, FORCEWAKE_ALL);
mutex_unlock(&dev->struct_mutex);
if (ecobus & FORCEWAKE_MT_ENABLE) {
dev_priv->uncore.funcs.force_wake_get =
- __gen6_gt_force_wake_mt_get;
+ __gen7_gt_force_wake_mt_get;
dev_priv->uncore.funcs.force_wake_put =
- __gen6_gt_force_wake_mt_put;
+ __gen7_gt_force_wake_mt_put;
} else {
DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
DRM_INFO("when using vblank-synced partial screen updates.\n");
@@ -792,12 +847,9 @@ void intel_uncore_init(struct drm_device *dev)
void intel_uncore_fini(struct drm_device *dev)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- flush_delayed_work(&dev_priv->uncore.force_wake_work);
-
/* Paranoia: make sure we have disabled everything before we exit. */
intel_uncore_sanitize(dev);
+ intel_uncore_forcewake_reset(dev, false);
}
static const struct register_whitelist {
@@ -814,7 +866,7 @@ int i915_reg_read_ioctl(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_reg_read *reg = data;
struct register_whitelist const *entry = whitelist;
- int i;
+ int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) {
if (entry->offset == reg->offset &&
@@ -825,6 +877,8 @@ int i915_reg_read_ioctl(struct drm_device *dev,
if (i == ARRAY_SIZE(whitelist))
return -EINVAL;
+ intel_runtime_pm_get(dev_priv);
+
switch (entry->size) {
case 8:
reg->val = I915_READ64(reg->offset);
@@ -840,10 +894,13 @@ int i915_reg_read_ioctl(struct drm_device *dev,
break;
default:
WARN_ON(1);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- return 0;
+out:
+ intel_runtime_pm_put(dev_priv);
+ return ret;
}
int i915_get_reset_stats_ioctl(struct drm_device *dev,
@@ -852,6 +909,7 @@ int i915_get_reset_stats_ioctl(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_reset_stats *args = data;
struct i915_ctx_hang_stats *hs;
+ struct i915_hw_context *ctx;
int ret;
if (args->flags || args->pad)
@@ -864,11 +922,12 @@ int i915_get_reset_stats_ioctl(struct drm_device *dev,
if (ret)
return ret;
- hs = i915_gem_context_get_hang_stats(dev, file, args->ctx_id);
- if (IS_ERR(hs)) {
+ ctx = i915_gem_context_get(file->driver_priv, args->ctx_id);
+ if (IS_ERR(ctx)) {
mutex_unlock(&dev->struct_mutex);
- return PTR_ERR(hs);
+ return PTR_ERR(ctx);
}
+ hs = &ctx->hang_stats;
if (capable(CAP_SYS_ADMIN))
args->reset_count = i915_reset_count(&dev_priv->gpu_error);
@@ -944,12 +1003,6 @@ static int gen6_do_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- unsigned long irqflags;
-
- /* Hold uncore.lock across reset to prevent any register access
- * with forcewake not set correctly
- */
- spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
/* Reset the chip */
@@ -962,18 +1015,8 @@ static int gen6_do_reset(struct drm_device *dev)
/* Spin waiting for the device to ack the reset request */
ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500);
- intel_uncore_forcewake_reset(dev);
+ intel_uncore_forcewake_reset(dev, true);
- /* If reset with a user forcewake, try to restore, otherwise turn it off */
- if (dev_priv->uncore.forcewake_count)
- dev_priv->uncore.funcs.force_wake_get(dev_priv, FORCEWAKE_ALL);
- else
- dev_priv->uncore.funcs.force_wake_put(dev_priv, FORCEWAKE_ALL);
-
- /* Restore fifo count */
- dev_priv->uncore.fifo_count = __raw_i915_read32(dev_priv, GTFIFOCTL) & GT_FIFO_FREE_ENTRIES_MASK;
-
- spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
return ret;
}
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 968374776db9..a034ed408252 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -29,7 +29,7 @@ static void mga_crtc_load_lut(struct drm_crtc *crtc)
struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = dev->dev_private;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
int i;
if (!crtc->enabled)
@@ -742,7 +742,7 @@ static int mga_crtc_do_set_base(struct drm_crtc *crtc,
mgag200_bo_unreserve(bo);
}
- mga_fb = to_mga_framebuffer(crtc->fb);
+ mga_fb = to_mga_framebuffer(crtc->primary->fb);
obj = mga_fb->obj;
bo = gem_to_mga_bo(obj);
@@ -805,7 +805,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
/* 0x48: */ 0, 0, 0, 0, 0, 0, 0, 0
};
- bppshift = mdev->bpp_shifts[(crtc->fb->bits_per_pixel >> 3) - 1];
+ bppshift = mdev->bpp_shifts[(crtc->primary->fb->bits_per_pixel >> 3) - 1];
switch (mdev->type) {
case G200_SE_A:
@@ -843,12 +843,12 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
break;
}
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_8bits;
break;
case 16:
- if (crtc->fb->depth == 15)
+ if (crtc->primary->fb->depth == 15)
dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_15bits;
else
dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_16bits;
@@ -896,8 +896,8 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
WREG_SEQ(3, 0);
WREG_SEQ(4, 0xe);
- pitch = crtc->fb->pitches[0] / (crtc->fb->bits_per_pixel / 8);
- if (crtc->fb->bits_per_pixel == 24)
+ pitch = crtc->primary->fb->pitches[0] / (crtc->primary->fb->bits_per_pixel / 8);
+ if (crtc->primary->fb->bits_per_pixel == 24)
pitch = (pitch * 3) >> (4 - bppshift);
else
pitch = pitch >> (4 - bppshift);
@@ -974,7 +974,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
((vdisplay & 0xc00) >> 7) |
((vsyncstart & 0xc00) >> 5) |
((vdisplay & 0x400) >> 3);
- if (crtc->fb->bits_per_pixel == 24)
+ if (crtc->primary->fb->bits_per_pixel == 24)
ext_vga[3] = (((1 << bppshift) * 3) - 1) | 0x80;
else
ext_vga[3] = ((1 << bppshift) - 1) | 0x80;
@@ -1034,9 +1034,9 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
u32 bpp;
u32 mb;
- if (crtc->fb->bits_per_pixel > 16)
+ if (crtc->primary->fb->bits_per_pixel > 16)
bpp = 32;
- else if (crtc->fb->bits_per_pixel > 8)
+ else if (crtc->primary->fb->bits_per_pixel > 8)
bpp = 16;
else
bpp = 8;
@@ -1277,8 +1277,8 @@ static void mga_crtc_disable(struct drm_crtc *crtc)
int ret;
DRM_DEBUG_KMS("\n");
mga_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
- if (crtc->fb) {
- struct mga_framebuffer *mga_fb = to_mga_framebuffer(crtc->fb);
+ if (crtc->primary->fb) {
+ struct mga_framebuffer *mga_fb = to_mga_framebuffer(crtc->primary->fb);
struct drm_gem_object *obj = mga_fb->obj;
struct mgag200_bo *bo = gem_to_mga_bo(obj);
ret = mgag200_bo_reserve(bo, false);
@@ -1287,7 +1287,7 @@ static void mga_crtc_disable(struct drm_crtc *crtc)
mgag200_bo_push_sysram(bo);
mgag200_bo_unreserve(bo);
}
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
}
/* These provide the minimum set of functions required to handle a CRTC */
diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c
index adb5166a5dfd..5a00e90696de 100644
--- a/drivers/gpu/drm/mgag200/mgag200_ttm.c
+++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c
@@ -259,7 +259,9 @@ int mgag200_mm_init(struct mga_device *mdev)
ret = ttm_bo_device_init(&mdev->ttm.bdev,
mdev->ttm.bo_global_ref.ref.object,
- &mgag200_bo_driver, DRM_FILE_PAGE_OFFSET,
+ &mgag200_bo_driver,
+ dev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
true);
if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret);
@@ -324,7 +326,6 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align,
}
mgabo->bo.bdev = &mdev->ttm.bdev;
- mgabo->bo.bdev->dev_mapping = dev->dev_mapping;
mgag200_ttm_placement(mgabo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index c69d1e07a3a6..b6984971ce0c 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -3,7 +3,7 @@ config DRM_MSM
tristate "MSM DRM"
depends on DRM
depends on MSM_IOMMU
- depends on (ARCH_MSM && ARCH_MSM8960) || (ARM && COMPILE_TEST)
+ depends on ARCH_MSM8960 || (ARM && COMPILE_TEST)
select DRM_KMS_HELPER
select SHMEM
select TMPFS
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4f977a593bea..5e1e6b0cd8ac 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -7,6 +7,7 @@ msm-y := \
adreno/adreno_gpu.o \
adreno/a3xx_gpu.o \
hdmi/hdmi.o \
+ hdmi/hdmi_audio.o \
hdmi/hdmi_bridge.o \
hdmi/hdmi_connector.o \
hdmi/hdmi_i2c.o \
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index 461df93e825e..f20fbde5dc49 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -35,7 +35,11 @@
A3XX_INT0_CP_AHB_ERROR_HALT | \
A3XX_INT0_UCHE_OOB_ACCESS)
-static struct platform_device *a3xx_pdev;
+
+static bool hang_debug = false;
+MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)");
+module_param_named(hang_debug, hang_debug, bool, 0600);
+static void a3xx_dump(struct msm_gpu *gpu);
static void a3xx_me_init(struct msm_gpu *gpu)
{
@@ -291,6 +295,9 @@ static int a3xx_hw_init(struct msm_gpu *gpu)
static void a3xx_recover(struct msm_gpu *gpu)
{
+ /* dump registers before resetting gpu, if enabled: */
+ if (hang_debug)
+ a3xx_dump(gpu);
gpu_write(gpu, REG_A3XX_RBBM_SW_RESET_CMD, 1);
gpu_read(gpu, REG_A3XX_RBBM_SW_RESET_CMD);
gpu_write(gpu, REG_A3XX_RBBM_SW_RESET_CMD, 0);
@@ -311,27 +318,18 @@ static void a3xx_destroy(struct msm_gpu *gpu)
ocmem_free(OCMEM_GRAPHICS, a3xx_gpu->ocmem_hdl);
#endif
- put_device(&a3xx_gpu->pdev->dev);
kfree(a3xx_gpu);
}
static void a3xx_idle(struct msm_gpu *gpu)
{
- unsigned long t;
-
/* wait for ringbuffer to drain: */
adreno_idle(gpu);
- t = jiffies + ADRENO_IDLE_TIMEOUT;
-
/* then wait for GPU to finish: */
- do {
- uint32_t rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS);
- if (!(rbbm_status & A3XX_RBBM_STATUS_GPU_BUSY))
- return;
- } while(time_before(jiffies, t));
-
- DRM_ERROR("timeout waiting for %s to idle!\n", gpu->name);
+ if (spin_until(!(gpu_read(gpu, REG_A3XX_RBBM_STATUS) &
+ A3XX_RBBM_STATUS_GPU_BUSY)))
+ DRM_ERROR("%s: timeout waiting for GPU to idle!\n", gpu->name);
/* TODO maybe we need to reset GPU here to recover from hang? */
}
@@ -352,7 +350,6 @@ static irqreturn_t a3xx_irq(struct msm_gpu *gpu)
return IRQ_HANDLED;
}
-#ifdef CONFIG_DEBUG_FS
static const unsigned int a3xx_registers[] = {
0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027,
0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c,
@@ -392,11 +389,18 @@ static const unsigned int a3xx_registers[] = {
0x303c, 0x303c, 0x305e, 0x305f,
};
+#ifdef CONFIG_DEBUG_FS
static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
{
+ struct drm_device *dev = gpu->dev;
int i;
adreno_show(gpu, m);
+
+ mutex_lock(&dev->struct_mutex);
+
+ gpu->funcs->pm_resume(gpu);
+
seq_printf(m, "status: %08x\n",
gpu_read(gpu, REG_A3XX_RBBM_STATUS));
@@ -412,9 +416,36 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
}
}
+
+ gpu->funcs->pm_suspend(gpu);
+
+ mutex_unlock(&dev->struct_mutex);
}
#endif
+/* would be nice to not have to duplicate the _show() stuff with printk(): */
+static void a3xx_dump(struct msm_gpu *gpu)
+{
+ int i;
+
+ adreno_dump(gpu);
+ printk("status: %08x\n",
+ gpu_read(gpu, REG_A3XX_RBBM_STATUS));
+
+ /* dump these out in a form that can be parsed by demsm: */
+ printk("IO:region %s 00000000 00020000\n", gpu->name);
+ for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) {
+ uint32_t start = a3xx_registers[i];
+ uint32_t end = a3xx_registers[i+1];
+ uint32_t addr;
+
+ for (addr = start; addr <= end; addr++) {
+ uint32_t val = gpu_read(gpu, addr);
+ printk("IO:R %08x %08x\n", addr<<2, val);
+ }
+ }
+}
+
static const struct adreno_gpu_funcs funcs = {
.base = {
.get_param = adreno_get_param,
@@ -439,7 +470,8 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
struct a3xx_gpu *a3xx_gpu = NULL;
struct adreno_gpu *adreno_gpu;
struct msm_gpu *gpu;
- struct platform_device *pdev = a3xx_pdev;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct platform_device *pdev = priv->gpu_pdev;
struct adreno_platform_config *config;
int ret;
@@ -460,7 +492,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
adreno_gpu = &a3xx_gpu->base;
gpu = &adreno_gpu->base;
- get_device(&pdev->dev);
a3xx_gpu->pdev = pdev;
gpu->fast_rate = config->fast_rate;
@@ -522,17 +553,24 @@ fail:
# include <mach/kgsl.h>
#endif
-static int a3xx_probe(struct platform_device *pdev)
+static void set_gpu_pdev(struct drm_device *dev,
+ struct platform_device *pdev)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+ priv->gpu_pdev = pdev;
+}
+
+static int a3xx_bind(struct device *dev, struct device *master, void *data)
{
static struct adreno_platform_config config = {};
#ifdef CONFIG_OF
- struct device_node *child, *node = pdev->dev.of_node;
+ struct device_node *child, *node = dev->of_node;
u32 val;
int ret;
ret = of_property_read_u32(node, "qcom,chipid", &val);
if (ret) {
- dev_err(&pdev->dev, "could not find chipid: %d\n", ret);
+ dev_err(dev, "could not find chipid: %d\n", ret);
return ret;
}
@@ -548,7 +586,7 @@ static int a3xx_probe(struct platform_device *pdev)
for_each_child_of_node(child, pwrlvl) {
ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val);
if (ret) {
- dev_err(&pdev->dev, "could not find gpu-freq: %d\n", ret);
+ dev_err(dev, "could not find gpu-freq: %d\n", ret);
return ret;
}
config.fast_rate = max(config.fast_rate, val);
@@ -558,12 +596,12 @@ static int a3xx_probe(struct platform_device *pdev)
}
if (!config.fast_rate) {
- dev_err(&pdev->dev, "could not find clk rates\n");
+ dev_err(dev, "could not find clk rates\n");
return -ENXIO;
}
#else
- struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
+ struct kgsl_device_platform_data *pdata = dev->platform_data;
uint32_t version = socinfo_get_version();
if (cpu_is_apq8064ab()) {
config.fast_rate = 450000000;
@@ -609,14 +647,30 @@ static int a3xx_probe(struct platform_device *pdev)
config.bus_scale_table = pdata->bus_scale_table;
# endif
#endif
- pdev->dev.platform_data = &config;
- a3xx_pdev = pdev;
+ dev->platform_data = &config;
+ set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev));
return 0;
}
+static void a3xx_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ set_gpu_pdev(dev_get_drvdata(master), NULL);
+}
+
+static const struct component_ops a3xx_ops = {
+ .bind = a3xx_bind,
+ .unbind = a3xx_unbind,
+};
+
+static int a3xx_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &a3xx_ops);
+}
+
static int a3xx_remove(struct platform_device *pdev)
{
- a3xx_pdev = NULL;
+ component_del(&pdev->dev, &a3xx_ops);
return 0;
}
@@ -624,7 +678,6 @@ static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,kgsl-3d0" },
{}
};
-MODULE_DEVICE_TABLE(of, dt_match);
static struct platform_driver a3xx_driver = {
.probe = a3xx_probe,
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index d321099abdd4..28ca8cd8b09e 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -73,6 +73,12 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
case MSM_PARAM_GMEM_SIZE:
*value = adreno_gpu->gmem;
return 0;
+ case MSM_PARAM_CHIP_ID:
+ *value = adreno_gpu->rev.patchid |
+ (adreno_gpu->rev.minor << 8) |
+ (adreno_gpu->rev.major << 16) |
+ (adreno_gpu->rev.core << 24);
+ return 0;
default:
DBG("%s: invalid param: %u", gpu->name, param);
return -EINVAL;
@@ -225,19 +231,11 @@ void adreno_flush(struct msm_gpu *gpu)
void adreno_idle(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
- uint32_t rptr, wptr = get_wptr(gpu->rb);
- unsigned long t;
-
- t = jiffies + ADRENO_IDLE_TIMEOUT;
-
- /* then wait for CP to drain ringbuffer: */
- do {
- rptr = adreno_gpu->memptrs->rptr;
- if (rptr == wptr)
- return;
- } while(time_before(jiffies, t));
+ uint32_t wptr = get_wptr(gpu->rb);
- DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name);
+ /* wait for CP to drain ringbuffer: */
+ if (spin_until(adreno_gpu->memptrs->rptr == wptr))
+ DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name);
/* TODO maybe we need to reset GPU here to recover from hang? */
}
@@ -260,22 +258,37 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
}
#endif
-void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
+/* would be nice to not have to duplicate the _show() stuff with printk(): */
+void adreno_dump(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
- uint32_t freedwords;
- unsigned long t = jiffies + ADRENO_IDLE_TIMEOUT;
- do {
- uint32_t size = gpu->rb->size / 4;
- uint32_t wptr = get_wptr(gpu->rb);
- uint32_t rptr = adreno_gpu->memptrs->rptr;
- freedwords = (rptr + (size - 1) - wptr) % size;
-
- if (time_after(jiffies, t)) {
- DRM_ERROR("%s: timeout waiting for ringbuffer space\n", gpu->name);
- break;
- }
- } while(freedwords < ndwords);
+
+ printk("revision: %d (%d.%d.%d.%d)\n",
+ adreno_gpu->info->revn, adreno_gpu->rev.core,
+ adreno_gpu->rev.major, adreno_gpu->rev.minor,
+ adreno_gpu->rev.patchid);
+
+ printk("fence: %d/%d\n", adreno_gpu->memptrs->fence,
+ gpu->submitted_fence);
+ printk("rptr: %d\n", adreno_gpu->memptrs->rptr);
+ printk("wptr: %d\n", adreno_gpu->memptrs->wptr);
+ printk("rb wptr: %d\n", get_wptr(gpu->rb));
+
+}
+
+static uint32_t ring_freewords(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ uint32_t size = gpu->rb->size / 4;
+ uint32_t wptr = get_wptr(gpu->rb);
+ uint32_t rptr = adreno_gpu->memptrs->rptr;
+ return (rptr + (size - 1) - wptr) % size;
+}
+
+void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords)
+{
+ if (spin_until(ring_freewords(gpu) >= ndwords))
+ DRM_ERROR("%s: timeout waiting for ringbuffer space\n", gpu->name);
}
static const char *iommu_ports[] = {
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index ca11ea4da165..63c36ce33020 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -76,7 +76,20 @@ struct adreno_platform_config {
#endif
};
-#define ADRENO_IDLE_TIMEOUT (20 * 1000)
+#define ADRENO_IDLE_TIMEOUT msecs_to_jiffies(1000)
+
+#define spin_until(X) ({ \
+ int __ret = -ETIMEDOUT; \
+ unsigned long __t = jiffies + ADRENO_IDLE_TIMEOUT; \
+ do { \
+ if (X) { \
+ __ret = 0; \
+ break; \
+ } \
+ } while (time_before(jiffies, __t)); \
+ __ret; \
+})
+
static inline bool adreno_is_a3xx(struct adreno_gpu *gpu)
{
@@ -114,6 +127,7 @@ void adreno_idle(struct msm_gpu *gpu);
#ifdef CONFIG_DEBUG_FS
void adreno_show(struct msm_gpu *gpu, struct seq_file *m);
#endif
+void adreno_dump(struct msm_gpu *gpu);
void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords);
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 6f1588aa9071..ae750f6928c1 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -17,8 +17,6 @@
#include "hdmi.h"
-static struct platform_device *hdmi_pdev;
-
void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
{
uint32_t ctrl = 0;
@@ -67,7 +65,7 @@ void hdmi_destroy(struct kref *kref)
if (hdmi->i2c)
hdmi_i2c_destroy(hdmi->i2c);
- put_device(&hdmi->pdev->dev);
+ platform_set_drvdata(hdmi->pdev, NULL);
}
/* initialize connector */
@@ -75,7 +73,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
{
struct hdmi *hdmi = NULL;
struct msm_drm_private *priv = dev->dev_private;
- struct platform_device *pdev = hdmi_pdev;
+ struct platform_device *pdev = priv->hdmi_pdev;
struct hdmi_platform_config *config;
int i, ret;
@@ -95,13 +93,13 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
kref_init(&hdmi->refcount);
- get_device(&pdev->dev);
-
hdmi->dev = dev;
hdmi->pdev = pdev;
hdmi->config = config;
hdmi->encoder = encoder;
+ hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
+
/* not sure about which phy maps to which msm.. probably I miss some */
if (config->phy_init)
hdmi->phy = config->phy_init(hdmi);
@@ -228,6 +226,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
priv->bridges[priv->num_bridges++] = hdmi->bridge;
priv->connectors[priv->num_connectors++] = hdmi->connector;
+ platform_set_drvdata(pdev, hdmi);
+
return hdmi;
fail:
@@ -249,17 +249,24 @@ fail:
#include <linux/of_gpio.h>
-static int hdmi_dev_probe(struct platform_device *pdev)
+static void set_hdmi_pdev(struct drm_device *dev,
+ struct platform_device *pdev)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+ priv->hdmi_pdev = pdev;
+}
+
+static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
static struct hdmi_platform_config config = {};
#ifdef CONFIG_OF
- struct device_node *of_node = pdev->dev.of_node;
+ struct device_node *of_node = dev->of_node;
int get_gpio(const char *name)
{
int gpio = of_get_named_gpio(of_node, name, 0);
if (gpio < 0) {
- dev_err(&pdev->dev, "failed to get gpio: %s (%d)\n",
+ dev_err(dev, "failed to get gpio: %s (%d)\n",
name, gpio);
gpio = -1;
}
@@ -305,7 +312,7 @@ static int hdmi_dev_probe(struct platform_device *pdev)
config.ddc_data_gpio = 71;
config.hpd_gpio = 72;
config.mux_en_gpio = -1;
- config.mux_sel_gpio = 13 + NR_GPIO_IRQS;
+ config.mux_sel_gpio = -1;
} else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
static const char *hpd_reg_names[] = {"8921_hdmi_mvs"};
config.phy_init = hdmi_phy_8960_init;
@@ -336,14 +343,30 @@ static int hdmi_dev_probe(struct platform_device *pdev)
config.mux_sel_gpio = -1;
}
#endif
- pdev->dev.platform_data = &config;
- hdmi_pdev = pdev;
+ dev->platform_data = &config;
+ set_hdmi_pdev(dev_get_drvdata(master), to_platform_device(dev));
return 0;
}
+static void hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ set_hdmi_pdev(dev_get_drvdata(master), NULL);
+}
+
+static const struct component_ops hdmi_ops = {
+ .bind = hdmi_bind,
+ .unbind = hdmi_unbind,
+};
+
+static int hdmi_dev_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &hdmi_ops);
+}
+
static int hdmi_dev_remove(struct platform_device *pdev)
{
- hdmi_pdev = NULL;
+ component_del(&pdev->dev, &hdmi_ops);
return 0;
}
@@ -351,7 +374,6 @@ static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,hdmi-tx" },
{}
};
-MODULE_DEVICE_TABLE(of, dt_match);
static struct platform_driver hdmi_driver = {
.probe = hdmi_dev_probe,
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index 41b29add70b1..9fafee6a3e43 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -22,6 +22,7 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/hdmi.h>
#include "msm_drv.h"
#include "hdmi.xml.h"
@@ -30,6 +31,12 @@
struct hdmi_phy;
struct hdmi_platform_config;
+struct hdmi_audio {
+ bool enabled;
+ struct hdmi_audio_infoframe infoframe;
+ int rate;
+};
+
struct hdmi {
struct kref refcount;
@@ -38,6 +45,13 @@ struct hdmi {
const struct hdmi_platform_config *config;
+ /* audio state: */
+ struct hdmi_audio audio;
+
+ /* video state: */
+ bool power_on;
+ unsigned long int pixclock;
+
void __iomem *mmio;
struct regulator *hpd_regs[2];
@@ -132,6 +146,17 @@ struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);
struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi);
/*
+ * audio:
+ */
+
+int hdmi_audio_update(struct hdmi *hdmi);
+int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled,
+ uint32_t num_of_channels, uint32_t channel_allocation,
+ uint32_t level_shift, bool down_mix);
+void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate);
+
+
+/*
* hdmi bridge:
*/
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c
new file mode 100644
index 000000000000..872485f60134
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/hdmi.h>
+#include "hdmi.h"
+
+
+/* Supported HDMI Audio channels */
+#define MSM_HDMI_AUDIO_CHANNEL_2 0
+#define MSM_HDMI_AUDIO_CHANNEL_4 1
+#define MSM_HDMI_AUDIO_CHANNEL_6 2
+#define MSM_HDMI_AUDIO_CHANNEL_8 3
+
+/* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */
+static int nchannels[] = { 2, 4, 6, 8 };
+
+/* Supported HDMI Audio sample rates */
+#define MSM_HDMI_SAMPLE_RATE_32KHZ 0
+#define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1
+#define MSM_HDMI_SAMPLE_RATE_48KHZ 2
+#define MSM_HDMI_SAMPLE_RATE_88_2KHZ 3
+#define MSM_HDMI_SAMPLE_RATE_96KHZ 4
+#define MSM_HDMI_SAMPLE_RATE_176_4KHZ 5
+#define MSM_HDMI_SAMPLE_RATE_192KHZ 6
+#define MSM_HDMI_SAMPLE_RATE_MAX 7
+
+
+struct hdmi_msm_audio_acr {
+ uint32_t n; /* N parameter for clock regeneration */
+ uint32_t cts; /* CTS parameter for clock regeneration */
+};
+
+struct hdmi_msm_audio_arcs {
+ unsigned long int pixclock;
+ struct hdmi_msm_audio_acr lut[MSM_HDMI_SAMPLE_RATE_MAX];
+};
+
+#define HDMI_MSM_AUDIO_ARCS(pclk, ...) { (1000 * (pclk)), __VA_ARGS__ }
+
+/* Audio constants lookup table for hdmi_msm_audio_acr_setup */
+/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
+static const struct hdmi_msm_audio_arcs acr_lut[] = {
+ /* 25.200MHz */
+ HDMI_MSM_AUDIO_ARCS(25200, {
+ {4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000},
+ {12288, 25200}, {25088, 28000}, {24576, 25200} }),
+ /* 27.000MHz */
+ HDMI_MSM_AUDIO_ARCS(27000, {
+ {4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000},
+ {12288, 27000}, {25088, 30000}, {24576, 27000} }),
+ /* 27.027MHz */
+ HDMI_MSM_AUDIO_ARCS(27030, {
+ {4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030},
+ {12288, 27027}, {25088, 30030}, {24576, 27027} }),
+ /* 74.250MHz */
+ HDMI_MSM_AUDIO_ARCS(74250, {
+ {4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500},
+ {12288, 74250}, {25088, 82500}, {24576, 74250} }),
+ /* 148.500MHz */
+ HDMI_MSM_AUDIO_ARCS(148500, {
+ {4096, 148500}, {6272, 165000}, {6144, 148500}, {12544, 165000},
+ {12288, 148500}, {25088, 165000}, {24576, 148500} }),
+};
+
+static const struct hdmi_msm_audio_arcs *get_arcs(unsigned long int pixclock)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(acr_lut); i++) {
+ const struct hdmi_msm_audio_arcs *arcs = &acr_lut[i];
+ if (arcs->pixclock == pixclock)
+ return arcs;
+ }
+
+ return NULL;
+}
+
+int hdmi_audio_update(struct hdmi *hdmi)
+{
+ struct hdmi_audio *audio = &hdmi->audio;
+ struct hdmi_audio_infoframe *info = &audio->infoframe;
+ const struct hdmi_msm_audio_arcs *arcs = NULL;
+ bool enabled = audio->enabled;
+ uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl;
+ uint32_t infofrm_ctrl, audio_config;
+
+ DBG("audio: enabled=%d, channels=%d, channel_allocation=0x%x, "
+ "level_shift_value=%d, downmix_inhibit=%d, rate=%d",
+ audio->enabled, info->channels, info->channel_allocation,
+ info->level_shift_value, info->downmix_inhibit, audio->rate);
+ DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock);
+
+ if (enabled && !(hdmi->power_on && hdmi->pixclock)) {
+ DBG("disabling audio: no video");
+ enabled = false;
+ }
+
+ if (enabled) {
+ arcs = get_arcs(hdmi->pixclock);
+ if (!arcs) {
+ DBG("disabling audio: unsupported pixclock: %lu",
+ hdmi->pixclock);
+ enabled = false;
+ }
+ }
+
+ /* Read first before writing */
+ acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL);
+ vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL);
+ aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1);
+ infofrm_ctrl = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0);
+ audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG);
+
+ /* Clear N/CTS selection bits */
+ acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SELECT__MASK;
+
+ if (enabled) {
+ uint32_t n, cts, multiplier;
+ enum hdmi_acr_cts select;
+ uint8_t buf[14];
+
+ n = arcs->lut[audio->rate].n;
+ cts = arcs->lut[audio->rate].cts;
+
+ if ((MSM_HDMI_SAMPLE_RATE_192KHZ == audio->rate) ||
+ (MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio->rate)) {
+ multiplier = 4;
+ n >>= 2; /* divide N by 4 and use multiplier */
+ } else if ((MSM_HDMI_SAMPLE_RATE_96KHZ == audio->rate) ||
+ (MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio->rate)) {
+ multiplier = 2;
+ n >>= 1; /* divide N by 2 and use multiplier */
+ } else {
+ multiplier = 1;
+ }
+
+ DBG("n=%u, cts=%u, multiplier=%u", n, cts, multiplier);
+
+ acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SOURCE;
+ acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_AUDIO_PRIORITY;
+ acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_N_MULTIPLIER(multiplier);
+
+ if ((MSM_HDMI_SAMPLE_RATE_48KHZ == audio->rate) ||
+ (MSM_HDMI_SAMPLE_RATE_96KHZ == audio->rate) ||
+ (MSM_HDMI_SAMPLE_RATE_192KHZ == audio->rate))
+ select = ACR_48;
+ else if ((MSM_HDMI_SAMPLE_RATE_44_1KHZ == audio->rate) ||
+ (MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio->rate) ||
+ (MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio->rate))
+ select = ACR_44;
+ else /* default to 32k */
+ select = ACR_32;
+
+ acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SELECT(select);
+
+ hdmi_write(hdmi, REG_HDMI_ACR_0(select - 1),
+ HDMI_ACR_0_CTS(cts));
+ hdmi_write(hdmi, REG_HDMI_ACR_1(select - 1),
+ HDMI_ACR_1_N(n));
+
+ hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2,
+ COND(info->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) |
+ HDMI_AUDIO_PKT_CTRL2_OVERRIDE);
+
+ acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT;
+ acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND;
+
+ /* configure infoframe: */
+ hdmi_audio_infoframe_pack(info, buf, sizeof(buf));
+ hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0,
+ (buf[3] << 0) || (buf[4] << 8) ||
+ (buf[5] << 16) || (buf[6] << 24));
+ hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1,
+ (buf[7] << 0) || (buf[8] << 8));
+
+ hdmi_write(hdmi, REG_HDMI_GC, 0);
+
+ vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE;
+ vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME;
+
+ aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND;
+
+ infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND;
+ infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT;
+ infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE;
+ infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE;
+
+ audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK;
+ audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4);
+ audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE;
+ } else {
+ hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
+ acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_CONT;
+ acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SEND;
+ vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE;
+ vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME;
+ aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND;
+ infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND;
+ infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT;
+ infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE;
+ infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE;
+ audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE;
+ }
+
+ hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl);
+ hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl);
+ hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl);
+ hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, infofrm_ctrl);
+
+ hdmi_write(hdmi, REG_HDMI_AUD_INT,
+ COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) |
+ COND(enabled, HDMI_AUD_INT_AUD_SAM_DROP_INT));
+
+ hdmi_write(hdmi, REG_HDMI_AUDIO_CFG, audio_config);
+
+
+ DBG("audio %sabled", enabled ? "en" : "dis");
+
+ return 0;
+}
+
+int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled,
+ uint32_t num_of_channels, uint32_t channel_allocation,
+ uint32_t level_shift, bool down_mix)
+{
+ struct hdmi_audio *audio;
+
+ if (!hdmi)
+ return -ENXIO;
+
+ audio = &hdmi->audio;
+
+ if (num_of_channels >= ARRAY_SIZE(nchannels))
+ return -EINVAL;
+
+ audio->enabled = enabled;
+ audio->infoframe.channels = nchannels[num_of_channels];
+ audio->infoframe.channel_allocation = channel_allocation;
+ audio->infoframe.level_shift_value = level_shift;
+ audio->infoframe.downmix_inhibit = down_mix;
+
+ return hdmi_audio_update(hdmi);
+}
+
+void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate)
+{
+ struct hdmi_audio *audio;
+
+ if (!hdmi)
+ return;
+
+ audio = &hdmi->audio;
+
+ if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX))
+ return;
+
+ audio->rate = rate;
+ hdmi_audio_update(hdmi);
+}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
index 7d10e55403c6..f6cf745c249e 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
@@ -19,11 +19,7 @@
struct hdmi_bridge {
struct drm_bridge base;
-
struct hdmi *hdmi;
- bool power_on;
-
- unsigned long int pixclock;
};
#define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base)
@@ -52,8 +48,8 @@ static void power_on(struct drm_bridge *bridge)
}
if (config->pwr_clk_cnt > 0) {
- DBG("pixclock: %lu", hdmi_bridge->pixclock);
- ret = clk_set_rate(hdmi->pwr_clks[0], hdmi_bridge->pixclock);
+ DBG("pixclock: %lu", hdmi->pixclock);
+ ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock);
if (ret) {
dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n",
config->pwr_clk_names[0], ret);
@@ -102,12 +98,13 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)
DBG("power up");
- if (!hdmi_bridge->power_on) {
+ if (!hdmi->power_on) {
power_on(bridge);
- hdmi_bridge->power_on = true;
+ hdmi->power_on = true;
+ hdmi_audio_update(hdmi);
}
- phy->funcs->powerup(phy, hdmi_bridge->pixclock);
+ phy->funcs->powerup(phy, hdmi->pixclock);
hdmi_set_mode(hdmi, true);
}
@@ -129,9 +126,10 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge)
hdmi_set_mode(hdmi, false);
phy->funcs->powerdown(phy);
- if (hdmi_bridge->power_on) {
+ if (hdmi->power_on) {
power_off(bridge);
- hdmi_bridge->power_on = false;
+ hdmi->power_on = false;
+ hdmi_audio_update(hdmi);
}
}
@@ -146,7 +144,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,
mode = adjusted_mode;
- hdmi_bridge->pixclock = mode->clock * 1000;
+ hdmi->pixclock = mode->clock * 1000;
hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1;
@@ -194,9 +192,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,
DBG("frame_ctrl=%08x", frame_ctrl);
hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);
- // TODO until we have audio, this might be safest:
- if (hdmi->hdmi_mode)
- hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
+ hdmi_audio_update(hdmi);
}
static const struct drm_bridge_funcs hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index 84c5b13b33c9..3e6c0f3ed592 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -120,7 +120,7 @@ static void update_fb(struct drm_crtc *crtc, struct drm_framebuffer *new_fb)
/* grab reference to incoming scanout fb: */
drm_framebuffer_reference(new_fb);
- mdp4_crtc->base.fb = new_fb;
+ mdp4_crtc->base.primary->fb = new_fb;
mdp4_crtc->fb = new_fb;
if (old_fb)
@@ -182,7 +182,7 @@ static void pageflip_cb(struct msm_fence_cb *cb)
struct mdp4_crtc *mdp4_crtc =
container_of(cb, struct mdp4_crtc, pageflip_cb);
struct drm_crtc *crtc = &mdp4_crtc->base;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
if (!fb)
return;
@@ -348,14 +348,14 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
mode->type, mode->flags);
/* grab extra ref for update_scanout() */
- drm_framebuffer_reference(crtc->fb);
+ drm_framebuffer_reference(crtc->primary->fb);
- ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->fb,
+ ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x << 16, y << 16,
mode->hdisplay << 16, mode->vdisplay << 16);
if (ret) {
- drm_framebuffer_unreference(crtc->fb);
+ drm_framebuffer_unreference(crtc->primary->fb);
dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n",
mdp4_crtc->name, ret);
return ret;
@@ -368,7 +368,7 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
/* take data from pipe: */
mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_BASE(dma), 0);
mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_STRIDE(dma),
- crtc->fb->pitches[0]);
+ crtc->primary->fb->pitches[0]);
mdp4_write(mdp4_kms, REG_MDP4_DMA_DST_SIZE(dma),
MDP4_DMA_DST_SIZE_WIDTH(0) |
MDP4_DMA_DST_SIZE_HEIGHT(0));
@@ -378,7 +378,7 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
MDP4_OVLP_SIZE_WIDTH(mode->hdisplay) |
MDP4_OVLP_SIZE_HEIGHT(mode->vdisplay));
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STRIDE(ovlp),
- crtc->fb->pitches[0]);
+ crtc->primary->fb->pitches[0]);
mdp4_write(mdp4_kms, REG_MDP4_OVLP_CFG(ovlp), 1);
@@ -388,8 +388,8 @@ static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(2), 0x00ff0000);
}
- update_fb(crtc, crtc->fb);
- update_scanout(crtc, crtc->fb);
+ update_fb(crtc, crtc->primary->fb);
+ update_scanout(crtc, crtc->primary->fb);
return 0;
}
@@ -420,19 +420,19 @@ static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
int ret;
/* grab extra ref for update_scanout() */
- drm_framebuffer_reference(crtc->fb);
+ drm_framebuffer_reference(crtc->primary->fb);
- ret = mdp4_plane_mode_set(plane, crtc, crtc->fb,
+ ret = mdp4_plane_mode_set(plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x << 16, y << 16,
mode->hdisplay << 16, mode->vdisplay << 16);
if (ret) {
- drm_framebuffer_unreference(crtc->fb);
+ drm_framebuffer_unreference(crtc->primary->fb);
return ret;
}
- update_fb(crtc, crtc->fb);
- update_scanout(crtc, crtc->fb);
+ update_fb(crtc, crtc->primary->fb);
+ update_scanout(crtc, crtc->primary->fb);
return 0;
}
@@ -740,6 +740,9 @@ void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane)
{
+ /* don't actually detatch our primary plane: */
+ if (to_mdp4_crtc(crtc)->plane == plane)
+ return;
set_attach(crtc, mdp4_plane_pipe(plane), NULL);
}
@@ -791,7 +794,7 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
- drm_crtc_init(dev, crtc, &mdp4_crtc_funcs);
+ drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);
drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
mdp4_plane_install_properties(mdp4_crtc->plane, &crtc->base);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
index 1e893dd13859..66f33dba1ebb 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
@@ -222,6 +222,7 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev,
struct drm_plane *plane = NULL;
struct mdp4_plane *mdp4_plane;
int ret;
+ enum drm_plane_type type;
mdp4_plane = kzalloc(sizeof(*mdp4_plane), GFP_KERNEL);
if (!mdp4_plane) {
@@ -237,9 +238,10 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev,
mdp4_plane->nformats = mdp4_get_formats(pipe_id, mdp4_plane->formats,
ARRAY_SIZE(mdp4_plane->formats));
- drm_plane_init(dev, plane, 0xff, &mdp4_plane_funcs,
- mdp4_plane->formats, mdp4_plane->nformats,
- private_plane);
+ type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+ drm_universal_plane_init(dev, plane, 0xff, &mdp4_plane_funcs,
+ mdp4_plane->formats, mdp4_plane->nformats,
+ type);
mdp4_plane_install_properties(plane, &plane->base);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index f2794021f086..6ea10bdb6e8f 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -102,7 +102,7 @@ static void update_fb(struct drm_crtc *crtc, struct drm_framebuffer *new_fb)
/* grab reference to incoming scanout fb: */
drm_framebuffer_reference(new_fb);
- mdp5_crtc->base.fb = new_fb;
+ mdp5_crtc->base.primary->fb = new_fb;
mdp5_crtc->fb = new_fb;
if (old_fb)
@@ -289,14 +289,14 @@ static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
mode->type, mode->flags);
/* grab extra ref for update_scanout() */
- drm_framebuffer_reference(crtc->fb);
+ drm_framebuffer_reference(crtc->primary->fb);
- ret = mdp5_plane_mode_set(mdp5_crtc->plane, crtc, crtc->fb,
+ ret = mdp5_plane_mode_set(mdp5_crtc->plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x << 16, y << 16,
mode->hdisplay << 16, mode->vdisplay << 16);
if (ret) {
- drm_framebuffer_unreference(crtc->fb);
+ drm_framebuffer_unreference(crtc->primary->fb);
dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n",
mdp5_crtc->name, ret);
return ret;
@@ -306,8 +306,8 @@ static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) |
MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay));
- update_fb(crtc, crtc->fb);
- update_scanout(crtc, crtc->fb);
+ update_fb(crtc, crtc->primary->fb);
+ update_scanout(crtc, crtc->primary->fb);
return 0;
}
@@ -338,19 +338,19 @@ static int mdp5_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
int ret;
/* grab extra ref for update_scanout() */
- drm_framebuffer_reference(crtc->fb);
+ drm_framebuffer_reference(crtc->primary->fb);
- ret = mdp5_plane_mode_set(plane, crtc, crtc->fb,
+ ret = mdp5_plane_mode_set(plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x << 16, y << 16,
mode->hdisplay << 16, mode->vdisplay << 16);
if (ret) {
- drm_framebuffer_unreference(crtc->fb);
+ drm_framebuffer_unreference(crtc->primary->fb);
return ret;
}
- update_fb(crtc, crtc->fb);
- update_scanout(crtc, crtc->fb);
+ update_fb(crtc, crtc->primary->fb);
+ update_scanout(crtc, crtc->primary->fb);
return 0;
}
@@ -524,6 +524,9 @@ void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane)
{
+ /* don't actually detatch our primary plane: */
+ if (to_mdp5_crtc(crtc)->plane == plane)
+ return;
set_attach(crtc, mdp5_plane_pipe(plane), NULL);
}
@@ -559,7 +562,7 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
- drm_crtc_init(dev, crtc, &mdp5_crtc_funcs);
+ drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
mdp5_plane_install_properties(mdp5_crtc->plane, &crtc->base);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 0ac8bb5e7e85..47f7bbb9c15a 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -358,6 +358,7 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev,
struct drm_plane *plane = NULL;
struct mdp5_plane *mdp5_plane;
int ret;
+ enum drm_plane_type type;
mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL);
if (!mdp5_plane) {
@@ -373,9 +374,10 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev,
mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats,
ARRAY_SIZE(mdp5_plane->formats));
- drm_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
- mdp5_plane->formats, mdp5_plane->nformats,
- private_plane);
+ type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+ drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
+ mdp5_plane->formats, mdp5_plane->nformats,
+ type);
mdp5_plane_install_properties(plane, &plane->base);
diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.c b/drivers/gpu/drm/msm/mdp/mdp_kms.c
index 3be48f7c36be..03455b64a245 100644
--- a/drivers/gpu/drm/msm/mdp/mdp_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp_kms.c
@@ -101,7 +101,8 @@ void mdp_irq_wait(struct mdp_kms *mdp_kms, uint32_t irqmask)
.count = 1,
};
mdp_irq_register(mdp_kms, &wait.irq);
- wait_event(wait_event, (wait.count <= 0));
+ wait_event_timeout(wait_event, (wait.count <= 0),
+ msecs_to_jiffies(100));
mdp_irq_unregister(mdp_kms, &wait.irq);
}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index e6adafc7eff3..f9de156b9e65 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -56,6 +56,10 @@ static char *vram;
MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU");
module_param(vram, charp, 0);
+/*
+ * Util/helpers:
+ */
+
void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
const char *dbgname)
{
@@ -143,6 +147,8 @@ static int msm_unload(struct drm_device *dev)
priv->vram.paddr, &attrs);
}
+ component_unbind_all(dev->dev, dev);
+
dev->dev_private = NULL;
kfree(priv);
@@ -175,6 +181,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
struct msm_kms *kms;
int ret;
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(dev->dev, "failed to allocate private data\n");
@@ -226,6 +233,13 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
(uint32_t)(priv->vram.paddr + size));
}
+ platform_set_drvdata(pdev, dev);
+
+ /* Bind all our sub-components: */
+ ret = component_bind_all(dev->dev, dev);
+ if (ret)
+ return ret;
+
switch (get_mdp_ver(pdev)) {
case 4:
kms = mdp4_kms_init(dev);
@@ -281,8 +295,6 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
goto fail;
}
- platform_set_drvdata(pdev, dev);
-
#ifdef CONFIG_DRM_MSM_FBDEV
priv->fbdev = msm_fbdev_init(dev);
#endif
@@ -311,7 +323,6 @@ static void load_gpu(struct drm_device *dev)
gpu = NULL;
/* not fatal */
}
- mutex_unlock(&dev->struct_mutex);
if (gpu) {
int ret;
@@ -321,10 +332,16 @@ static void load_gpu(struct drm_device *dev)
dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
gpu->funcs->destroy(gpu);
gpu = NULL;
+ } else {
+ /* give inactive pm a chance to kick in: */
+ msm_gpu_retire(gpu);
}
+
}
priv->gpu = gpu;
+
+ mutex_unlock(&dev->struct_mutex);
}
static int msm_open(struct drm_device *dev, struct drm_file *file)
@@ -647,6 +664,12 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_msm_gem_new *args = data;
+
+ if (args->flags & ~MSM_BO_FLAGS) {
+ DRM_ERROR("invalid flags: %08x\n", args->flags);
+ return -EINVAL;
+ }
+
return msm_gem_new_handle(dev, file, args->size,
args->flags, &args->handle);
}
@@ -660,6 +683,11 @@ static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
struct drm_gem_object *obj;
int ret;
+ if (args->op & ~MSM_PREP_FLAGS) {
+ DRM_ERROR("invalid op: %08x\n", args->op);
+ return -EINVAL;
+ }
+
obj = drm_gem_object_lookup(dev, file, args->handle);
if (!obj)
return -ENOENT;
@@ -714,7 +742,14 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_msm_wait_fence *args = data;
- return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout));
+
+ if (args->pad) {
+ DRM_ERROR("invalid pad: %08x\n", args->pad);
+ return -EINVAL;
+ }
+
+ return msm_wait_fence_interruptable(dev, args->fence,
+ &TS(args->timeout));
}
static const struct drm_ioctl_desc msm_ioctls[] = {
@@ -819,18 +854,110 @@ static const struct dev_pm_ops msm_pm_ops = {
};
/*
+ * Componentized driver support:
+ */
+
+#ifdef CONFIG_OF
+/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx
+ * (or probably any other).. so probably some room for some helpers
+ */
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int msm_drm_add_components(struct device *master, struct master *m)
+{
+ struct device_node *np = master->of_node;
+ unsigned i;
+ int ret;
+
+ for (i = 0; ; i++) {
+ struct device_node *node;
+
+ node = of_parse_phandle(np, "connectors", i);
+ if (!node)
+ break;
+
+ ret = component_master_add_child(m, compare_of, node);
+ of_node_put(node);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+#else
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == data;
+}
+
+static int msm_drm_add_components(struct device *master, struct master *m)
+{
+ /* For non-DT case, it kinda sucks. We don't actually have a way
+ * to know whether or not we are waiting for certain devices (or if
+ * they are simply not present). But for non-DT we only need to
+ * care about apq8064/apq8060/etc (all mdp4/a3xx):
+ */
+ static const char *devnames[] = {
+ "hdmi_msm.0", "kgsl-3d0.0",
+ };
+ int i;
+
+ DBG("Adding components..");
+
+ for (i = 0; i < ARRAY_SIZE(devnames); i++) {
+ struct device *dev;
+ int ret;
+
+ dev = bus_find_device_by_name(&platform_bus_type,
+ NULL, devnames[i]);
+ if (!dev) {
+ dev_info(master, "still waiting for %s\n", devnames[i]);
+ return -EPROBE_DEFER;
+ }
+
+ ret = component_master_add_child(m, compare_dev, dev);
+ if (ret) {
+ DBG("could not add child: %d", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static int msm_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&msm_driver, to_platform_device(dev));
+}
+
+static void msm_drm_unbind(struct device *dev)
+{
+ drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
+}
+
+static const struct component_master_ops msm_drm_ops = {
+ .add_components = msm_drm_add_components,
+ .bind = msm_drm_bind,
+ .unbind = msm_drm_unbind,
+};
+
+/*
* Platform driver:
*/
static int msm_pdev_probe(struct platform_device *pdev)
{
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- return drm_platform_init(&msm_driver, pdev);
+ return component_master_add(&pdev->dev, &msm_drm_ops);
}
static int msm_pdev_remove(struct platform_device *pdev)
{
- drm_put_dev(platform_get_drvdata(pdev));
+ component_master_del(&pdev->dev, &msm_drm_ops);
return 0;
}
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 3d63269c5b29..9d10ee0b5aac 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -22,6 +22,7 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/module.h>
+#include <linux/component.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -69,6 +70,9 @@ struct msm_drm_private {
struct msm_kms *kms;
+ /* subordinate devices, if present: */
+ struct platform_device *hdmi_pdev, *gpu_pdev;
+
/* when we have more than one 'msm_gpu' these need to be an array: */
struct msm_gpu *gpu;
struct msm_file_private *lastctx;
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 5423e914e491..1f1f4cffdaed 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -23,7 +23,6 @@
* Cmdstream submission:
*/
-#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
#define BO_VALID 0x8000
#define BO_LOCKED 0x4000
@@ -77,7 +76,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
goto out_unlock;
}
- if (submit_bo.flags & BO_INVALID_FLAGS) {
+ if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) {
DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
ret = -EINVAL;
goto out_unlock;
@@ -369,6 +368,18 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
goto out;
}
+ /* validate input from userspace: */
+ switch (submit_cmd.type) {
+ case MSM_SUBMIT_CMD_BUF:
+ case MSM_SUBMIT_CMD_IB_TARGET_BUF:
+ case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
+ break;
+ default:
+ DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = submit_bo(submit, submit_cmd.submit_idx,
&msm_obj, &iova, NULL);
if (ret)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 0cfe3f426ee4..3e667ca1f2b9 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -154,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)
int msm_gpu_pm_resume(struct msm_gpu *gpu)
{
+ struct drm_device *dev = gpu->dev;
int ret;
- DBG("%s", gpu->name);
+ DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ if (gpu->active_cnt++ > 0)
+ return 0;
+
+ if (WARN_ON(gpu->active_cnt <= 0))
+ return -EINVAL;
ret = enable_pwrrail(gpu);
if (ret)
@@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)
int msm_gpu_pm_suspend(struct msm_gpu *gpu)
{
+ struct drm_device *dev = gpu->dev;
int ret;
- DBG("%s", gpu->name);
+ DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ if (--gpu->active_cnt > 0)
+ return 0;
+
+ if (WARN_ON(gpu->active_cnt < 0))
+ return -EINVAL;
ret = disable_axi(gpu);
if (ret)
@@ -195,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu)
}
/*
+ * Inactivity detection (for suspend):
+ */
+
+static void inactive_worker(struct work_struct *work)
+{
+ struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work);
+ struct drm_device *dev = gpu->dev;
+
+ if (gpu->inactive)
+ return;
+
+ DBG("%s: inactive!\n", gpu->name);
+ mutex_lock(&dev->struct_mutex);
+ if (!(msm_gpu_active(gpu) || gpu->inactive)) {
+ disable_axi(gpu);
+ disable_clk(gpu);
+ gpu->inactive = true;
+ }
+ mutex_unlock(&dev->struct_mutex);
+}
+
+static void inactive_handler(unsigned long data)
+{
+ struct msm_gpu *gpu = (struct msm_gpu *)data;
+ struct msm_drm_private *priv = gpu->dev->dev_private;
+
+ queue_work(priv->wq, &gpu->inactive_work);
+}
+
+/* cancel inactive timer and make sure we are awake: */
+static void inactive_cancel(struct msm_gpu *gpu)
+{
+ DBG("%s", gpu->name);
+ del_timer(&gpu->inactive_timer);
+ if (gpu->inactive) {
+ enable_clk(gpu);
+ enable_axi(gpu);
+ gpu->inactive = false;
+ }
+}
+
+static void inactive_start(struct msm_gpu *gpu)
+{
+ DBG("%s", gpu->name);
+ mod_timer(&gpu->inactive_timer,
+ round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES));
+}
+
+/*
* Hangcheck detection for locked gpu:
*/
@@ -206,7 +273,10 @@ static void recover_worker(struct work_struct *work)
dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
mutex_lock(&dev->struct_mutex);
- gpu->funcs->recover(gpu);
+ if (msm_gpu_active(gpu)) {
+ inactive_cancel(gpu);
+ gpu->funcs->recover(gpu);
+ }
mutex_unlock(&dev->struct_mutex);
msm_gpu_retire(gpu);
@@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work)
}
mutex_unlock(&dev->struct_mutex);
+
+ if (!msm_gpu_active(gpu))
+ inactive_start(gpu);
}
/* call from irq handler to schedule work to retire bo's */
@@ -302,6 +375,8 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
gpu->submitted_fence = submit->fence;
+ inactive_cancel(gpu);
+
ret = gpu->funcs->submit(gpu, submit, ctx);
priv->lastctx = ctx;
@@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
gpu->dev = drm;
gpu->funcs = funcs;
gpu->name = name;
+ gpu->inactive = true;
INIT_LIST_HEAD(&gpu->active_list);
INIT_WORK(&gpu->retire_work, retire_worker);
+ INIT_WORK(&gpu->inactive_work, inactive_worker);
INIT_WORK(&gpu->recover_work, recover_worker);
+ setup_timer(&gpu->inactive_timer, inactive_handler,
+ (unsigned long)gpu);
setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
(unsigned long)gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 458db8c64c28..fad27008922f 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -72,6 +72,10 @@ struct msm_gpu {
uint32_t submitted_fence;
+ /* is gpu powered/active? */
+ int active_cnt;
+ bool inactive;
+
/* worker for handling active-list retiring: */
struct work_struct retire_work;
@@ -91,7 +95,12 @@ struct msm_gpu {
uint32_t bsc;
#endif
- /* Hang Detction: */
+ /* Hang and Inactivity Detection:
+ */
+#define DRM_MSM_INACTIVE_PERIOD 66 /* in ms (roughly four frames) */
+#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD)
+ struct timer_list inactive_timer;
+ struct work_struct inactive_work;
#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
struct timer_list hangcheck_timer;
@@ -99,6 +108,11 @@ struct msm_gpu {
struct work_struct recover_work;
};
+static inline bool msm_gpu_active(struct msm_gpu *gpu)
+{
+ return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
+}
+
static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
{
msm_writel(data, gpu->mmio + (reg << 2));
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index d310c195bdfe..b7d216264775 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -48,6 +48,7 @@ nouveau-y += core/subdev/bios/therm.o
nouveau-y += core/subdev/bios/vmap.o
nouveau-y += core/subdev/bios/volt.o
nouveau-y += core/subdev/bios/xpio.o
+nouveau-y += core/subdev/bios/P0260.o
nouveau-y += core/subdev/bus/hwsq.o
nouveau-y += core/subdev/bus/nv04.o
nouveau-y += core/subdev/bus/nv31.o
@@ -77,6 +78,7 @@ nouveau-y += core/subdev/devinit/nv98.o
nouveau-y += core/subdev/devinit/nva3.o
nouveau-y += core/subdev/devinit/nvaf.o
nouveau-y += core/subdev/devinit/nvc0.o
+nouveau-y += core/subdev/devinit/gm107.o
nouveau-y += core/subdev/fb/base.o
nouveau-y += core/subdev/fb/nv04.o
nouveau-y += core/subdev/fb/nv10.o
@@ -100,6 +102,7 @@ nouveau-y += core/subdev/fb/nvaa.o
nouveau-y += core/subdev/fb/nvaf.o
nouveau-y += core/subdev/fb/nvc0.o
nouveau-y += core/subdev/fb/nve0.o
+nouveau-y += core/subdev/fb/gm107.o
nouveau-y += core/subdev/fb/ramnv04.o
nouveau-y += core/subdev/fb/ramnv10.o
nouveau-y += core/subdev/fb/ramnv1a.o
@@ -114,6 +117,7 @@ nouveau-y += core/subdev/fb/ramnva3.o
nouveau-y += core/subdev/fb/ramnvaa.o
nouveau-y += core/subdev/fb/ramnvc0.o
nouveau-y += core/subdev/fb/ramnve0.o
+nouveau-y += core/subdev/fb/ramgm107.o
nouveau-y += core/subdev/fb/sddr3.o
nouveau-y += core/subdev/fb/gddr5.o
nouveau-y += core/subdev/gpio/base.o
@@ -136,7 +140,8 @@ nouveau-y += core/subdev/instmem/base.o
nouveau-y += core/subdev/instmem/nv04.o
nouveau-y += core/subdev/instmem/nv40.o
nouveau-y += core/subdev/instmem/nv50.o
-nouveau-y += core/subdev/ltcg/nvc0.o
+nouveau-y += core/subdev/ltcg/gf100.o
+nouveau-y += core/subdev/ltcg/gm107.o
nouveau-y += core/subdev/mc/base.o
nouveau-y += core/subdev/mc/nv04.o
nouveau-y += core/subdev/mc/nv40.o
@@ -170,6 +175,7 @@ nouveau-y += core/subdev/therm/nva3.o
nouveau-y += core/subdev/therm/nvd0.o
nouveau-y += core/subdev/timer/base.o
nouveau-y += core/subdev/timer/nv04.o
+nouveau-y += core/subdev/timer/gk20a.o
nouveau-y += core/subdev/vm/base.o
nouveau-y += core/subdev/vm/nv04.o
nouveau-y += core/subdev/vm/nv41.o
@@ -206,6 +212,7 @@ nouveau-y += core/engine/device/nv40.o
nouveau-y += core/engine/device/nv50.o
nouveau-y += core/engine/device/nvc0.o
nouveau-y += core/engine/device/nve0.o
+nouveau-y += core/engine/device/gm100.o
nouveau-y += core/engine/disp/base.o
nouveau-y += core/engine/disp/nv04.o
nouveau-y += core/engine/disp/nv50.o
@@ -216,6 +223,7 @@ nouveau-y += core/engine/disp/nva3.o
nouveau-y += core/engine/disp/nvd0.o
nouveau-y += core/engine/disp/nve0.o
nouveau-y += core/engine/disp/nvf0.o
+nouveau-y += core/engine/disp/gm107.o
nouveau-y += core/engine/disp/dacnv50.o
nouveau-y += core/engine/disp/dport.o
nouveau-y += core/engine/disp/hdanva3.o
@@ -242,13 +250,14 @@ nouveau-y += core/engine/graph/ctxnv40.o
nouveau-y += core/engine/graph/ctxnv50.o
nouveau-y += core/engine/graph/ctxnvc0.o
nouveau-y += core/engine/graph/ctxnvc1.o
-nouveau-y += core/engine/graph/ctxnvc3.o
+nouveau-y += core/engine/graph/ctxnvc4.o
nouveau-y += core/engine/graph/ctxnvc8.o
nouveau-y += core/engine/graph/ctxnvd7.o
nouveau-y += core/engine/graph/ctxnvd9.o
nouveau-y += core/engine/graph/ctxnve4.o
nouveau-y += core/engine/graph/ctxnvf0.o
nouveau-y += core/engine/graph/ctxnv108.o
+nouveau-y += core/engine/graph/ctxgm107.o
nouveau-y += core/engine/graph/nv04.o
nouveau-y += core/engine/graph/nv10.o
nouveau-y += core/engine/graph/nv20.o
@@ -261,13 +270,14 @@ nouveau-y += core/engine/graph/nv40.o
nouveau-y += core/engine/graph/nv50.o
nouveau-y += core/engine/graph/nvc0.o
nouveau-y += core/engine/graph/nvc1.o
-nouveau-y += core/engine/graph/nvc3.o
+nouveau-y += core/engine/graph/nvc4.o
nouveau-y += core/engine/graph/nvc8.o
nouveau-y += core/engine/graph/nvd7.o
nouveau-y += core/engine/graph/nvd9.o
nouveau-y += core/engine/graph/nve4.o
nouveau-y += core/engine/graph/nvf0.o
nouveau-y += core/engine/graph/nv108.o
+nouveau-y += core/engine/graph/gm107.o
nouveau-y += core/engine/mpeg/nv31.o
nouveau-y += core/engine/mpeg/nv40.o
nouveau-y += core/engine/mpeg/nv44.o
diff --git a/drivers/gpu/drm/nouveau/core/core/namedb.c b/drivers/gpu/drm/nouveau/core/core/namedb.c
index 1ce95a8709df..0594a599f6fb 100644
--- a/drivers/gpu/drm/nouveau/core/core/namedb.c
+++ b/drivers/gpu/drm/nouveau/core/core/namedb.c
@@ -167,7 +167,7 @@ int
nouveau_namedb_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, u32 pclass,
- struct nouveau_oclass *sclass, u32 engcls,
+ struct nouveau_oclass *sclass, u64 engcls,
int length, void **pobject)
{
struct nouveau_namedb *namedb;
diff --git a/drivers/gpu/drm/nouveau/core/core/parent.c b/drivers/gpu/drm/nouveau/core/core/parent.c
index 313380ce632d..dee5d1235e9b 100644
--- a/drivers/gpu/drm/nouveau/core/core/parent.c
+++ b/drivers/gpu/drm/nouveau/core/core/parent.c
@@ -49,7 +49,7 @@ nouveau_parent_sclass(struct nouveau_object *parent, u16 handle,
mask = nv_parent(parent)->engine;
while (mask) {
- int i = ffsll(mask) - 1;
+ int i = __ffs64(mask);
if (nv_iclass(parent, NV_CLIENT_CLASS))
engine = nv_engine(nv_client(parent)->device);
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/base.c b/drivers/gpu/drm/nouveau/core/engine/device/base.c
index dd01c6c435d6..18c8c7245b73 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/base.c
@@ -131,8 +131,8 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
if (ret)
return ret;
- mmio_base = pci_resource_start(device->pdev, 0);
- mmio_size = pci_resource_len(device->pdev, 0);
+ mmio_base = nv_device_resource_start(device, 0);
+ mmio_size = nv_device_resource_len(device, 0);
/* translate api disable mask into internal mapping */
disable = args->debug0;
@@ -185,6 +185,7 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
case 0x0e0:
case 0x0f0:
case 0x100: device->card_type = NV_E0; break;
+ case 0x110: device->card_type = GM100; break;
default:
break;
}
@@ -208,6 +209,7 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
case NV_C0:
case NV_D0: ret = nvc0_identify(device); break;
case NV_E0: ret = nve0_identify(device); break;
+ case GM100: ret = gm100_identify(device); break;
default:
ret = -EINVAL;
break;
@@ -446,6 +448,72 @@ nouveau_device_dtor(struct nouveau_object *object)
nouveau_engine_destroy(&device->base);
}
+resource_size_t
+nv_device_resource_start(struct nouveau_device *device, unsigned int bar)
+{
+ if (nv_device_is_pci(device)) {
+ return pci_resource_start(device->pdev, bar);
+ } else {
+ struct resource *res;
+ res = platform_get_resource(device->platformdev,
+ IORESOURCE_MEM, bar);
+ if (!res)
+ return 0;
+ return res->start;
+ }
+}
+
+resource_size_t
+nv_device_resource_len(struct nouveau_device *device, unsigned int bar)
+{
+ if (nv_device_is_pci(device)) {
+ return pci_resource_len(device->pdev, bar);
+ } else {
+ struct resource *res;
+ res = platform_get_resource(device->platformdev,
+ IORESOURCE_MEM, bar);
+ if (!res)
+ return 0;
+ return resource_size(res);
+ }
+}
+
+dma_addr_t
+nv_device_map_page(struct nouveau_device *device, struct page *page)
+{
+ dma_addr_t ret;
+
+ if (nv_device_is_pci(device)) {
+ ret = pci_map_page(device->pdev, page, 0, PAGE_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(device->pdev, ret))
+ ret = 0;
+ } else {
+ ret = page_to_phys(page);
+ }
+
+ return ret;
+}
+
+void
+nv_device_unmap_page(struct nouveau_device *device, dma_addr_t addr)
+{
+ if (nv_device_is_pci(device))
+ pci_unmap_page(device->pdev, addr, PAGE_SIZE,
+ PCI_DMA_BIDIRECTIONAL);
+}
+
+int
+nv_device_get_irq(struct nouveau_device *device, bool stall)
+{
+ if (nv_device_is_pci(device)) {
+ return device->pdev->irq;
+ } else {
+ return platform_get_irq_byname(device->platformdev,
+ stall ? "stall" : "nonstall");
+ }
+}
+
static struct nouveau_oclass
nouveau_device_oclass = {
.handle = NV_ENGINE(DEVICE, 0x00),
@@ -457,8 +525,8 @@ nouveau_device_oclass = {
};
int
-nouveau_device_create_(struct pci_dev *pdev, u64 name, const char *sname,
- const char *cfg, const char *dbg,
+nouveau_device_create_(void *dev, enum nv_bus_type type, u64 name,
+ const char *sname, const char *cfg, const char *dbg,
int length, void **pobject)
{
struct nouveau_device *device;
@@ -476,7 +544,14 @@ nouveau_device_create_(struct pci_dev *pdev, u64 name, const char *sname,
if (ret)
goto done;
- device->pdev = pdev;
+ switch (type) {
+ case NOUVEAU_BUS_PCI:
+ device->pdev = dev;
+ break;
+ case NOUVEAU_BUS_PLATFORM:
+ device->platformdev = dev;
+ break;
+ }
device->handle = name;
device->cfgopt = cfg;
device->dbgopt = dbg;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
new file mode 100644
index 000000000000..d258c21c4a22
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bus.h>
+#include <subdev/gpio.h>
+#include <subdev/i2c.h>
+#include <subdev/clock.h>
+#include <subdev/therm.h>
+#include <subdev/mxm.h>
+#include <subdev/devinit.h>
+#include <subdev/mc.h>
+#include <subdev/timer.h>
+#include <subdev/fb.h>
+#include <subdev/ltcg.h>
+#include <subdev/ibus.h>
+#include <subdev/instmem.h>
+#include <subdev/vm.h>
+#include <subdev/bar.h>
+#include <subdev/pwr.h>
+#include <subdev/volt.h>
+
+#include <engine/device.h>
+#include <engine/dmaobj.h>
+#include <engine/fifo.h>
+#include <engine/software.h>
+#include <engine/graph.h>
+#include <engine/disp.h>
+#include <engine/copy.h>
+#include <engine/bsp.h>
+#include <engine/vp.h>
+#include <engine/ppp.h>
+#include <engine/perfmon.h>
+
+int
+gm100_identify(struct nouveau_device *device)
+{
+ switch (device->chipset) {
+ case 0x117:
+ device->cname = "GM107";
+ device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
+#if 0
+ device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
+#endif
+ device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
+ device->oclass[NVDEV_SUBDEV_DEVINIT] = gm107_devinit_oclass;
+ device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass;
+ device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
+ device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass;
+ device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gm107_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
+ device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
+ device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
+ device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
+#if 0
+ device->oclass[NVDEV_SUBDEV_PWR ] = &nv108_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
+#endif
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_FIFO ] = nv108_fifo_oclass;
+ device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = gm107_graph_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = gm107_disp_oclass;
+ device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
+#if 0
+ device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
+#endif
+ device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
+#if 0
+ device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass;
+ device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass;
+ device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
+#endif
+ break;
+ default:
+ nv_fatal(device, "unknown Maxwell chipset\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
index 32113b08c4d5..0a51ff4e9e00 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
@@ -60,7 +60,7 @@ nv04_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv04_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv04_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv04_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x05:
device->cname = "NV05";
@@ -78,7 +78,7 @@ nv04_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv04_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv04_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv04_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
default:
nv_fatal(device, "unknown RIVA chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
index 744f15d7e131..e008de8b51b0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
@@ -60,7 +60,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x15:
device->cname = "NV15";
@@ -79,7 +79,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x16:
device->cname = "NV16";
@@ -98,7 +98,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x1a:
device->cname = "nForce";
@@ -117,7 +117,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x11:
device->cname = "NV11";
@@ -136,7 +136,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x17:
device->cname = "NV17";
@@ -155,7 +155,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x1f:
device->cname = "nForce2";
@@ -174,7 +174,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x18:
device->cname = "NV18";
@@ -193,7 +193,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
default:
nv_fatal(device, "unknown Celsius chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
index 27ba61fb2710..7b629a3aed05 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
@@ -63,7 +63,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv20_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x25:
device->cname = "NV25";
@@ -82,7 +82,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv25_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x28:
device->cname = "NV28";
@@ -101,7 +101,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv25_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x2a:
device->cname = "NV2A";
@@ -120,7 +120,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv2a_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
default:
nv_fatal(device, "unknown Kelvin chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
index fd47ace67543..7dfddd5a1908 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
@@ -63,7 +63,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv30_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x35:
device->cname = "NV35";
@@ -82,7 +82,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv35_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x31:
device->cname = "NV31";
@@ -102,7 +102,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv30_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv31_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x36:
device->cname = "NV36";
@@ -122,7 +122,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv35_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv31_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
case 0x34:
device->cname = "NV34";
@@ -142,7 +142,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv34_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv31_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
default:
nv_fatal(device, "unknown Rankine chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
index 08b88591ed60..7c1ce6cf4f1f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
@@ -70,7 +70,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x41:
@@ -93,7 +93,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x42:
@@ -116,7 +116,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x43:
@@ -139,7 +139,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv40_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x45:
@@ -162,7 +162,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x47:
@@ -185,7 +185,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x49:
@@ -208,7 +208,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x4b:
@@ -231,7 +231,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x44:
@@ -254,7 +254,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x46:
@@ -277,7 +277,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x4a:
@@ -300,7 +300,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x4c:
@@ -323,7 +323,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x4e:
@@ -346,7 +346,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x63:
@@ -369,7 +369,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x67:
@@ -392,7 +392,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
case 0x68:
@@ -415,7 +415,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv44_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv04_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv40_perfmon_oclass;
break;
default:
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
index 81d5c26643d5..66499fa0f758 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
@@ -79,7 +79,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
device->oclass[NVDEV_ENGINE_MPEG ] = &nv50_mpeg_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv50_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv50_perfmon_oclass;
break;
case 0x84:
@@ -107,7 +107,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass;
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv84_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv84_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0x86:
@@ -135,7 +135,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass;
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv84_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv84_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0x92:
@@ -163,7 +163,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass;
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv84_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv84_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0x94:
@@ -191,7 +191,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass;
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv94_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0x96:
@@ -219,7 +219,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass;
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv94_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0x98:
@@ -247,7 +247,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv94_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0xa0:
@@ -275,7 +275,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass;
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva0_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0xaa:
@@ -303,7 +303,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv94_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0xac:
@@ -331,7 +331,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nv94_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nv84_perfmon_oclass;
break;
case 0xa3:
@@ -361,7 +361,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nva3_perfmon_oclass;
break;
case 0xa5:
@@ -390,7 +390,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nva3_perfmon_oclass;
break;
case 0xa8:
@@ -419,7 +419,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nva3_perfmon_oclass;
break;
case 0xaf:
@@ -448,7 +448,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = nva3_perfmon_oclass;
break;
default:
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
index b7d66b59f43d..2075b3027052 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
@@ -70,7 +70,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -86,7 +86,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xc4:
@@ -102,7 +102,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -112,13 +112,13 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
- device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xc3:
@@ -134,7 +134,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -144,12 +144,12 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
- device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xce:
@@ -165,7 +165,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -175,13 +175,13 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
- device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xcf:
@@ -197,7 +197,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -207,13 +207,13 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
- device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass;
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xc1:
@@ -229,7 +229,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -244,7 +244,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xc8:
@@ -260,7 +260,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -276,7 +276,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xd9:
@@ -292,7 +292,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -307,7 +307,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nvd0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nvd0_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
case 0xd7:
@@ -323,7 +323,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -336,7 +336,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nvd0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nvd0_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
default:
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
index 987edbc30a09..9784cbf8a9d2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
@@ -70,7 +70,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -81,7 +81,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nve0_disp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
@@ -103,7 +103,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -114,7 +114,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nve0_disp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
@@ -136,7 +136,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -147,7 +147,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nve0_disp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
@@ -169,7 +169,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -180,7 +180,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nvf0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nvf0_disp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
@@ -204,7 +204,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = &nvc0_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
@@ -215,7 +215,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_FIFO ] = nv108_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nv108_graph_oclass;
- device->oclass[NVDEV_ENGINE_DISP ] = &nvf0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_DISP ] = nvf0_disp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
index 1bd4c63369c1..3ca2d25b7f5e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
@@ -273,7 +273,7 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
.outp = outp,
.head = head,
}, *dp = &_dp;
- const u32 bw_list[] = { 270000, 162000, 0 };
+ const u32 bw_list[] = { 540000, 270000, 162000, 0 };
const u32 *link_bw = bw_list;
u8 hdr, cnt, len;
u32 data;
@@ -312,6 +312,14 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
ERR("failed to read DPCD\n");
}
+ /* bring capabilities within encoder limits */
+ if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) {
+ dp->dpcd[2] &= ~0x1f;
+ dp->dpcd[2] |= dp->outp->dpconf.link_nr;
+ }
+ if (dp->dpcd[1] > dp->outp->dpconf.link_bw)
+ dp->dpcd[1] = dp->outp->dpconf.link_bw;
+
/* adjust required bandwidth for 8B/10B coding overhead */
datarate = (datarate / 8) * 10;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
new file mode 100644
index 000000000000..cf6f59677b74
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <engine/software.h>
+#include <engine/disp.h>
+
+#include <core/class.h>
+
+#include "nv50.h"
+
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
+static struct nouveau_oclass
+gm107_disp_sclass[] = {
+ { GM107_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
+ { GM107_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
+ { GM107_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
+ { GM107_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
+ { GM107_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
+ {}
+};
+
+static struct nouveau_oclass
+gm107_disp_base_oclass[] = {
+ { GM107_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
+ {}
+};
+
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
+static int
+gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nv50_disp_priv *priv;
+ int heads = nv_rd32(parent, 0x022448);
+ int ret;
+
+ ret = nouveau_disp_create(parent, engine, oclass, heads,
+ "PDISP", "display", &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ nv_engine(priv)->sclass = gm107_disp_base_oclass;
+ nv_engine(priv)->cclass = &nv50_disp_cclass;
+ nv_subdev(priv)->intr = nvd0_disp_intr;
+ INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
+ priv->sclass = gm107_disp_sclass;
+ priv->head.nr = heads;
+ priv->dac.nr = 3;
+ priv->sor.nr = 4;
+ priv->dac.power = nv50_dac_power;
+ priv->dac.sense = nv50_dac_sense;
+ priv->sor.power = nv50_sor_power;
+ priv->sor.hda_eld = nvd0_hda_eld;
+ priv->sor.hdmi = nvd0_hdmi_ctrl;
+ priv->sor.dp = &nvd0_sor_dp_func;
+ return 0;
+}
+
+struct nouveau_oclass *
+gm107_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x07),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gm107_disp_ctor,
+ .dtor = _nouveau_disp_dtor,
+ .init = _nouveau_disp_init,
+ .fini = _nouveau_disp_fini,
+ },
+ .mthd.core = &nve0_disp_mast_mthd_chan,
+ .mthd.base = &nvd0_disp_sync_mthd_chan,
+ .mthd.ovly = &nve0_disp_ovly_mthd_chan,
+ .mthd.prev = -0x020000,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
index 7cf8b1348632..6c89af792889 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
@@ -22,7 +22,7 @@
* Authors: Ben Skeggs
*/
-#include <engine/disp.h>
+#include "priv.h"
#include <core/event.h>
#include <core/class.h>
@@ -138,13 +138,13 @@ nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nv04_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x04),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv04_disp_oclass = &(struct nouveau_disp_impl) {
+ .base.handle = NV_ENGINE(DISP, 0x04),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv04_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index 9ad722e4e087..9a0cab9c3adb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -26,8 +26,7 @@
#include <core/parent.h>
#include <core/handle.h>
#include <core/class.h>
-
-#include <engine/disp.h>
+#include <core/enum.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -227,6 +226,177 @@ nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend)
* EVO master channel object
******************************************************************************/
+static void
+nv50_disp_mthd_list(struct nv50_disp_priv *priv, int debug, u32 base, int c,
+ const struct nv50_disp_mthd_list *list, int inst)
+{
+ struct nouveau_object *disp = nv_object(priv);
+ int i;
+
+ for (i = 0; list->data[i].mthd; i++) {
+ if (list->data[i].addr) {
+ u32 next = nv_rd32(priv, list->data[i].addr + base + 0);
+ u32 prev = nv_rd32(priv, list->data[i].addr + base + c);
+ u32 mthd = list->data[i].mthd + (list->mthd * inst);
+ const char *name = list->data[i].name;
+ char mods[16];
+
+ if (prev != next)
+ snprintf(mods, sizeof(mods), "-> 0x%08x", next);
+ else
+ snprintf(mods, sizeof(mods), "%13c", ' ');
+
+ nv_printk_(disp, debug, "\t0x%04x: 0x%08x %s%s%s\n",
+ mthd, prev, mods, name ? " // " : "",
+ name ? name : "");
+ }
+ }
+}
+
+void
+nv50_disp_mthd_chan(struct nv50_disp_priv *priv, int debug, int head,
+ const struct nv50_disp_mthd_chan *chan)
+{
+ struct nouveau_object *disp = nv_object(priv);
+ const struct nv50_disp_impl *impl = (void *)disp->oclass;
+ const struct nv50_disp_mthd_list *list;
+ int i, j;
+
+ if (debug > nv_subdev(priv)->debug)
+ return;
+
+ for (i = 0; (list = chan->data[i].mthd) != NULL; i++) {
+ u32 base = head * chan->addr;
+ for (j = 0; j < chan->data[i].nr; j++, base += list->addr) {
+ const char *cname = chan->name;
+ const char *sname = "";
+ char cname_[16], sname_[16];
+
+ if (chan->addr) {
+ snprintf(cname_, sizeof(cname_), "%s %d",
+ chan->name, head);
+ cname = cname_;
+ }
+
+ if (chan->data[i].nr > 1) {
+ snprintf(sname_, sizeof(sname_), " - %s %d",
+ chan->data[i].name, j);
+ sname = sname_;
+ }
+
+ nv_printk_(disp, debug, "%s%s:\n", cname, sname);
+ nv50_disp_mthd_list(priv, debug, base, impl->mthd.prev,
+ list, j);
+ }
+ }
+}
+
+const struct nv50_disp_mthd_list
+nv50_disp_mast_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x000000 },
+ { 0x0084, 0x610bb8 },
+ { 0x0088, 0x610b9c },
+ { 0x008c, 0x000000 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_list
+nv50_disp_mast_mthd_dac = {
+ .mthd = 0x0080,
+ .addr = 0x000008,
+ .data = {
+ { 0x0400, 0x610b58 },
+ { 0x0404, 0x610bdc },
+ { 0x0420, 0x610828 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nv50_disp_mast_mthd_sor = {
+ .mthd = 0x0040,
+ .addr = 0x000008,
+ .data = {
+ { 0x0600, 0x610b70 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nv50_disp_mast_mthd_pior = {
+ .mthd = 0x0040,
+ .addr = 0x000008,
+ .data = {
+ { 0x0700, 0x610b80 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_list
+nv50_disp_mast_mthd_head = {
+ .mthd = 0x0400,
+ .addr = 0x000540,
+ .data = {
+ { 0x0800, 0x610ad8 },
+ { 0x0804, 0x610ad0 },
+ { 0x0808, 0x610a48 },
+ { 0x080c, 0x610a78 },
+ { 0x0810, 0x610ac0 },
+ { 0x0814, 0x610af8 },
+ { 0x0818, 0x610b00 },
+ { 0x081c, 0x610ae8 },
+ { 0x0820, 0x610af0 },
+ { 0x0824, 0x610b08 },
+ { 0x0828, 0x610b10 },
+ { 0x082c, 0x610a68 },
+ { 0x0830, 0x610a60 },
+ { 0x0834, 0x000000 },
+ { 0x0838, 0x610a40 },
+ { 0x0840, 0x610a24 },
+ { 0x0844, 0x610a2c },
+ { 0x0848, 0x610aa8 },
+ { 0x084c, 0x610ab0 },
+ { 0x0860, 0x610a84 },
+ { 0x0864, 0x610a90 },
+ { 0x0868, 0x610b18 },
+ { 0x086c, 0x610b20 },
+ { 0x0870, 0x610ac8 },
+ { 0x0874, 0x610a38 },
+ { 0x0880, 0x610a58 },
+ { 0x0884, 0x610a9c },
+ { 0x08a0, 0x610a70 },
+ { 0x08a4, 0x610a50 },
+ { 0x08a8, 0x610ae0 },
+ { 0x08c0, 0x610b28 },
+ { 0x08c4, 0x610b30 },
+ { 0x08c8, 0x610b40 },
+ { 0x08d4, 0x610b38 },
+ { 0x08d8, 0x610b48 },
+ { 0x08dc, 0x610b50 },
+ { 0x0900, 0x610a18 },
+ { 0x0904, 0x610ab8 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_chan
+nv50_disp_mast_mthd_chan = {
+ .name = "Core",
+ .addr = 0x000000,
+ .data = {
+ { "Global", 1, &nv50_disp_mast_mthd_base },
+ { "DAC", 3, &nv50_disp_mast_mthd_dac },
+ { "SOR", 2, &nv50_disp_mast_mthd_sor },
+ { "PIOR", 3, &nv50_disp_mast_mthd_pior },
+ { "HEAD", 2, &nv50_disp_mast_mthd_head },
+ {}
+ }
+};
+
static int
nv50_disp_mast_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -323,6 +493,56 @@ nv50_disp_mast_ofuncs = {
* EVO sync channel objects
******************************************************************************/
+static const struct nv50_disp_mthd_list
+nv50_disp_sync_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x000000 },
+ { 0x0084, 0x0008c4 },
+ { 0x0088, 0x0008d0 },
+ { 0x008c, 0x0008dc },
+ { 0x0090, 0x0008e4 },
+ { 0x0094, 0x610884 },
+ { 0x00a0, 0x6108a0 },
+ { 0x00a4, 0x610878 },
+ { 0x00c0, 0x61086c },
+ { 0x00e0, 0x610858 },
+ { 0x00e4, 0x610860 },
+ { 0x00e8, 0x6108ac },
+ { 0x00ec, 0x6108b4 },
+ { 0x0100, 0x610894 },
+ { 0x0110, 0x6108bc },
+ { 0x0114, 0x61088c },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nv50_disp_sync_mthd_image = {
+ .mthd = 0x0400,
+ .addr = 0x000000,
+ .data = {
+ { 0x0800, 0x6108f0 },
+ { 0x0804, 0x6108fc },
+ { 0x0808, 0x61090c },
+ { 0x080c, 0x610914 },
+ { 0x0810, 0x610904 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_chan
+nv50_disp_sync_mthd_chan = {
+ .name = "Base",
+ .addr = 0x000540,
+ .data = {
+ { "Global", 1, &nv50_disp_sync_mthd_base },
+ { "Image", 2, &nv50_disp_sync_mthd_image },
+ {}
+ }
+};
+
static int
nv50_disp_sync_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -362,6 +582,44 @@ nv50_disp_sync_ofuncs = {
* EVO overlay channel objects
******************************************************************************/
+const struct nv50_disp_mthd_list
+nv50_disp_ovly_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x000000 },
+ { 0x0084, 0x0009a0 },
+ { 0x0088, 0x0009c0 },
+ { 0x008c, 0x0009c8 },
+ { 0x0090, 0x6109b4 },
+ { 0x0094, 0x610970 },
+ { 0x00a0, 0x610998 },
+ { 0x00a4, 0x610964 },
+ { 0x00c0, 0x610958 },
+ { 0x00e0, 0x6109a8 },
+ { 0x00e4, 0x6109d0 },
+ { 0x00e8, 0x6109d8 },
+ { 0x0100, 0x61094c },
+ { 0x0104, 0x610984 },
+ { 0x0108, 0x61098c },
+ { 0x0800, 0x6109f8 },
+ { 0x0808, 0x610a08 },
+ { 0x080c, 0x610a10 },
+ { 0x0810, 0x610a00 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_chan
+nv50_disp_ovly_mthd_chan = {
+ .name = "Overlay",
+ .addr = 0x000540,
+ .data = {
+ { "Global", 1, &nv50_disp_ovly_mthd_base },
+ {}
+ }
+};
+
static int
nv50_disp_ovly_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -782,25 +1040,78 @@ nv50_disp_cclass = {
* Display engine implementation
******************************************************************************/
-static void
-nv50_disp_intr_error(struct nv50_disp_priv *priv)
-{
- u32 channels = (nv_rd32(priv, 0x610020) & 0x001f0000) >> 16;
- u32 addr, data;
- int chid;
-
- for (chid = 0; chid < 5; chid++) {
- if (!(channels & (1 << chid)))
- continue;
+static const struct nouveau_enum
+nv50_disp_intr_error_type[] = {
+ { 3, "ILLEGAL_MTHD" },
+ { 4, "INVALID_VALUE" },
+ { 5, "INVALID_STATE" },
+ { 7, "INVALID_HANDLE" },
+ {}
+};
- nv_wr32(priv, 0x610020, 0x00010000 << chid);
- addr = nv_rd32(priv, 0x610080 + (chid * 0x08));
- data = nv_rd32(priv, 0x610084 + (chid * 0x08));
- nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000);
+static const struct nouveau_enum
+nv50_disp_intr_error_code[] = {
+ { 0x00, "" },
+ {}
+};
- nv_error(priv, "chid %d mthd 0x%04x data 0x%08x 0x%08x\n",
- chid, addr & 0xffc, data, addr);
+static void
+nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid)
+{
+ struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass;
+ u32 data = nv_rd32(priv, 0x610084 + (chid * 0x08));
+ u32 addr = nv_rd32(priv, 0x610080 + (chid * 0x08));
+ u32 code = (addr & 0x00ff0000) >> 16;
+ u32 type = (addr & 0x00007000) >> 12;
+ u32 mthd = (addr & 0x00000ffc);
+ const struct nouveau_enum *ec, *et;
+ char ecunk[6], etunk[6];
+
+ et = nouveau_enum_find(nv50_disp_intr_error_type, type);
+ if (!et)
+ snprintf(etunk, sizeof(etunk), "UNK%02X", type);
+
+ ec = nouveau_enum_find(nv50_disp_intr_error_code, code);
+ if (!ec)
+ snprintf(ecunk, sizeof(ecunk), "UNK%02X", code);
+
+ nv_error(priv, "%s [%s] chid %d mthd 0x%04x data 0x%08x\n",
+ et ? et->name : etunk, ec ? ec->name : ecunk,
+ chid, mthd, data);
+
+ if (chid == 0) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0,
+ impl->mthd.core);
+ break;
+ default:
+ break;
+ }
+ } else
+ if (chid <= 2) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1,
+ impl->mthd.base);
+ break;
+ default:
+ break;
+ }
+ } else
+ if (chid <= 4) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 3,
+ impl->mthd.ovly);
+ break;
+ default:
+ break;
+ }
}
+
+ nv_wr32(priv, 0x610020, 0x00010000 << chid);
+ nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000);
}
static u16
@@ -1241,12 +1552,14 @@ nv50_disp_intr_supervisor(struct work_struct *work)
{
struct nv50_disp_priv *priv =
container_of(work, struct nv50_disp_priv, supervisor);
+ struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass;
u32 super = nv_rd32(priv, 0x610030);
int head;
nv_debug(priv, "supervisor 0x%08x 0x%08x\n", priv->super, super);
if (priv->super & 0x00000010) {
+ nv50_disp_mthd_chan(priv, NV_DBG_DEBUG, 0, impl->mthd.core);
for (head = 0; head < priv->head.nr; head++) {
if (!(super & (0x00000020 << head)))
continue;
@@ -1290,9 +1603,10 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
u32 intr0 = nv_rd32(priv, 0x610020);
u32 intr1 = nv_rd32(priv, 0x610024);
- if (intr0 & 0x001f0000) {
- nv50_disp_intr_error(priv);
- intr0 &= ~0x001f0000;
+ while (intr0 & 0x001f0000) {
+ u32 chid = __ffs(intr0 & 0x001f0000) - 16;
+ nv50_disp_intr_error(priv, chid);
+ intr0 &= ~(0x00010000 << chid);
}
if (intr1 & 0x00000004) {
@@ -1346,13 +1660,17 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nv50_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x50),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv50_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x50),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv50_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nv50_disp_mast_mthd_chan,
+ .mthd.base = &nv50_disp_sync_mthd_chan,
+ .mthd.ovly = &nv50_disp_ovly_mthd_chan,
+ .mthd.prev = 0x000004,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
index d31d426ea1f6..48d59db47f0d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
@@ -8,9 +8,19 @@
#include <core/event.h>
#include <engine/dmaobj.h>
-#include <engine/disp.h>
#include "dport.h"
+#include "priv.h"
+
+struct nv50_disp_impl {
+ struct nouveau_disp_impl base;
+ struct {
+ const struct nv50_disp_mthd_chan *core;
+ const struct nv50_disp_mthd_chan *base;
+ const struct nv50_disp_mthd_chan *ovly;
+ int prev;
+ } mthd;
+};
struct nv50_disp_priv {
struct nouveau_disp base;
@@ -124,21 +134,60 @@ struct nv50_disp_pioc {
struct nv50_disp_chan base;
};
+struct nv50_disp_mthd_list {
+ u32 mthd;
+ u32 addr;
+ struct {
+ u32 mthd;
+ u32 addr;
+ const char *name;
+ } data[];
+};
+
+struct nv50_disp_mthd_chan {
+ const char *name;
+ u32 addr;
+ struct {
+ const char *name;
+ int nr;
+ const struct nv50_disp_mthd_list *mthd;
+ } data[];
+};
+
extern struct nouveau_ofuncs nv50_disp_mast_ofuncs;
+extern const struct nv50_disp_mthd_list nv50_disp_mast_mthd_base;
+extern const struct nv50_disp_mthd_list nv50_disp_mast_mthd_sor;
+extern const struct nv50_disp_mthd_list nv50_disp_mast_mthd_pior;
extern struct nouveau_ofuncs nv50_disp_sync_ofuncs;
+extern const struct nv50_disp_mthd_list nv50_disp_sync_mthd_image;
extern struct nouveau_ofuncs nv50_disp_ovly_ofuncs;
+extern const struct nv50_disp_mthd_list nv50_disp_ovly_mthd_base;
extern struct nouveau_ofuncs nv50_disp_oimm_ofuncs;
extern struct nouveau_ofuncs nv50_disp_curs_ofuncs;
extern struct nouveau_ofuncs nv50_disp_base_ofuncs;
extern struct nouveau_oclass nv50_disp_cclass;
+void nv50_disp_mthd_chan(struct nv50_disp_priv *, int debug, int head,
+ const struct nv50_disp_mthd_chan *);
void nv50_disp_intr_supervisor(struct work_struct *);
void nv50_disp_intr(struct nouveau_subdev *);
+extern const struct nv50_disp_mthd_chan nv84_disp_mast_mthd_chan;
+extern const struct nv50_disp_mthd_list nv84_disp_mast_mthd_dac;
+extern const struct nv50_disp_mthd_list nv84_disp_mast_mthd_head;
+extern const struct nv50_disp_mthd_chan nv84_disp_sync_mthd_chan;
+extern const struct nv50_disp_mthd_chan nv84_disp_ovly_mthd_chan;
extern struct nouveau_omthds nv84_disp_base_omthds[];
+extern const struct nv50_disp_mthd_chan nv94_disp_mast_mthd_chan;
+
extern struct nouveau_ofuncs nvd0_disp_mast_ofuncs;
+extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_base;
+extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_dac;
+extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_sor;
+extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_pior;
extern struct nouveau_ofuncs nvd0_disp_sync_ofuncs;
extern struct nouveau_ofuncs nvd0_disp_ovly_ofuncs;
+extern const struct nv50_disp_mthd_chan nvd0_disp_sync_mthd_chan;
extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs;
extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs;
extern struct nouveau_omthds nvd0_disp_base_omthds[];
@@ -147,4 +196,7 @@ extern struct nouveau_oclass nvd0_disp_cclass;
void nvd0_disp_intr_supervisor(struct work_struct *);
void nvd0_disp_intr(struct nouveau_subdev *);
+extern const struct nv50_disp_mthd_chan nve0_disp_mast_mthd_chan;
+extern const struct nv50_disp_mthd_chan nve0_disp_ovly_mthd_chan;
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
index ef9ce300a496..98c5b19bc2b0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
@@ -29,6 +29,179 @@
#include "nv50.h"
+/*******************************************************************************
+ * EVO master channel object
+ ******************************************************************************/
+
+const struct nv50_disp_mthd_list
+nv84_disp_mast_mthd_dac = {
+ .mthd = 0x0080,
+ .addr = 0x000008,
+ .data = {
+ { 0x0400, 0x610b58 },
+ { 0x0404, 0x610bdc },
+ { 0x0420, 0x610bc4 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nv84_disp_mast_mthd_head = {
+ .mthd = 0x0400,
+ .addr = 0x000540,
+ .data = {
+ { 0x0800, 0x610ad8 },
+ { 0x0804, 0x610ad0 },
+ { 0x0808, 0x610a48 },
+ { 0x080c, 0x610a78 },
+ { 0x0810, 0x610ac0 },
+ { 0x0814, 0x610af8 },
+ { 0x0818, 0x610b00 },
+ { 0x081c, 0x610ae8 },
+ { 0x0820, 0x610af0 },
+ { 0x0824, 0x610b08 },
+ { 0x0828, 0x610b10 },
+ { 0x082c, 0x610a68 },
+ { 0x0830, 0x610a60 },
+ { 0x0834, 0x000000 },
+ { 0x0838, 0x610a40 },
+ { 0x0840, 0x610a24 },
+ { 0x0844, 0x610a2c },
+ { 0x0848, 0x610aa8 },
+ { 0x084c, 0x610ab0 },
+ { 0x085c, 0x610c5c },
+ { 0x0860, 0x610a84 },
+ { 0x0864, 0x610a90 },
+ { 0x0868, 0x610b18 },
+ { 0x086c, 0x610b20 },
+ { 0x0870, 0x610ac8 },
+ { 0x0874, 0x610a38 },
+ { 0x0878, 0x610c50 },
+ { 0x0880, 0x610a58 },
+ { 0x0884, 0x610a9c },
+ { 0x089c, 0x610c68 },
+ { 0x08a0, 0x610a70 },
+ { 0x08a4, 0x610a50 },
+ { 0x08a8, 0x610ae0 },
+ { 0x08c0, 0x610b28 },
+ { 0x08c4, 0x610b30 },
+ { 0x08c8, 0x610b40 },
+ { 0x08d4, 0x610b38 },
+ { 0x08d8, 0x610b48 },
+ { 0x08dc, 0x610b50 },
+ { 0x0900, 0x610a18 },
+ { 0x0904, 0x610ab8 },
+ { 0x0910, 0x610c70 },
+ { 0x0914, 0x610c78 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nv84_disp_mast_mthd_chan = {
+ .name = "Core",
+ .addr = 0x000000,
+ .data = {
+ { "Global", 1, &nv50_disp_mast_mthd_base },
+ { "DAC", 3, &nv84_disp_mast_mthd_dac },
+ { "SOR", 2, &nv50_disp_mast_mthd_sor },
+ { "PIOR", 3, &nv50_disp_mast_mthd_pior },
+ { "HEAD", 2, &nv84_disp_mast_mthd_head },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * EVO sync channel objects
+ ******************************************************************************/
+
+static const struct nv50_disp_mthd_list
+nv84_disp_sync_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x000000 },
+ { 0x0084, 0x0008c4 },
+ { 0x0088, 0x0008d0 },
+ { 0x008c, 0x0008dc },
+ { 0x0090, 0x0008e4 },
+ { 0x0094, 0x610884 },
+ { 0x00a0, 0x6108a0 },
+ { 0x00a4, 0x610878 },
+ { 0x00c0, 0x61086c },
+ { 0x00c4, 0x610800 },
+ { 0x00c8, 0x61080c },
+ { 0x00cc, 0x610818 },
+ { 0x00e0, 0x610858 },
+ { 0x00e4, 0x610860 },
+ { 0x00e8, 0x6108ac },
+ { 0x00ec, 0x6108b4 },
+ { 0x00fc, 0x610824 },
+ { 0x0100, 0x610894 },
+ { 0x0104, 0x61082c },
+ { 0x0110, 0x6108bc },
+ { 0x0114, 0x61088c },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nv84_disp_sync_mthd_chan = {
+ .name = "Base",
+ .addr = 0x000540,
+ .data = {
+ { "Global", 1, &nv84_disp_sync_mthd_base },
+ { "Image", 2, &nv50_disp_sync_mthd_image },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * EVO overlay channel objects
+ ******************************************************************************/
+
+static const struct nv50_disp_mthd_list
+nv84_disp_ovly_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x000000 },
+ { 0x0084, 0x6109a0 },
+ { 0x0088, 0x6109c0 },
+ { 0x008c, 0x6109c8 },
+ { 0x0090, 0x6109b4 },
+ { 0x0094, 0x610970 },
+ { 0x00a0, 0x610998 },
+ { 0x00a4, 0x610964 },
+ { 0x00c0, 0x610958 },
+ { 0x00e0, 0x6109a8 },
+ { 0x00e4, 0x6109d0 },
+ { 0x00e8, 0x6109d8 },
+ { 0x0100, 0x61094c },
+ { 0x0104, 0x610984 },
+ { 0x0108, 0x61098c },
+ { 0x0800, 0x6109f8 },
+ { 0x0808, 0x610a08 },
+ { 0x080c, 0x610a10 },
+ { 0x0810, 0x610a00 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nv84_disp_ovly_mthd_chan = {
+ .name = "Overlay",
+ .addr = 0x000540,
+ .data = {
+ { "Global", 1, &nv84_disp_ovly_mthd_base },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
static struct nouveau_oclass
nv84_disp_sclass[] = {
{ NV84_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
@@ -59,6 +232,10 @@ nv84_disp_base_oclass[] = {
{}
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
static int
nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -91,13 +268,17 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nv84_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x82),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv84_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x82),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv84_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nv84_disp_mast_mthd_chan,
+ .mthd.base = &nv84_disp_sync_mthd_chan,
+ .mthd.ovly = &nv84_disp_ovly_mthd_chan,
+ .mthd.prev = 0x000004,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
index a518543c00ab..6844061c7e04 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
@@ -29,6 +29,38 @@
#include "nv50.h"
+/*******************************************************************************
+ * EVO master channel object
+ ******************************************************************************/
+
+const struct nv50_disp_mthd_list
+nv94_disp_mast_mthd_sor = {
+ .mthd = 0x0040,
+ .addr = 0x000008,
+ .data = {
+ { 0x0600, 0x610794 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nv94_disp_mast_mthd_chan = {
+ .name = "Core",
+ .addr = 0x000000,
+ .data = {
+ { "Global", 1, &nv50_disp_mast_mthd_base },
+ { "DAC", 3, &nv84_disp_mast_mthd_dac },
+ { "SOR", 4, &nv94_disp_mast_mthd_sor },
+ { "PIOR", 3, &nv50_disp_mast_mthd_pior },
+ { "HEAD", 2, &nv84_disp_mast_mthd_head },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
static struct nouveau_oclass
nv94_disp_sclass[] = {
{ NV94_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
@@ -59,6 +91,10 @@ nv94_disp_base_oclass[] = {
{}
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
static int
nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -92,13 +128,17 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nv94_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x88),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nv94_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x88),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv94_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nv94_disp_mast_mthd_chan,
+ .mthd.base = &nv84_disp_sync_mthd_chan,
+ .mthd.ovly = &nv84_disp_ovly_mthd_chan,
+ .mthd.prev = 0x000004,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
index 6cf8eefac368..88c96241c02a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
@@ -29,6 +29,55 @@
#include "nv50.h"
+/*******************************************************************************
+ * EVO overlay channel objects
+ ******************************************************************************/
+
+static const struct nv50_disp_mthd_list
+nva0_disp_ovly_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x000000 },
+ { 0x0084, 0x6109a0 },
+ { 0x0088, 0x6109c0 },
+ { 0x008c, 0x6109c8 },
+ { 0x0090, 0x6109b4 },
+ { 0x0094, 0x610970 },
+ { 0x00a0, 0x610998 },
+ { 0x00a4, 0x610964 },
+ { 0x00b0, 0x610c98 },
+ { 0x00b4, 0x610ca4 },
+ { 0x00b8, 0x610cac },
+ { 0x00c0, 0x610958 },
+ { 0x00e0, 0x6109a8 },
+ { 0x00e4, 0x6109d0 },
+ { 0x00e8, 0x6109d8 },
+ { 0x0100, 0x61094c },
+ { 0x0104, 0x610984 },
+ { 0x0108, 0x61098c },
+ { 0x0800, 0x6109f8 },
+ { 0x0808, 0x610a08 },
+ { 0x080c, 0x610a10 },
+ { 0x0810, 0x610a00 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_chan
+nva0_disp_ovly_mthd_chan = {
+ .name = "Overlay",
+ .addr = 0x000540,
+ .data = {
+ { "Global", 1, &nva0_disp_ovly_mthd_base },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
static struct nouveau_oclass
nva0_disp_sclass[] = {
{ NVA0_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
@@ -45,6 +94,10 @@ nva0_disp_base_oclass[] = {
{}
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
static int
nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -77,13 +130,17 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nva0_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x83),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nva0_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x83),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nva0_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nv84_disp_mast_mthd_chan,
+ .mthd.base = &nv84_disp_sync_mthd_chan,
+ .mthd.ovly = &nva0_disp_ovly_mthd_chan,
+ .mthd.prev = 0x000004,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
index 6ad6dcece43b..46cb2ce0e82a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
@@ -29,6 +29,10 @@
#include "nv50.h"
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
static struct nouveau_oclass
nva3_disp_sclass[] = {
{ NVA3_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
@@ -60,6 +64,10 @@ nva3_disp_base_oclass[] = {
{}
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
static int
nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -94,13 +102,17 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nva3_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x85),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nva3_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x85),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nva3_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nv94_disp_mast_mthd_chan,
+ .mthd.base = &nv84_disp_sync_mthd_chan,
+ .mthd.ovly = &nv84_disp_ovly_mthd_chan,
+ .mthd.prev = 0x000004,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
index 1c5e4e8b2c82..7762665ad8fd 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
@@ -124,6 +124,146 @@ nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend)
* EVO master channel object
******************************************************************************/
+const struct nv50_disp_mthd_list
+nvd0_disp_mast_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x660080 },
+ { 0x0084, 0x660084 },
+ { 0x0088, 0x660088 },
+ { 0x008c, 0x000000 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nvd0_disp_mast_mthd_dac = {
+ .mthd = 0x0020,
+ .addr = 0x000020,
+ .data = {
+ { 0x0180, 0x660180 },
+ { 0x0184, 0x660184 },
+ { 0x0188, 0x660188 },
+ { 0x0190, 0x660190 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nvd0_disp_mast_mthd_sor = {
+ .mthd = 0x0020,
+ .addr = 0x000020,
+ .data = {
+ { 0x0200, 0x660200 },
+ { 0x0204, 0x660204 },
+ { 0x0208, 0x660208 },
+ { 0x0210, 0x660210 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_list
+nvd0_disp_mast_mthd_pior = {
+ .mthd = 0x0020,
+ .addr = 0x000020,
+ .data = {
+ { 0x0300, 0x660300 },
+ { 0x0304, 0x660304 },
+ { 0x0308, 0x660308 },
+ { 0x0310, 0x660310 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_list
+nvd0_disp_mast_mthd_head = {
+ .mthd = 0x0300,
+ .addr = 0x000300,
+ .data = {
+ { 0x0400, 0x660400 },
+ { 0x0404, 0x660404 },
+ { 0x0408, 0x660408 },
+ { 0x040c, 0x66040c },
+ { 0x0410, 0x660410 },
+ { 0x0414, 0x660414 },
+ { 0x0418, 0x660418 },
+ { 0x041c, 0x66041c },
+ { 0x0420, 0x660420 },
+ { 0x0424, 0x660424 },
+ { 0x0428, 0x660428 },
+ { 0x042c, 0x66042c },
+ { 0x0430, 0x660430 },
+ { 0x0434, 0x660434 },
+ { 0x0438, 0x660438 },
+ { 0x0440, 0x660440 },
+ { 0x0444, 0x660444 },
+ { 0x0448, 0x660448 },
+ { 0x044c, 0x66044c },
+ { 0x0450, 0x660450 },
+ { 0x0454, 0x660454 },
+ { 0x0458, 0x660458 },
+ { 0x045c, 0x66045c },
+ { 0x0460, 0x660460 },
+ { 0x0468, 0x660468 },
+ { 0x046c, 0x66046c },
+ { 0x0470, 0x660470 },
+ { 0x0474, 0x660474 },
+ { 0x0480, 0x660480 },
+ { 0x0484, 0x660484 },
+ { 0x048c, 0x66048c },
+ { 0x0490, 0x660490 },
+ { 0x0494, 0x660494 },
+ { 0x0498, 0x660498 },
+ { 0x04b0, 0x6604b0 },
+ { 0x04b8, 0x6604b8 },
+ { 0x04bc, 0x6604bc },
+ { 0x04c0, 0x6604c0 },
+ { 0x04c4, 0x6604c4 },
+ { 0x04c8, 0x6604c8 },
+ { 0x04d0, 0x6604d0 },
+ { 0x04d4, 0x6604d4 },
+ { 0x04e0, 0x6604e0 },
+ { 0x04e4, 0x6604e4 },
+ { 0x04e8, 0x6604e8 },
+ { 0x04ec, 0x6604ec },
+ { 0x04f0, 0x6604f0 },
+ { 0x04f4, 0x6604f4 },
+ { 0x04f8, 0x6604f8 },
+ { 0x04fc, 0x6604fc },
+ { 0x0500, 0x660500 },
+ { 0x0504, 0x660504 },
+ { 0x0508, 0x660508 },
+ { 0x050c, 0x66050c },
+ { 0x0510, 0x660510 },
+ { 0x0514, 0x660514 },
+ { 0x0518, 0x660518 },
+ { 0x051c, 0x66051c },
+ { 0x052c, 0x66052c },
+ { 0x0530, 0x660530 },
+ { 0x054c, 0x66054c },
+ { 0x0550, 0x660550 },
+ { 0x0554, 0x660554 },
+ { 0x0558, 0x660558 },
+ { 0x055c, 0x66055c },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_chan
+nvd0_disp_mast_mthd_chan = {
+ .name = "Core",
+ .addr = 0x000000,
+ .data = {
+ { "Global", 1, &nvd0_disp_mast_mthd_base },
+ { "DAC", 3, &nvd0_disp_mast_mthd_dac },
+ { "SOR", 8, &nvd0_disp_mast_mthd_sor },
+ { "PIOR", 4, &nvd0_disp_mast_mthd_pior },
+ { "HEAD", 4, &nvd0_disp_mast_mthd_head },
+ {}
+ }
+};
+
static int
nvd0_disp_mast_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -216,6 +356,81 @@ nvd0_disp_mast_ofuncs = {
* EVO sync channel objects
******************************************************************************/
+static const struct nv50_disp_mthd_list
+nvd0_disp_sync_mthd_base = {
+ .mthd = 0x0000,
+ .addr = 0x000000,
+ .data = {
+ { 0x0080, 0x661080 },
+ { 0x0084, 0x661084 },
+ { 0x0088, 0x661088 },
+ { 0x008c, 0x66108c },
+ { 0x0090, 0x661090 },
+ { 0x0094, 0x661094 },
+ { 0x00a0, 0x6610a0 },
+ { 0x00a4, 0x6610a4 },
+ { 0x00c0, 0x6610c0 },
+ { 0x00c4, 0x6610c4 },
+ { 0x00c8, 0x6610c8 },
+ { 0x00cc, 0x6610cc },
+ { 0x00e0, 0x6610e0 },
+ { 0x00e4, 0x6610e4 },
+ { 0x00e8, 0x6610e8 },
+ { 0x00ec, 0x6610ec },
+ { 0x00fc, 0x6610fc },
+ { 0x0100, 0x661100 },
+ { 0x0104, 0x661104 },
+ { 0x0108, 0x661108 },
+ { 0x010c, 0x66110c },
+ { 0x0110, 0x661110 },
+ { 0x0114, 0x661114 },
+ { 0x0118, 0x661118 },
+ { 0x011c, 0x66111c },
+ { 0x0130, 0x661130 },
+ { 0x0134, 0x661134 },
+ { 0x0138, 0x661138 },
+ { 0x013c, 0x66113c },
+ { 0x0140, 0x661140 },
+ { 0x0144, 0x661144 },
+ { 0x0148, 0x661148 },
+ { 0x014c, 0x66114c },
+ { 0x0150, 0x661150 },
+ { 0x0154, 0x661154 },
+ { 0x0158, 0x661158 },
+ { 0x015c, 0x66115c },
+ { 0x0160, 0x661160 },
+ { 0x0164, 0x661164 },
+ { 0x0168, 0x661168 },
+ { 0x016c, 0x66116c },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_list
+nvd0_disp_sync_mthd_image = {
+ .mthd = 0x0400,
+ .addr = 0x000400,
+ .data = {
+ { 0x0400, 0x661400 },
+ { 0x0404, 0x661404 },
+ { 0x0408, 0x661408 },
+ { 0x040c, 0x66140c },
+ { 0x0410, 0x661410 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nvd0_disp_sync_mthd_chan = {
+ .name = "Base",
+ .addr = 0x001000,
+ .data = {
+ { "Global", 1, &nvd0_disp_sync_mthd_base },
+ { "Image", 2, &nvd0_disp_sync_mthd_image },
+ {}
+ }
+};
+
static int
nvd0_disp_sync_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -256,6 +471,68 @@ nvd0_disp_sync_ofuncs = {
* EVO overlay channel objects
******************************************************************************/
+static const struct nv50_disp_mthd_list
+nvd0_disp_ovly_mthd_base = {
+ .mthd = 0x0000,
+ .data = {
+ { 0x0080, 0x665080 },
+ { 0x0084, 0x665084 },
+ { 0x0088, 0x665088 },
+ { 0x008c, 0x66508c },
+ { 0x0090, 0x665090 },
+ { 0x0094, 0x665094 },
+ { 0x00a0, 0x6650a0 },
+ { 0x00a4, 0x6650a4 },
+ { 0x00b0, 0x6650b0 },
+ { 0x00b4, 0x6650b4 },
+ { 0x00b8, 0x6650b8 },
+ { 0x00c0, 0x6650c0 },
+ { 0x00e0, 0x6650e0 },
+ { 0x00e4, 0x6650e4 },
+ { 0x00e8, 0x6650e8 },
+ { 0x0100, 0x665100 },
+ { 0x0104, 0x665104 },
+ { 0x0108, 0x665108 },
+ { 0x010c, 0x66510c },
+ { 0x0110, 0x665110 },
+ { 0x0118, 0x665118 },
+ { 0x011c, 0x66511c },
+ { 0x0120, 0x665120 },
+ { 0x0124, 0x665124 },
+ { 0x0130, 0x665130 },
+ { 0x0134, 0x665134 },
+ { 0x0138, 0x665138 },
+ { 0x013c, 0x66513c },
+ { 0x0140, 0x665140 },
+ { 0x0144, 0x665144 },
+ { 0x0148, 0x665148 },
+ { 0x014c, 0x66514c },
+ { 0x0150, 0x665150 },
+ { 0x0154, 0x665154 },
+ { 0x0158, 0x665158 },
+ { 0x015c, 0x66515c },
+ { 0x0160, 0x665160 },
+ { 0x0164, 0x665164 },
+ { 0x0168, 0x665168 },
+ { 0x016c, 0x66516c },
+ { 0x0400, 0x665400 },
+ { 0x0408, 0x665408 },
+ { 0x040c, 0x66540c },
+ { 0x0410, 0x665410 },
+ {}
+ }
+};
+
+static const struct nv50_disp_mthd_chan
+nvd0_disp_ovly_mthd_chan = {
+ .name = "Overlay",
+ .addr = 0x001000,
+ .data = {
+ { "Global", 1, &nvd0_disp_ovly_mthd_base },
+ {}
+ }
+};
+
static int
nvd0_disp_ovly_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -897,19 +1174,22 @@ nvd0_disp_intr_supervisor(struct work_struct *work)
{
struct nv50_disp_priv *priv =
container_of(work, struct nv50_disp_priv, supervisor);
+ struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass;
u32 mask[4];
int head;
- nv_debug(priv, "supervisor %08x\n", priv->super);
+ nv_debug(priv, "supervisor %d\n", ffs(priv->super));
for (head = 0; head < priv->head.nr; head++) {
mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800));
nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]);
}
if (priv->super & 0x00000001) {
+ nv50_disp_mthd_chan(priv, NV_DBG_DEBUG, 0, impl->mthd.core);
for (head = 0; head < priv->head.nr; head++) {
if (!(mask[head] & 0x00001000))
continue;
+ nv_debug(priv, "supervisor 1.0 - head %d\n", head);
nvd0_disp_intr_unk1_0(priv, head);
}
} else
@@ -917,16 +1197,19 @@ nvd0_disp_intr_supervisor(struct work_struct *work)
for (head = 0; head < priv->head.nr; head++) {
if (!(mask[head] & 0x00001000))
continue;
+ nv_debug(priv, "supervisor 2.0 - head %d\n", head);
nvd0_disp_intr_unk2_0(priv, head);
}
for (head = 0; head < priv->head.nr; head++) {
if (!(mask[head] & 0x00010000))
continue;
+ nv_debug(priv, "supervisor 2.1 - head %d\n", head);
nvd0_disp_intr_unk2_1(priv, head);
}
for (head = 0; head < priv->head.nr; head++) {
if (!(mask[head] & 0x00001000))
continue;
+ nv_debug(priv, "supervisor 2.2 - head %d\n", head);
nvd0_disp_intr_unk2_2(priv, head);
}
} else
@@ -934,6 +1217,7 @@ nvd0_disp_intr_supervisor(struct work_struct *work)
for (head = 0; head < priv->head.nr; head++) {
if (!(mask[head] & 0x00001000))
continue;
+ nv_debug(priv, "supervisor 3.0 - head %d\n", head);
nvd0_disp_intr_unk4_0(priv, head);
}
}
@@ -943,6 +1227,53 @@ nvd0_disp_intr_supervisor(struct work_struct *work)
nv_wr32(priv, 0x6101d0, 0x80000000);
}
+static void
+nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
+{
+ const struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass;
+ u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12));
+ u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12));
+ u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12));
+
+ nv_error(priv, "chid %d mthd 0x%04x data 0x%08x "
+ "0x%08x 0x%08x\n",
+ chid, (mthd & 0x0000ffc), data, mthd, unkn);
+
+ if (chid == 0) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0,
+ impl->mthd.core);
+ break;
+ default:
+ break;
+ }
+ } else
+ if (chid <= 4) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1,
+ impl->mthd.base);
+ break;
+ default:
+ break;
+ }
+ } else
+ if (chid <= 8) {
+ switch (mthd) {
+ case 0x0080:
+ nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5,
+ impl->mthd.ovly);
+ break;
+ default:
+ break;
+ }
+ }
+
+ nv_wr32(priv, 0x61009c, (1 << chid));
+ nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000);
+}
+
void
nvd0_disp_intr(struct nouveau_subdev *subdev)
{
@@ -959,18 +1290,8 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
if (intr & 0x00000002) {
u32 stat = nv_rd32(priv, 0x61009c);
int chid = ffs(stat) - 1;
- if (chid >= 0) {
- u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12));
- u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12));
- u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12));
-
- nv_error(priv, "chid %d mthd 0x%04x data 0x%08x "
- "0x%08x 0x%08x\n",
- chid, (mthd & 0x0000ffc), data, mthd, unkn);
- nv_wr32(priv, 0x61009c, (1 << chid));
- nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000);
- }
-
+ if (chid >= 0)
+ nvd0_disp_intr_error(priv, chid);
intr &= ~0x00000002;
}
@@ -1035,13 +1356,17 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nvd0_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x90),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nvd0_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x90),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvd0_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nvd0_disp_mast_mthd_chan,
+ .mthd.base = &nvd0_disp_sync_mthd_chan,
+ .mthd.ovly = &nvd0_disp_ovly_mthd_chan,
+ .mthd.prev = -0x020000,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
index ab63f32c00b2..44e0b8f34c1a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
@@ -29,6 +29,175 @@
#include "nv50.h"
+/*******************************************************************************
+ * EVO master channel object
+ ******************************************************************************/
+
+static const struct nv50_disp_mthd_list
+nve0_disp_mast_mthd_head = {
+ .mthd = 0x0300,
+ .addr = 0x000300,
+ .data = {
+ { 0x0400, 0x660400 },
+ { 0x0404, 0x660404 },
+ { 0x0408, 0x660408 },
+ { 0x040c, 0x66040c },
+ { 0x0410, 0x660410 },
+ { 0x0414, 0x660414 },
+ { 0x0418, 0x660418 },
+ { 0x041c, 0x66041c },
+ { 0x0420, 0x660420 },
+ { 0x0424, 0x660424 },
+ { 0x0428, 0x660428 },
+ { 0x042c, 0x66042c },
+ { 0x0430, 0x660430 },
+ { 0x0434, 0x660434 },
+ { 0x0438, 0x660438 },
+ { 0x0440, 0x660440 },
+ { 0x0444, 0x660444 },
+ { 0x0448, 0x660448 },
+ { 0x044c, 0x66044c },
+ { 0x0450, 0x660450 },
+ { 0x0454, 0x660454 },
+ { 0x0458, 0x660458 },
+ { 0x045c, 0x66045c },
+ { 0x0460, 0x660460 },
+ { 0x0468, 0x660468 },
+ { 0x046c, 0x66046c },
+ { 0x0470, 0x660470 },
+ { 0x0474, 0x660474 },
+ { 0x047c, 0x66047c },
+ { 0x0480, 0x660480 },
+ { 0x0484, 0x660484 },
+ { 0x0488, 0x660488 },
+ { 0x048c, 0x66048c },
+ { 0x0490, 0x660490 },
+ { 0x0494, 0x660494 },
+ { 0x0498, 0x660498 },
+ { 0x04a0, 0x6604a0 },
+ { 0x04b0, 0x6604b0 },
+ { 0x04b8, 0x6604b8 },
+ { 0x04bc, 0x6604bc },
+ { 0x04c0, 0x6604c0 },
+ { 0x04c4, 0x6604c4 },
+ { 0x04c8, 0x6604c8 },
+ { 0x04d0, 0x6604d0 },
+ { 0x04d4, 0x6604d4 },
+ { 0x04e0, 0x6604e0 },
+ { 0x04e4, 0x6604e4 },
+ { 0x04e8, 0x6604e8 },
+ { 0x04ec, 0x6604ec },
+ { 0x04f0, 0x6604f0 },
+ { 0x04f4, 0x6604f4 },
+ { 0x04f8, 0x6604f8 },
+ { 0x04fc, 0x6604fc },
+ { 0x0500, 0x660500 },
+ { 0x0504, 0x660504 },
+ { 0x0508, 0x660508 },
+ { 0x050c, 0x66050c },
+ { 0x0510, 0x660510 },
+ { 0x0514, 0x660514 },
+ { 0x0518, 0x660518 },
+ { 0x051c, 0x66051c },
+ { 0x0520, 0x660520 },
+ { 0x0524, 0x660524 },
+ { 0x052c, 0x66052c },
+ { 0x0530, 0x660530 },
+ { 0x054c, 0x66054c },
+ { 0x0550, 0x660550 },
+ { 0x0554, 0x660554 },
+ { 0x0558, 0x660558 },
+ { 0x055c, 0x66055c },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nve0_disp_mast_mthd_chan = {
+ .name = "Core",
+ .addr = 0x000000,
+ .data = {
+ { "Global", 1, &nvd0_disp_mast_mthd_base },
+ { "DAC", 3, &nvd0_disp_mast_mthd_dac },
+ { "SOR", 8, &nvd0_disp_mast_mthd_sor },
+ { "PIOR", 4, &nvd0_disp_mast_mthd_pior },
+ { "HEAD", 4, &nve0_disp_mast_mthd_head },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * EVO overlay channel objects
+ ******************************************************************************/
+
+static const struct nv50_disp_mthd_list
+nve0_disp_ovly_mthd_base = {
+ .mthd = 0x0000,
+ .data = {
+ { 0x0080, 0x665080 },
+ { 0x0084, 0x665084 },
+ { 0x0088, 0x665088 },
+ { 0x008c, 0x66508c },
+ { 0x0090, 0x665090 },
+ { 0x0094, 0x665094 },
+ { 0x00a0, 0x6650a0 },
+ { 0x00a4, 0x6650a4 },
+ { 0x00b0, 0x6650b0 },
+ { 0x00b4, 0x6650b4 },
+ { 0x00b8, 0x6650b8 },
+ { 0x00c0, 0x6650c0 },
+ { 0x00c4, 0x6650c4 },
+ { 0x00e0, 0x6650e0 },
+ { 0x00e4, 0x6650e4 },
+ { 0x00e8, 0x6650e8 },
+ { 0x0100, 0x665100 },
+ { 0x0104, 0x665104 },
+ { 0x0108, 0x665108 },
+ { 0x010c, 0x66510c },
+ { 0x0110, 0x665110 },
+ { 0x0118, 0x665118 },
+ { 0x011c, 0x66511c },
+ { 0x0120, 0x665120 },
+ { 0x0124, 0x665124 },
+ { 0x0130, 0x665130 },
+ { 0x0134, 0x665134 },
+ { 0x0138, 0x665138 },
+ { 0x013c, 0x66513c },
+ { 0x0140, 0x665140 },
+ { 0x0144, 0x665144 },
+ { 0x0148, 0x665148 },
+ { 0x014c, 0x66514c },
+ { 0x0150, 0x665150 },
+ { 0x0154, 0x665154 },
+ { 0x0158, 0x665158 },
+ { 0x015c, 0x66515c },
+ { 0x0160, 0x665160 },
+ { 0x0164, 0x665164 },
+ { 0x0168, 0x665168 },
+ { 0x016c, 0x66516c },
+ { 0x0400, 0x665400 },
+ { 0x0404, 0x665404 },
+ { 0x0408, 0x665408 },
+ { 0x040c, 0x66540c },
+ { 0x0410, 0x665410 },
+ {}
+ }
+};
+
+const struct nv50_disp_mthd_chan
+nve0_disp_ovly_mthd_chan = {
+ .name = "Overlay",
+ .addr = 0x001000,
+ .data = {
+ { "Global", 1, &nve0_disp_ovly_mthd_base },
+ {}
+ }
+};
+
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
static struct nouveau_oclass
nve0_disp_sclass[] = {
{ NVE0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
@@ -45,6 +214,10 @@ nve0_disp_base_oclass[] = {
{}
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
static int
nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -77,13 +250,17 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nve0_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x91),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nve0_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x91),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nve0_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nve0_disp_mast_mthd_chan,
+ .mthd.base = &nvd0_disp_sync_mthd_chan,
+ .mthd.ovly = &nve0_disp_ovly_mthd_chan,
+ .mthd.prev = -0x020000,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
index 05fee10e0c97..482585d375fa 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
@@ -29,6 +29,10 @@
#include "nv50.h"
+/*******************************************************************************
+ * Base display object
+ ******************************************************************************/
+
static struct nouveau_oclass
nvf0_disp_sclass[] = {
{ NVF0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
@@ -45,6 +49,10 @@ nvf0_disp_base_oclass[] = {
{}
};
+/*******************************************************************************
+ * Display engine implementation
+ ******************************************************************************/
+
static int
nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -77,13 +85,17 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nouveau_oclass
-nvf0_disp_oclass = {
- .handle = NV_ENGINE(DISP, 0x92),
- .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nvf0_disp_oclass = &(struct nv50_disp_impl) {
+ .base.base.handle = NV_ENGINE(DISP, 0x92),
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvf0_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
-};
+ .mthd.core = &nve0_disp_mast_mthd_chan,
+ .mthd.base = &nvd0_disp_sync_mthd_chan,
+ .mthd.ovly = &nve0_disp_ovly_mthd_chan,
+ .mthd.prev = -0x020000,
+}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
new file mode 100644
index 000000000000..cc3c7a4ca747
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
@@ -0,0 +1,10 @@
+#ifndef __NVKM_DISP_PRIV_H__
+#define __NVKM_DISP_PRIV_H__
+
+#include <engine/disp.h>
+
+struct nouveau_disp_impl {
+ struct nouveau_oclass base;
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
index 944e73ac485c..1cfb3bb90131 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
@@ -53,6 +53,9 @@ nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
case NVF0_DISP_MAST_CLASS:
case NVF0_DISP_SYNC_CLASS:
case NVF0_DISP_OVLY_CLASS:
+ case GM107_DISP_MAST_CLASS:
+ case GM107_DISP_SYNC_CLASS:
+ case GM107_DISP_OVLY_CLASS:
break;
default:
return -EINVAL;
diff --git a/drivers/gpu/drm/nouveau/core/engine/falcon.c b/drivers/gpu/drm/nouveau/core/engine/falcon.c
index 5e077e4ed7f6..2914646c8709 100644
--- a/drivers/gpu/drm/nouveau/core/engine/falcon.c
+++ b/drivers/gpu/drm/nouveau/core/engine/falcon.c
@@ -119,7 +119,7 @@ _nouveau_falcon_init(struct nouveau_object *object)
snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x",
device->chipset, falcon->addr >> 12);
- ret = request_firmware(&fw, name, &device->pdev->dev);
+ ret = request_firmware(&fw, name, nv_device_base(device));
if (ret == 0) {
falcon->code.data = vmemdup(fw->data, fw->size);
falcon->code.size = fw->size;
@@ -138,7 +138,7 @@ _nouveau_falcon_init(struct nouveau_object *object)
snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd",
device->chipset, falcon->addr >> 12);
- ret = request_firmware(&fw, name, &device->pdev->dev);
+ ret = request_firmware(&fw, name, nv_device_base(device));
if (ret) {
nv_error(falcon, "unable to load firmware data\n");
return ret;
@@ -153,7 +153,7 @@ _nouveau_falcon_init(struct nouveau_object *object)
snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc",
device->chipset, falcon->addr >> 12);
- ret = request_firmware(&fw, name, &device->pdev->dev);
+ ret = request_firmware(&fw, name, nv_device_base(device));
if (ret) {
nv_error(falcon, "unable to load firmware code\n");
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
index d3ec436d9cb5..6f9041ced9a2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
@@ -86,7 +86,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent,
}
/* map fifo control registers */
- chan->user = ioremap(pci_resource_start(device->pdev, bar) + addr +
+ chan->user = ioremap(nv_device_resource_start(device, bar) + addr +
(chan->chid * size), size);
if (!chan->user)
return -EFAULT;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
index b22a33f0702d..fa1e719872b7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
@@ -41,8 +41,16 @@
struct nvc0_fifo_priv {
struct nouveau_fifo base;
- struct nouveau_gpuobj *playlist[2];
- int cur_playlist;
+
+ struct work_struct fault;
+ u64 mask;
+
+ struct {
+ struct nouveau_gpuobj *mem[2];
+ int active;
+ wait_queue_head_t wait;
+ } runlist;
+
struct {
struct nouveau_gpuobj *mem;
struct nouveau_vma bar;
@@ -58,6 +66,11 @@ struct nvc0_fifo_base {
struct nvc0_fifo_chan {
struct nouveau_fifo_chan base;
+ enum {
+ STOPPED,
+ RUNNING,
+ KILLED
+ } state;
};
/*******************************************************************************
@@ -65,29 +78,33 @@ struct nvc0_fifo_chan {
******************************************************************************/
static void
-nvc0_fifo_playlist_update(struct nvc0_fifo_priv *priv)
+nvc0_fifo_runlist_update(struct nvc0_fifo_priv *priv)
{
struct nouveau_bar *bar = nouveau_bar(priv);
struct nouveau_gpuobj *cur;
int i, p;
mutex_lock(&nv_subdev(priv)->mutex);
- cur = priv->playlist[priv->cur_playlist];
- priv->cur_playlist = !priv->cur_playlist;
+ cur = priv->runlist.mem[priv->runlist.active];
+ priv->runlist.active = !priv->runlist.active;
for (i = 0, p = 0; i < 128; i++) {
- if (!(nv_rd32(priv, 0x003004 + (i * 8)) & 1))
- continue;
- nv_wo32(cur, p + 0, i);
- nv_wo32(cur, p + 4, 0x00000004);
- p += 8;
+ struct nvc0_fifo_chan *chan = (void *)priv->base.channel[i];
+ if (chan && chan->state == RUNNING) {
+ nv_wo32(cur, p + 0, i);
+ nv_wo32(cur, p + 4, 0x00000004);
+ p += 8;
+ }
}
bar->flush(bar);
nv_wr32(priv, 0x002270, cur->addr >> 12);
nv_wr32(priv, 0x002274, 0x01f00000 | (p >> 3));
- if (!nv_wait(priv, 0x00227c, 0x00100000, 0x00000000))
- nv_error(priv, "playlist update failed\n");
+
+ if (wait_event_timeout(priv->runlist.wait,
+ !(nv_rd32(priv, 0x00227c) & 0x00100000),
+ msecs_to_jiffies(2000)) == 0)
+ nv_error(priv, "runlist update timeout\n");
mutex_unlock(&nv_subdev(priv)->mutex);
}
@@ -239,30 +256,32 @@ nvc0_fifo_chan_init(struct nouveau_object *object)
return ret;
nv_wr32(priv, 0x003000 + (chid * 8), 0xc0000000 | base->addr >> 12);
- nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001);
- nvc0_fifo_playlist_update(priv);
+
+ if (chan->state == STOPPED && (chan->state = RUNNING) == RUNNING) {
+ nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001);
+ nvc0_fifo_runlist_update(priv);
+ }
+
return 0;
}
+static void nvc0_fifo_intr_engine(struct nvc0_fifo_priv *priv);
+
static int
nvc0_fifo_chan_fini(struct nouveau_object *object, bool suspend)
{
struct nvc0_fifo_priv *priv = (void *)object->engine;
struct nvc0_fifo_chan *chan = (void *)object;
u32 chid = chan->base.chid;
- u32 mask, engine;
- nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000);
- nvc0_fifo_playlist_update(priv);
- mask = nv_rd32(priv, 0x0025a4);
- for (engine = 0; mask && engine < 16; engine++) {
- if (!(mask & (1 << engine)))
- continue;
- nv_mask(priv, 0x0025a8 + (engine * 4), 0x00000000, 0x00000000);
- mask &= ~(1 << engine);
+ if (chan->state == RUNNING && (chan->state = STOPPED) == STOPPED) {
+ nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000);
+ nvc0_fifo_runlist_update(priv);
}
- nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000);
+ nvc0_fifo_intr_engine(priv);
+
+ nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000);
return nouveau_fifo_channel_fini(&chan->base, suspend);
}
@@ -345,11 +364,177 @@ nvc0_fifo_cclass = {
* PFIFO engine
******************************************************************************/
-static const struct nouveau_enum nvc0_fifo_fault_unit[] = {
+static inline int
+nvc0_fifo_engidx(struct nvc0_fifo_priv *priv, u32 engn)
+{
+ switch (engn) {
+ case NVDEV_ENGINE_GR : engn = 0; break;
+ case NVDEV_ENGINE_BSP : engn = 1; break;
+ case NVDEV_ENGINE_PPP : engn = 2; break;
+ case NVDEV_ENGINE_VP : engn = 3; break;
+ case NVDEV_ENGINE_COPY0: engn = 4; break;
+ case NVDEV_ENGINE_COPY1: engn = 5; break;
+ default:
+ return -1;
+ }
+
+ return engn;
+}
+
+static inline struct nouveau_engine *
+nvc0_fifo_engine(struct nvc0_fifo_priv *priv, u32 engn)
+{
+ switch (engn) {
+ case 0: engn = NVDEV_ENGINE_GR; break;
+ case 1: engn = NVDEV_ENGINE_BSP; break;
+ case 2: engn = NVDEV_ENGINE_PPP; break;
+ case 3: engn = NVDEV_ENGINE_VP; break;
+ case 4: engn = NVDEV_ENGINE_COPY0; break;
+ case 5: engn = NVDEV_ENGINE_COPY1; break;
+ default:
+ return NULL;
+ }
+
+ return nouveau_engine(priv, engn);
+}
+
+static void
+nvc0_fifo_recover_work(struct work_struct *work)
+{
+ struct nvc0_fifo_priv *priv = container_of(work, typeof(*priv), fault);
+ struct nouveau_object *engine;
+ unsigned long flags;
+ u32 engn, engm = 0;
+ u64 mask, todo;
+
+ spin_lock_irqsave(&priv->base.lock, flags);
+ mask = priv->mask;
+ priv->mask = 0ULL;
+ spin_unlock_irqrestore(&priv->base.lock, flags);
+
+ for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn))
+ engm |= 1 << nvc0_fifo_engidx(priv, engn);
+ nv_mask(priv, 0x002630, engm, engm);
+
+ for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) {
+ if ((engine = (void *)nouveau_engine(priv, engn))) {
+ nv_ofuncs(engine)->fini(engine, false);
+ WARN_ON(nv_ofuncs(engine)->init(engine));
+ }
+ }
+
+ nvc0_fifo_runlist_update(priv);
+ nv_wr32(priv, 0x00262c, engm);
+ nv_mask(priv, 0x002630, engm, 0x00000000);
+}
+
+static void
+nvc0_fifo_recover(struct nvc0_fifo_priv *priv, struct nouveau_engine *engine,
+ struct nvc0_fifo_chan *chan)
+{
+ struct nouveau_object *engobj = nv_object(engine);
+ u32 chid = chan->base.chid;
+ unsigned long flags;
+
+ nv_error(priv, "%s engine fault on channel %d, recovering...\n",
+ nv_subdev(engine)->name, chid);
+
+ nv_mask(priv, 0x003004 + (chid * 0x08), 0x00000001, 0x00000000);
+ chan->state = KILLED;
+
+ spin_lock_irqsave(&priv->base.lock, flags);
+ priv->mask |= 1ULL << nv_engidx(engobj);
+ spin_unlock_irqrestore(&priv->base.lock, flags);
+ schedule_work(&priv->fault);
+}
+
+static int
+nvc0_fifo_swmthd(struct nvc0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
+{
+ struct nvc0_fifo_chan *chan = NULL;
+ struct nouveau_handle *bind;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&priv->base.lock, flags);
+ if (likely(chid >= priv->base.min && chid <= priv->base.max))
+ chan = (void *)priv->base.channel[chid];
+ if (unlikely(!chan))
+ goto out;
+
+ bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
+ if (likely(bind)) {
+ if (!mthd || !nv_call(bind->object, mthd, data))
+ ret = 0;
+ nouveau_namedb_put(bind);
+ }
+
+out:
+ spin_unlock_irqrestore(&priv->base.lock, flags);
+ return ret;
+}
+
+static const struct nouveau_enum
+nvc0_fifo_sched_reason[] = {
+ { 0x0a, "CTXSW_TIMEOUT" },
+ {}
+};
+
+static void
+nvc0_fifo_intr_sched_ctxsw(struct nvc0_fifo_priv *priv)
+{
+ struct nouveau_engine *engine;
+ struct nvc0_fifo_chan *chan;
+ u32 engn;
+
+ for (engn = 0; engn < 6; engn++) {
+ u32 stat = nv_rd32(priv, 0x002640 + (engn * 0x04));
+ u32 busy = (stat & 0x80000000);
+ u32 save = (stat & 0x00100000); /* maybe? */
+ u32 unk0 = (stat & 0x00040000);
+ u32 unk1 = (stat & 0x00001000);
+ u32 chid = (stat & 0x0000007f);
+ (void)save;
+
+ if (busy && unk0 && unk1) {
+ if (!(chan = (void *)priv->base.channel[chid]))
+ continue;
+ if (!(engine = nvc0_fifo_engine(priv, engn)))
+ continue;
+ nvc0_fifo_recover(priv, engine, chan);
+ }
+ }
+}
+
+static void
+nvc0_fifo_intr_sched(struct nvc0_fifo_priv *priv)
+{
+ u32 intr = nv_rd32(priv, 0x00254c);
+ u32 code = intr & 0x000000ff;
+ const struct nouveau_enum *en;
+ char enunk[6] = "";
+
+ en = nouveau_enum_find(nvc0_fifo_sched_reason, code);
+ if (!en)
+ snprintf(enunk, sizeof(enunk), "UNK%02x", code);
+
+ nv_error(priv, "SCHED_ERROR [ %s ]\n", en ? en->name : enunk);
+
+ switch (code) {
+ case 0x0a:
+ nvc0_fifo_intr_sched_ctxsw(priv);
+ break;
+ default:
+ break;
+ }
+}
+
+static const struct nouveau_enum
+nvc0_fifo_fault_engine[] = {
{ 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR },
- { 0x03, "PEEPHOLE" },
- { 0x04, "BAR1" },
- { 0x05, "BAR3" },
+ { 0x03, "PEEPHOLE", NULL, NVDEV_ENGINE_IFB },
+ { 0x04, "BAR1", NULL, NVDEV_SUBDEV_BAR },
+ { 0x05, "BAR3", NULL, NVDEV_SUBDEV_INSTMEM },
{ 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO },
{ 0x10, "PBSP", NULL, NVDEV_ENGINE_BSP },
{ 0x11, "PPPP", NULL, NVDEV_ENGINE_PPP },
@@ -361,7 +546,8 @@ static const struct nouveau_enum nvc0_fifo_fault_unit[] = {
{}
};
-static const struct nouveau_enum nvc0_fifo_fault_reason[] = {
+static const struct nouveau_enum
+nvc0_fifo_fault_reason[] = {
{ 0x00, "PT_NOT_PRESENT" },
{ 0x01, "PT_TOO_SHORT" },
{ 0x02, "PAGE_NOT_PRESENT" },
@@ -374,7 +560,8 @@ static const struct nouveau_enum nvc0_fifo_fault_reason[] = {
{}
};
-static const struct nouveau_enum nvc0_fifo_fault_hubclient[] = {
+static const struct nouveau_enum
+nvc0_fifo_fault_hubclient[] = {
{ 0x01, "PCOPY0" },
{ 0x02, "PCOPY1" },
{ 0x04, "DISPATCH" },
@@ -392,7 +579,8 @@ static const struct nouveau_enum nvc0_fifo_fault_hubclient[] = {
{}
};
-static const struct nouveau_enum nvc0_fifo_fault_gpcclient[] = {
+static const struct nouveau_enum
+nvc0_fifo_fault_gpcclient[] = {
{ 0x01, "TEX" },
{ 0x0c, "ESETUP" },
{ 0x0e, "CTXCTL" },
@@ -400,92 +588,92 @@ static const struct nouveau_enum nvc0_fifo_fault_gpcclient[] = {
{}
};
-static const struct nouveau_bitfield nvc0_fifo_subfifo_intr[] = {
-/* { 0x00008000, "" } seen with null ib push */
- { 0x00200000, "ILLEGAL_MTHD" },
- { 0x00800000, "EMPTY_SUBC" },
- {}
-};
-
static void
-nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
+nvc0_fifo_intr_fault(struct nvc0_fifo_priv *priv, int unit)
{
u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10));
u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10));
u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10));
u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10));
+ u32 gpc = (stat & 0x1f000000) >> 24;
u32 client = (stat & 0x00001f00) >> 8;
- const struct nouveau_enum *en;
- struct nouveau_engine *engine;
- struct nouveau_object *engctx = NULL;
-
- switch (unit) {
- case 3: /* PEEPHOLE */
- nv_mask(priv, 0x001718, 0x00000000, 0x00000000);
- break;
- case 4: /* BAR1 */
- nv_mask(priv, 0x001704, 0x00000000, 0x00000000);
- break;
- case 5: /* BAR3 */
- nv_mask(priv, 0x001714, 0x00000000, 0x00000000);
- break;
- default:
- break;
+ u32 write = (stat & 0x00000080);
+ u32 hub = (stat & 0x00000040);
+ u32 reason = (stat & 0x0000000f);
+ struct nouveau_object *engctx = NULL, *object;
+ struct nouveau_engine *engine = NULL;
+ const struct nouveau_enum *er, *eu, *ec;
+ char erunk[6] = "";
+ char euunk[6] = "";
+ char ecunk[6] = "";
+ char gpcid[3] = "";
+
+ er = nouveau_enum_find(nvc0_fifo_fault_reason, reason);
+ if (!er)
+ snprintf(erunk, sizeof(erunk), "UNK%02X", reason);
+
+ eu = nouveau_enum_find(nvc0_fifo_fault_engine, unit);
+ if (eu) {
+ switch (eu->data2) {
+ case NVDEV_SUBDEV_BAR:
+ nv_mask(priv, 0x001704, 0x00000000, 0x00000000);
+ break;
+ case NVDEV_SUBDEV_INSTMEM:
+ nv_mask(priv, 0x001714, 0x00000000, 0x00000000);
+ break;
+ case NVDEV_ENGINE_IFB:
+ nv_mask(priv, 0x001718, 0x00000000, 0x00000000);
+ break;
+ default:
+ engine = nouveau_engine(priv, eu->data2);
+ if (engine)
+ engctx = nouveau_engctx_get(engine, inst);
+ break;
+ }
+ } else {
+ snprintf(euunk, sizeof(euunk), "UNK%02x", unit);
}
- nv_error(priv, "%s fault at 0x%010llx [", (stat & 0x00000080) ?
- "write" : "read", (u64)vahi << 32 | valo);
- nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f);
- pr_cont("] from ");
- en = nouveau_enum_print(nvc0_fifo_fault_unit, unit);
- if (stat & 0x00000040) {
- pr_cont("/");
- nouveau_enum_print(nvc0_fifo_fault_hubclient, client);
+ if (hub) {
+ ec = nouveau_enum_find(nvc0_fifo_fault_hubclient, client);
} else {
- pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24);
- nouveau_enum_print(nvc0_fifo_fault_gpcclient, client);
+ ec = nouveau_enum_find(nvc0_fifo_fault_gpcclient, client);
+ snprintf(gpcid, sizeof(gpcid), "%d", gpc);
}
- if (en && en->data2) {
- engine = nouveau_engine(priv, en->data2);
- if (engine)
- engctx = nouveau_engctx_get(engine, inst);
-
+ if (!ec)
+ snprintf(ecunk, sizeof(ecunk), "UNK%02x", client);
+
+ nv_error(priv, "%s fault at 0x%010llx [%s] from %s/%s%s%s%s on "
+ "channel 0x%010llx [%s]\n", write ? "write" : "read",
+ (u64)vahi << 32 | valo, er ? er->name : erunk,
+ eu ? eu->name : euunk, hub ? "" : "GPC", gpcid, hub ? "" : "/",
+ ec ? ec->name : ecunk, (u64)inst << 12,
+ nouveau_client_name(engctx));
+
+ object = engctx;
+ while (object) {
+ switch (nv_mclass(object)) {
+ case NVC0_CHANNEL_IND_CLASS:
+ nvc0_fifo_recover(priv, engine, (void *)object);
+ break;
+ }
+ object = object->parent;
}
- pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12,
- nouveau_client_name(engctx));
nouveau_engctx_put(engctx);
}
-static int
-nvc0_fifo_swmthd(struct nvc0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
-{
- struct nvc0_fifo_chan *chan = NULL;
- struct nouveau_handle *bind;
- unsigned long flags;
- int ret = -EINVAL;
-
- spin_lock_irqsave(&priv->base.lock, flags);
- if (likely(chid >= priv->base.min && chid <= priv->base.max))
- chan = (void *)priv->base.channel[chid];
- if (unlikely(!chan))
- goto out;
-
- bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
- if (likely(bind)) {
- if (!mthd || !nv_call(bind->object, mthd, data))
- ret = 0;
- nouveau_namedb_put(bind);
- }
-
-out:
- spin_unlock_irqrestore(&priv->base.lock, flags);
- return ret;
-}
+static const struct nouveau_bitfield
+nvc0_fifo_pbdma_intr[] = {
+/* { 0x00008000, "" } seen with null ib push */
+ { 0x00200000, "ILLEGAL_MTHD" },
+ { 0x00800000, "EMPTY_SUBC" },
+ {}
+};
static void
-nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
+nvc0_fifo_intr_pbdma(struct nvc0_fifo_priv *priv, int unit)
{
u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000));
u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000));
@@ -501,11 +689,11 @@ nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
}
if (show) {
- nv_error(priv, "SUBFIFO%d:", unit);
- nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
+ nv_error(priv, "PBDMA%d:", unit);
+ nouveau_bitfield_print(nvc0_fifo_pbdma_intr, show);
pr_cont("\n");
nv_error(priv,
- "SUBFIFO%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
+ "PBDMA%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
unit, chid,
nouveau_client_name_for_fifo_chid(&priv->base, chid),
subc, mthd, data);
@@ -516,6 +704,56 @@ nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
}
static void
+nvc0_fifo_intr_runlist(struct nvc0_fifo_priv *priv)
+{
+ u32 intr = nv_rd32(priv, 0x002a00);
+
+ if (intr & 0x10000000) {
+ wake_up(&priv->runlist.wait);
+ nv_wr32(priv, 0x002a00, 0x10000000);
+ intr &= ~0x10000000;
+ }
+
+ if (intr) {
+ nv_error(priv, "RUNLIST 0x%08x\n", intr);
+ nv_wr32(priv, 0x002a00, intr);
+ }
+}
+
+static void
+nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn)
+{
+ u32 intr = nv_rd32(priv, 0x0025a8 + (engn * 0x04));
+ u32 inte = nv_rd32(priv, 0x002628);
+ u32 unkn;
+
+ for (unkn = 0; unkn < 8; unkn++) {
+ u32 ints = (intr >> (unkn * 0x04)) & inte;
+ if (ints & 0x1) {
+ nouveau_event_trigger(priv->base.uevent, 0);
+ ints &= ~1;
+ }
+ if (ints) {
+ nv_error(priv, "ENGINE %d %d %01x", engn, unkn, ints);
+ nv_mask(priv, 0x002628, ints, 0);
+ }
+ }
+
+ nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr);
+}
+
+static void
+nvc0_fifo_intr_engine(struct nvc0_fifo_priv *priv)
+{
+ u32 mask = nv_rd32(priv, 0x0025a4);
+ while (mask) {
+ u32 unit = __ffs(mask);
+ nvc0_fifo_intr_engine_unit(priv, unit);
+ mask &= ~(1 << unit);
+ }
+}
+
+static void
nvc0_fifo_intr(struct nouveau_subdev *subdev)
{
struct nvc0_fifo_priv *priv = (void *)subdev;
@@ -530,8 +768,7 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
}
if (stat & 0x00000100) {
- u32 intr = nv_rd32(priv, 0x00254c);
- nv_warn(priv, "INTR 0x00000100: 0x%08x\n", intr);
+ nvc0_fifo_intr_sched(priv);
nv_wr32(priv, 0x002100, 0x00000100);
stat &= ~0x00000100;
}
@@ -551,52 +788,41 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
}
if (stat & 0x10000000) {
- u32 units = nv_rd32(priv, 0x00259c);
- u32 u = units;
-
- while (u) {
- int i = ffs(u) - 1;
- nvc0_fifo_isr_vm_fault(priv, i);
- u &= ~(1 << i);
+ u32 mask = nv_rd32(priv, 0x00259c);
+ while (mask) {
+ u32 unit = __ffs(mask);
+ nvc0_fifo_intr_fault(priv, unit);
+ nv_wr32(priv, 0x00259c, (1 << unit));
+ mask &= ~(1 << unit);
}
-
- nv_wr32(priv, 0x00259c, units);
stat &= ~0x10000000;
}
if (stat & 0x20000000) {
- u32 units = nv_rd32(priv, 0x0025a0);
- u32 u = units;
-
- while (u) {
- int i = ffs(u) - 1;
- nvc0_fifo_isr_subfifo_intr(priv, i);
- u &= ~(1 << i);
+ u32 mask = nv_rd32(priv, 0x0025a0);
+ while (mask) {
+ u32 unit = __ffs(mask);
+ nvc0_fifo_intr_pbdma(priv, unit);
+ nv_wr32(priv, 0x0025a0, (1 << unit));
+ mask &= ~(1 << unit);
}
-
- nv_wr32(priv, 0x0025a0, units);
stat &= ~0x20000000;
}
if (stat & 0x40000000) {
- u32 intr0 = nv_rd32(priv, 0x0025a4);
- u32 intr1 = nv_mask(priv, 0x002a00, 0x00000000, 0x00000);
- nv_debug(priv, "INTR 0x40000000: 0x%08x 0x%08x\n",
- intr0, intr1);
+ nvc0_fifo_intr_runlist(priv);
stat &= ~0x40000000;
}
if (stat & 0x80000000) {
- u32 intr = nv_mask(priv, 0x0025a8, 0x00000000, 0x00000000);
- nouveau_event_trigger(priv->base.uevent, 0);
- nv_debug(priv, "INTR 0x80000000: 0x%08x\n", intr);
+ nvc0_fifo_intr_engine(priv);
stat &= ~0x80000000;
}
if (stat) {
- nv_fatal(priv, "unhandled status 0x%08x\n", stat);
+ nv_error(priv, "INTR 0x%08x\n", stat);
+ nv_mask(priv, 0x002140, stat, 0x00000000);
nv_wr32(priv, 0x002100, stat);
- nv_wr32(priv, 0x002140, 0);
}
}
@@ -627,16 +853,20 @@ nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ INIT_WORK(&priv->fault, nvc0_fifo_recover_work);
+
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0,
- &priv->playlist[0]);
+ &priv->runlist.mem[0]);
if (ret)
return ret;
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0,
- &priv->playlist[1]);
+ &priv->runlist.mem[1]);
if (ret)
return ret;
+ init_waitqueue_head(&priv->runlist.wait);
+
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 128 * 0x1000, 0x1000, 0,
&priv->user.mem);
if (ret)
@@ -665,8 +895,8 @@ nvc0_fifo_dtor(struct nouveau_object *object)
nouveau_gpuobj_unmap(&priv->user.bar);
nouveau_gpuobj_ref(NULL, &priv->user.mem);
- nouveau_gpuobj_ref(NULL, &priv->playlist[1]);
- nouveau_gpuobj_ref(NULL, &priv->playlist[0]);
+ nouveau_gpuobj_ref(NULL, &priv->runlist.mem[0]);
+ nouveau_gpuobj_ref(NULL, &priv->runlist.mem[1]);
nouveau_fifo_destroy(&priv->base);
}
@@ -685,9 +915,9 @@ nvc0_fifo_init(struct nouveau_object *object)
nv_wr32(priv, 0x002204, 0xffffffff);
priv->spoon_nr = hweight32(nv_rd32(priv, 0x002204));
- nv_debug(priv, "%d subfifo(s)\n", priv->spoon_nr);
+ nv_debug(priv, "%d PBDMA unit(s)\n", priv->spoon_nr);
- /* assign engines to subfifos */
+ /* assign engines to PBDMAs */
if (priv->spoon_nr >= 3) {
nv_wr32(priv, 0x002208, ~(1 << 0)); /* PGRAPH */
nv_wr32(priv, 0x00220c, ~(1 << 1)); /* PVP */
@@ -697,7 +927,7 @@ nvc0_fifo_init(struct nouveau_object *object)
nv_wr32(priv, 0x00221c, ~(1 << 1)); /* PCE1 */
}
- /* PSUBFIFO[n] */
+ /* PBDMA[n] */
for (i = 0; i < priv->spoon_nr; i++) {
nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
@@ -707,10 +937,9 @@ nvc0_fifo_init(struct nouveau_object *object)
nv_mask(priv, 0x002200, 0x00000001, 0x00000001);
nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
- nv_wr32(priv, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
nv_wr32(priv, 0x002100, 0xffffffff);
- nv_wr32(priv, 0x002140, 0x3fffffff);
- nv_wr32(priv, 0x002628, 0x00000001); /* makes mthd 0x20 work */
+ nv_wr32(priv, 0x002140, 0x7fffffff);
+ nv_wr32(priv, 0x002628, 0x00000001); /* ENGINE_INTR_EN */
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
index 54c1b5b471cd..a9a1a9c9f9f2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
@@ -60,10 +60,15 @@ static const struct {
struct nve0_fifo_engn {
struct nouveau_gpuobj *runlist[2];
int cur_runlist;
+ wait_queue_head_t wait;
};
struct nve0_fifo_priv {
struct nouveau_fifo base;
+
+ struct work_struct fault;
+ u64 mask;
+
struct nve0_fifo_engn engine[FIFO_ENGINE_NR];
struct {
struct nouveau_gpuobj *mem;
@@ -81,6 +86,11 @@ struct nve0_fifo_base {
struct nve0_fifo_chan {
struct nouveau_fifo_chan base;
u32 engine;
+ enum {
+ STOPPED,
+ RUNNING,
+ KILLED
+ } state;
};
/*******************************************************************************
@@ -93,7 +103,6 @@ nve0_fifo_runlist_update(struct nve0_fifo_priv *priv, u32 engine)
struct nouveau_bar *bar = nouveau_bar(priv);
struct nve0_fifo_engn *engn = &priv->engine[engine];
struct nouveau_gpuobj *cur;
- u32 match = (engine << 16) | 0x00000001;
int i, p;
mutex_lock(&nv_subdev(priv)->mutex);
@@ -101,18 +110,21 @@ nve0_fifo_runlist_update(struct nve0_fifo_priv *priv, u32 engine)
engn->cur_runlist = !engn->cur_runlist;
for (i = 0, p = 0; i < priv->base.max; i++) {
- u32 ctrl = nv_rd32(priv, 0x800004 + (i * 8)) & 0x001f0001;
- if (ctrl != match)
- continue;
- nv_wo32(cur, p + 0, i);
- nv_wo32(cur, p + 4, 0x00000000);
- p += 8;
+ struct nve0_fifo_chan *chan = (void *)priv->base.channel[i];
+ if (chan && chan->state == RUNNING && chan->engine == engine) {
+ nv_wo32(cur, p + 0, i);
+ nv_wo32(cur, p + 4, 0x00000000);
+ p += 8;
+ }
}
bar->flush(bar);
nv_wr32(priv, 0x002270, cur->addr >> 12);
nv_wr32(priv, 0x002274, (engine << 20) | (p >> 3));
- if (!nv_wait(priv, 0x002284 + (engine * 8), 0x00100000, 0x00000000))
+
+ if (wait_event_timeout(engn->wait, !(nv_rd32(priv, 0x002284 +
+ (engine * 0x08)) & 0x00100000),
+ msecs_to_jiffies(2000)) == 0)
nv_error(priv, "runlist %d update timeout\n", engine);
mutex_unlock(&nv_subdev(priv)->mutex);
}
@@ -129,9 +141,11 @@ nve0_fifo_context_attach(struct nouveau_object *parent,
switch (nv_engidx(object->engine)) {
case NVDEV_ENGINE_SW :
+ return 0;
case NVDEV_ENGINE_COPY0:
case NVDEV_ENGINE_COPY1:
case NVDEV_ENGINE_COPY2:
+ nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12;
return 0;
case NVDEV_ENGINE_GR : addr = 0x0210; break;
case NVDEV_ENGINE_BSP : addr = 0x0270; break;
@@ -279,9 +293,13 @@ nve0_fifo_chan_init(struct nouveau_object *object)
nv_mask(priv, 0x800004 + (chid * 8), 0x000f0000, chan->engine << 16);
nv_wr32(priv, 0x800000 + (chid * 8), 0x80000000 | base->addr >> 12);
- nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400);
- nve0_fifo_runlist_update(priv, chan->engine);
- nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400);
+
+ if (chan->state == STOPPED && (chan->state = RUNNING) == RUNNING) {
+ nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400);
+ nve0_fifo_runlist_update(priv, chan->engine);
+ nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400);
+ }
+
return 0;
}
@@ -292,10 +310,12 @@ nve0_fifo_chan_fini(struct nouveau_object *object, bool suspend)
struct nve0_fifo_chan *chan = (void *)object;
u32 chid = chan->base.chid;
- nv_mask(priv, 0x800004 + (chid * 8), 0x00000800, 0x00000800);
- nve0_fifo_runlist_update(priv, chan->engine);
- nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000);
+ if (chan->state == RUNNING && (chan->state = STOPPED) == STOPPED) {
+ nv_mask(priv, 0x800004 + (chid * 8), 0x00000800, 0x00000800);
+ nve0_fifo_runlist_update(priv, chan->engine);
+ }
+ nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000);
return nouveau_fifo_channel_fini(&chan->base, suspend);
}
@@ -377,14 +397,211 @@ nve0_fifo_cclass = {
* PFIFO engine
******************************************************************************/
-static const struct nouveau_enum nve0_fifo_sched_reason[] = {
+static inline int
+nve0_fifo_engidx(struct nve0_fifo_priv *priv, u32 engn)
+{
+ switch (engn) {
+ case NVDEV_ENGINE_GR :
+ case NVDEV_ENGINE_COPY2: engn = 0; break;
+ case NVDEV_ENGINE_BSP : engn = 1; break;
+ case NVDEV_ENGINE_PPP : engn = 2; break;
+ case NVDEV_ENGINE_VP : engn = 3; break;
+ case NVDEV_ENGINE_COPY0: engn = 4; break;
+ case NVDEV_ENGINE_COPY1: engn = 5; break;
+ case NVDEV_ENGINE_VENC : engn = 6; break;
+ default:
+ return -1;
+ }
+
+ return engn;
+}
+
+static inline struct nouveau_engine *
+nve0_fifo_engine(struct nve0_fifo_priv *priv, u32 engn)
+{
+ if (engn >= ARRAY_SIZE(fifo_engine))
+ return NULL;
+ return nouveau_engine(priv, fifo_engine[engn].subdev);
+}
+
+static void
+nve0_fifo_recover_work(struct work_struct *work)
+{
+ struct nve0_fifo_priv *priv = container_of(work, typeof(*priv), fault);
+ struct nouveau_object *engine;
+ unsigned long flags;
+ u32 engn, engm = 0;
+ u64 mask, todo;
+
+ spin_lock_irqsave(&priv->base.lock, flags);
+ mask = priv->mask;
+ priv->mask = 0ULL;
+ spin_unlock_irqrestore(&priv->base.lock, flags);
+
+ for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn))
+ engm |= 1 << nve0_fifo_engidx(priv, engn);
+ nv_mask(priv, 0x002630, engm, engm);
+
+ for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) {
+ if ((engine = (void *)nouveau_engine(priv, engn))) {
+ nv_ofuncs(engine)->fini(engine, false);
+ WARN_ON(nv_ofuncs(engine)->init(engine));
+ }
+ nve0_fifo_runlist_update(priv, nve0_fifo_engidx(priv, engn));
+ }
+
+ nv_wr32(priv, 0x00262c, engm);
+ nv_mask(priv, 0x002630, engm, 0x00000000);
+}
+
+static void
+nve0_fifo_recover(struct nve0_fifo_priv *priv, struct nouveau_engine *engine,
+ struct nve0_fifo_chan *chan)
+{
+ struct nouveau_object *engobj = nv_object(engine);
+ u32 chid = chan->base.chid;
+ unsigned long flags;
+
+ nv_error(priv, "%s engine fault on channel %d, recovering...\n",
+ nv_subdev(engine)->name, chid);
+
+ nv_mask(priv, 0x800004 + (chid * 0x08), 0x00000800, 0x00000800);
+ chan->state = KILLED;
+
+ spin_lock_irqsave(&priv->base.lock, flags);
+ priv->mask |= 1ULL << nv_engidx(engobj);
+ spin_unlock_irqrestore(&priv->base.lock, flags);
+ schedule_work(&priv->fault);
+}
+
+static int
+nve0_fifo_swmthd(struct nve0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
+{
+ struct nve0_fifo_chan *chan = NULL;
+ struct nouveau_handle *bind;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&priv->base.lock, flags);
+ if (likely(chid >= priv->base.min && chid <= priv->base.max))
+ chan = (void *)priv->base.channel[chid];
+ if (unlikely(!chan))
+ goto out;
+
+ bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
+ if (likely(bind)) {
+ if (!mthd || !nv_call(bind->object, mthd, data))
+ ret = 0;
+ nouveau_namedb_put(bind);
+ }
+
+out:
+ spin_unlock_irqrestore(&priv->base.lock, flags);
+ return ret;
+}
+
+static const struct nouveau_enum
+nve0_fifo_bind_reason[] = {
+ { 0x01, "BIND_NOT_UNBOUND" },
+ { 0x02, "SNOOP_WITHOUT_BAR1" },
+ { 0x03, "UNBIND_WHILE_RUNNING" },
+ { 0x05, "INVALID_RUNLIST" },
+ { 0x06, "INVALID_CTX_TGT" },
+ { 0x0b, "UNBIND_WHILE_PARKED" },
+ {}
+};
+
+static void
+nve0_fifo_intr_bind(struct nve0_fifo_priv *priv)
+{
+ u32 intr = nv_rd32(priv, 0x00252c);
+ u32 code = intr & 0x000000ff;
+ const struct nouveau_enum *en;
+ char enunk[6] = "";
+
+ en = nouveau_enum_find(nve0_fifo_bind_reason, code);
+ if (!en)
+ snprintf(enunk, sizeof(enunk), "UNK%02x", code);
+
+ nv_error(priv, "BIND_ERROR [ %s ]\n", en ? en->name : enunk);
+}
+
+static const struct nouveau_enum
+nve0_fifo_sched_reason[] = {
{ 0x0a, "CTXSW_TIMEOUT" },
{}
};
-static const struct nouveau_enum nve0_fifo_fault_engine[] = {
+static void
+nve0_fifo_intr_sched_ctxsw(struct nve0_fifo_priv *priv)
+{
+ struct nouveau_engine *engine;
+ struct nve0_fifo_chan *chan;
+ u32 engn;
+
+ for (engn = 0; engn < ARRAY_SIZE(fifo_engine); engn++) {
+ u32 stat = nv_rd32(priv, 0x002640 + (engn * 0x04));
+ u32 busy = (stat & 0x80000000);
+ u32 next = (stat & 0x07ff0000) >> 16;
+ u32 chsw = (stat & 0x00008000);
+ u32 save = (stat & 0x00004000);
+ u32 load = (stat & 0x00002000);
+ u32 prev = (stat & 0x000007ff);
+ u32 chid = load ? next : prev;
+ (void)save;
+
+ if (busy && chsw) {
+ if (!(chan = (void *)priv->base.channel[chid]))
+ continue;
+ if (!(engine = nve0_fifo_engine(priv, engn)))
+ continue;
+ nve0_fifo_recover(priv, engine, chan);
+ }
+ }
+}
+
+static void
+nve0_fifo_intr_sched(struct nve0_fifo_priv *priv)
+{
+ u32 intr = nv_rd32(priv, 0x00254c);
+ u32 code = intr & 0x000000ff;
+ const struct nouveau_enum *en;
+ char enunk[6] = "";
+
+ en = nouveau_enum_find(nve0_fifo_sched_reason, code);
+ if (!en)
+ snprintf(enunk, sizeof(enunk), "UNK%02x", code);
+
+ nv_error(priv, "SCHED_ERROR [ %s ]\n", en ? en->name : enunk);
+
+ switch (code) {
+ case 0x0a:
+ nve0_fifo_intr_sched_ctxsw(priv);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+nve0_fifo_intr_chsw(struct nve0_fifo_priv *priv)
+{
+ u32 stat = nv_rd32(priv, 0x00256c);
+ nv_error(priv, "CHSW_ERROR 0x%08x\n", stat);
+ nv_wr32(priv, 0x00256c, stat);
+}
+
+static void
+nve0_fifo_intr_dropped_fault(struct nve0_fifo_priv *priv)
+{
+ u32 stat = nv_rd32(priv, 0x00259c);
+ nv_error(priv, "DROPPED_MMU_FAULT 0x%08x\n", stat);
+}
+
+static const struct nouveau_enum
+nve0_fifo_fault_engine[] = {
{ 0x00, "GR", NULL, NVDEV_ENGINE_GR },
- { 0x03, "IFB" },
+ { 0x03, "IFB", NULL, NVDEV_ENGINE_IFB },
{ 0x04, "BAR1", NULL, NVDEV_SUBDEV_BAR },
{ 0x05, "BAR3", NULL, NVDEV_SUBDEV_INSTMEM },
{ 0x07, "PBDMA0", NULL, NVDEV_ENGINE_FIFO },
@@ -402,7 +619,8 @@ static const struct nouveau_enum nve0_fifo_fault_engine[] = {
{}
};
-static const struct nouveau_enum nve0_fifo_fault_reason[] = {
+static const struct nouveau_enum
+nve0_fifo_fault_reason[] = {
{ 0x00, "PDE" },
{ 0x01, "PDE_SIZE" },
{ 0x02, "PTE" },
@@ -422,7 +640,8 @@ static const struct nouveau_enum nve0_fifo_fault_reason[] = {
{}
};
-static const struct nouveau_enum nve0_fifo_fault_hubclient[] = {
+static const struct nouveau_enum
+nve0_fifo_fault_hubclient[] = {
{ 0x00, "VIP" },
{ 0x01, "CE0" },
{ 0x02, "CE1" },
@@ -458,7 +677,8 @@ static const struct nouveau_enum nve0_fifo_fault_hubclient[] = {
{}
};
-static const struct nouveau_enum nve0_fifo_fault_gpcclient[] = {
+static const struct nouveau_enum
+nve0_fifo_fault_gpcclient[] = {
{ 0x00, "L1_0" }, { 0x01, "T1_0" }, { 0x02, "PE_0" },
{ 0x03, "L1_1" }, { 0x04, "T1_1" }, { 0x05, "PE_1" },
{ 0x06, "L1_2" }, { 0x07, "T1_2" }, { 0x08, "PE_2" },
@@ -483,6 +703,82 @@ static const struct nouveau_enum nve0_fifo_fault_gpcclient[] = {
{}
};
+static void
+nve0_fifo_intr_fault(struct nve0_fifo_priv *priv, int unit)
+{
+ u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10));
+ u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10));
+ u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10));
+ u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10));
+ u32 gpc = (stat & 0x1f000000) >> 24;
+ u32 client = (stat & 0x00001f00) >> 8;
+ u32 write = (stat & 0x00000080);
+ u32 hub = (stat & 0x00000040);
+ u32 reason = (stat & 0x0000000f);
+ struct nouveau_object *engctx = NULL, *object;
+ struct nouveau_engine *engine = NULL;
+ const struct nouveau_enum *er, *eu, *ec;
+ char erunk[6] = "";
+ char euunk[6] = "";
+ char ecunk[6] = "";
+ char gpcid[3] = "";
+
+ er = nouveau_enum_find(nve0_fifo_fault_reason, reason);
+ if (!er)
+ snprintf(erunk, sizeof(erunk), "UNK%02X", reason);
+
+ eu = nouveau_enum_find(nve0_fifo_fault_engine, unit);
+ if (eu) {
+ switch (eu->data2) {
+ case NVDEV_SUBDEV_BAR:
+ nv_mask(priv, 0x001704, 0x00000000, 0x00000000);
+ break;
+ case NVDEV_SUBDEV_INSTMEM:
+ nv_mask(priv, 0x001714, 0x00000000, 0x00000000);
+ break;
+ case NVDEV_ENGINE_IFB:
+ nv_mask(priv, 0x001718, 0x00000000, 0x00000000);
+ break;
+ default:
+ engine = nouveau_engine(priv, eu->data2);
+ if (engine)
+ engctx = nouveau_engctx_get(engine, inst);
+ break;
+ }
+ } else {
+ snprintf(euunk, sizeof(euunk), "UNK%02x", unit);
+ }
+
+ if (hub) {
+ ec = nouveau_enum_find(nve0_fifo_fault_hubclient, client);
+ } else {
+ ec = nouveau_enum_find(nve0_fifo_fault_gpcclient, client);
+ snprintf(gpcid, sizeof(gpcid), "%d", gpc);
+ }
+
+ if (!ec)
+ snprintf(ecunk, sizeof(ecunk), "UNK%02x", client);
+
+ nv_error(priv, "%s fault at 0x%010llx [%s] from %s/%s%s%s%s on "
+ "channel 0x%010llx [%s]\n", write ? "write" : "read",
+ (u64)vahi << 32 | valo, er ? er->name : erunk,
+ eu ? eu->name : euunk, hub ? "" : "GPC", gpcid, hub ? "" : "/",
+ ec ? ec->name : ecunk, (u64)inst << 12,
+ nouveau_client_name(engctx));
+
+ object = engctx;
+ while (object) {
+ switch (nv_mclass(object)) {
+ case NVE0_CHANNEL_IND_CLASS:
+ nve0_fifo_recover(priv, engine, (void *)object);
+ break;
+ }
+ object = object->parent;
+ }
+
+ nouveau_engctx_put(engctx);
+}
+
static const struct nouveau_bitfield nve0_fifo_pbdma_intr[] = {
{ 0x00000001, "MEMREQ" },
{ 0x00000002, "MEMACK_TIMEOUT" },
@@ -518,104 +814,6 @@ static const struct nouveau_bitfield nve0_fifo_pbdma_intr[] = {
};
static void
-nve0_fifo_intr_sched(struct nve0_fifo_priv *priv)
-{
- u32 intr = nv_rd32(priv, 0x00254c);
- u32 code = intr & 0x000000ff;
- nv_error(priv, "SCHED_ERROR [");
- nouveau_enum_print(nve0_fifo_sched_reason, code);
- pr_cont("]\n");
-}
-
-static void
-nve0_fifo_intr_chsw(struct nve0_fifo_priv *priv)
-{
- u32 stat = nv_rd32(priv, 0x00256c);
- nv_error(priv, "CHSW_ERROR 0x%08x\n", stat);
- nv_wr32(priv, 0x00256c, stat);
-}
-
-static void
-nve0_fifo_intr_dropped_fault(struct nve0_fifo_priv *priv)
-{
- u32 stat = nv_rd32(priv, 0x00259c);
- nv_error(priv, "DROPPED_MMU_FAULT 0x%08x\n", stat);
-}
-
-static void
-nve0_fifo_intr_fault(struct nve0_fifo_priv *priv, int unit)
-{
- u32 inst = nv_rd32(priv, 0x2800 + (unit * 0x10));
- u32 valo = nv_rd32(priv, 0x2804 + (unit * 0x10));
- u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10));
- u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10));
- u32 client = (stat & 0x00001f00) >> 8;
- struct nouveau_engine *engine = NULL;
- struct nouveau_object *engctx = NULL;
- const struct nouveau_enum *en;
- const char *name = "unknown";
-
- nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ?
- "write" : "read", (u64)vahi << 32 | valo);
- nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f);
- pr_cont("] from ");
- en = nouveau_enum_print(nve0_fifo_fault_engine, unit);
- if (stat & 0x00000040) {
- pr_cont("/");
- nouveau_enum_print(nve0_fifo_fault_hubclient, client);
- } else {
- pr_cont("/GPC%d/", (stat & 0x1f000000) >> 24);
- nouveau_enum_print(nve0_fifo_fault_gpcclient, client);
- }
-
- if (en && en->data2) {
- if (en->data2 == NVDEV_SUBDEV_BAR) {
- nv_mask(priv, 0x001704, 0x00000000, 0x00000000);
- name = "BAR1";
- } else
- if (en->data2 == NVDEV_SUBDEV_INSTMEM) {
- nv_mask(priv, 0x001714, 0x00000000, 0x00000000);
- name = "BAR3";
- } else {
- engine = nouveau_engine(priv, en->data2);
- if (engine) {
- engctx = nouveau_engctx_get(engine, inst);
- name = nouveau_client_name(engctx);
- }
- }
- }
- pr_cont(" on channel 0x%010llx [%s]\n", (u64)inst << 12, name);
-
- nouveau_engctx_put(engctx);
-}
-
-static int
-nve0_fifo_swmthd(struct nve0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
-{
- struct nve0_fifo_chan *chan = NULL;
- struct nouveau_handle *bind;
- unsigned long flags;
- int ret = -EINVAL;
-
- spin_lock_irqsave(&priv->base.lock, flags);
- if (likely(chid >= priv->base.min && chid <= priv->base.max))
- chan = (void *)priv->base.channel[chid];
- if (unlikely(!chan))
- goto out;
-
- bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
- if (likely(bind)) {
- if (!mthd || !nv_call(bind->object, mthd, data))
- ret = 0;
- nouveau_namedb_put(bind);
- }
-
-out:
- spin_unlock_irqrestore(&priv->base.lock, flags);
- return ret;
-}
-
-static void
nve0_fifo_intr_pbdma(struct nve0_fifo_priv *priv, int unit)
{
u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000));
@@ -647,6 +845,24 @@ nve0_fifo_intr_pbdma(struct nve0_fifo_priv *priv, int unit)
}
static void
+nve0_fifo_intr_runlist(struct nve0_fifo_priv *priv)
+{
+ u32 mask = nv_rd32(priv, 0x002a00);
+ while (mask) {
+ u32 engn = __ffs(mask);
+ wake_up(&priv->engine[engn].wait);
+ nv_wr32(priv, 0x002a00, 1 << engn);
+ mask &= ~(1 << engn);
+ }
+}
+
+static void
+nve0_fifo_intr_engine(struct nve0_fifo_priv *priv)
+{
+ nouveau_event_trigger(priv->base.uevent, 0);
+}
+
+static void
nve0_fifo_intr(struct nouveau_subdev *subdev)
{
struct nve0_fifo_priv *priv = (void *)subdev;
@@ -654,8 +870,7 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
u32 stat = nv_rd32(priv, 0x002100) & mask;
if (stat & 0x00000001) {
- u32 stat = nv_rd32(priv, 0x00252c);
- nv_error(priv, "BIND_ERROR 0x%08x\n", stat);
+ nve0_fifo_intr_bind(priv);
nv_wr32(priv, 0x002100, 0x00000001);
stat &= ~0x00000001;
}
@@ -697,55 +912,42 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
}
if (stat & 0x10000000) {
- u32 units = nv_rd32(priv, 0x00259c);
- u32 u = units;
-
- while (u) {
- int i = ffs(u) - 1;
- nve0_fifo_intr_fault(priv, i);
- u &= ~(1 << i);
+ u32 mask = nv_rd32(priv, 0x00259c);
+ while (mask) {
+ u32 unit = __ffs(mask);
+ nve0_fifo_intr_fault(priv, unit);
+ nv_wr32(priv, 0x00259c, (1 << unit));
+ mask &= ~(1 << unit);
}
-
- nv_wr32(priv, 0x00259c, units);
stat &= ~0x10000000;
}
if (stat & 0x20000000) {
u32 mask = nv_rd32(priv, 0x0025a0);
- u32 temp = mask;
-
- while (temp) {
- u32 unit = ffs(temp) - 1;
+ while (mask) {
+ u32 unit = __ffs(mask);
nve0_fifo_intr_pbdma(priv, unit);
- temp &= ~(1 << unit);
+ nv_wr32(priv, 0x0025a0, (1 << unit));
+ mask &= ~(1 << unit);
}
-
- nv_wr32(priv, 0x0025a0, mask);
stat &= ~0x20000000;
}
if (stat & 0x40000000) {
- u32 mask = nv_mask(priv, 0x002a00, 0x00000000, 0x00000000);
-
- while (mask) {
- u32 engn = ffs(mask) - 1;
- /* runlist event, not currently used */
- mask &= ~(1 << engn);
- }
-
+ nve0_fifo_intr_runlist(priv);
stat &= ~0x40000000;
}
if (stat & 0x80000000) {
- nouveau_event_trigger(priv->base.uevent, 0);
+ nve0_fifo_intr_engine(priv);
nv_wr32(priv, 0x002100, 0x80000000);
stat &= ~0x80000000;
}
if (stat) {
- nv_fatal(priv, "unhandled status 0x%08x\n", stat);
+ nv_error(priv, "INTR 0x%08x\n", stat);
+ nv_mask(priv, 0x002140, stat, 0x00000000);
nv_wr32(priv, 0x002100, stat);
- nv_wr32(priv, 0x002140, 0);
}
}
@@ -802,9 +1004,8 @@ nve0_fifo_init(struct nouveau_object *object)
nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
- nv_wr32(priv, 0x002a00, 0xffffffff);
nv_wr32(priv, 0x002100, 0xffffffff);
- nv_wr32(priv, 0x002140, 0x3fffffff);
+ nv_wr32(priv, 0x002140, 0x7fffffff);
return 0;
}
@@ -840,6 +1041,8 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ INIT_WORK(&priv->fault, nve0_fifo_recover_work);
+
for (i = 0; i < FIFO_ENGINE_NR; i++) {
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000,
0, &priv->engine[i].runlist[0]);
@@ -850,10 +1053,12 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
0, &priv->engine[i].runlist[1]);
if (ret)
return ret;
+
+ init_waitqueue_head(&priv->engine[i].wait);
}
- ret = nouveau_gpuobj_new(nv_object(priv), NULL, 4096 * 0x200, 0x1000,
- NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem);
+ ret = nouveau_gpuobj_new(nv_object(priv), NULL, impl->channels * 0x200,
+ 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c
new file mode 100644
index 000000000000..1dc37b1ddbfa
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c
@@ -0,0 +1,989 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "ctxnvc0.h"
+
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+gm107_grctx_init_icmd_0[] = {
+ { 0x001000, 1, 0x01, 0x00000004 },
+ { 0x000039, 3, 0x01, 0x00000000 },
+ { 0x0000a9, 1, 0x01, 0x0000ffff },
+ { 0x000038, 1, 0x01, 0x0fac6881 },
+ { 0x00003d, 1, 0x01, 0x00000001 },
+ { 0x0000e8, 8, 0x01, 0x00000400 },
+ { 0x000078, 8, 0x01, 0x00000300 },
+ { 0x000050, 1, 0x01, 0x00000011 },
+ { 0x000058, 8, 0x01, 0x00000008 },
+ { 0x000208, 8, 0x01, 0x00000001 },
+ { 0x000081, 1, 0x01, 0x00000001 },
+ { 0x000085, 1, 0x01, 0x00000004 },
+ { 0x000088, 1, 0x01, 0x00000400 },
+ { 0x000090, 1, 0x01, 0x00000300 },
+ { 0x000098, 1, 0x01, 0x00001001 },
+ { 0x0000e3, 1, 0x01, 0x00000001 },
+ { 0x0000da, 1, 0x01, 0x00000001 },
+ { 0x0000f8, 1, 0x01, 0x00000003 },
+ { 0x0000fa, 1, 0x01, 0x00000001 },
+ { 0x0000b1, 2, 0x01, 0x00000001 },
+ { 0x00009f, 4, 0x01, 0x0000ffff },
+ { 0x0000a8, 1, 0x01, 0x0000ffff },
+ { 0x0000ad, 1, 0x01, 0x0000013e },
+ { 0x0000e1, 1, 0x01, 0x00000010 },
+ { 0x000290, 16, 0x01, 0x00000000 },
+ { 0x0003b0, 16, 0x01, 0x00000000 },
+ { 0x0002a0, 16, 0x01, 0x00000000 },
+ { 0x000420, 16, 0x01, 0x00000000 },
+ { 0x0002b0, 16, 0x01, 0x00000000 },
+ { 0x000430, 16, 0x01, 0x00000000 },
+ { 0x0002c0, 16, 0x01, 0x00000000 },
+ { 0x0004d0, 16, 0x01, 0x00000000 },
+ { 0x000720, 16, 0x01, 0x00000000 },
+ { 0x0008c0, 16, 0x01, 0x00000000 },
+ { 0x000890, 16, 0x01, 0x00000000 },
+ { 0x0008e0, 16, 0x01, 0x00000000 },
+ { 0x0008a0, 16, 0x01, 0x00000000 },
+ { 0x0008f0, 16, 0x01, 0x00000000 },
+ { 0x00094c, 1, 0x01, 0x000000ff },
+ { 0x00094d, 1, 0x01, 0xffffffff },
+ { 0x00094e, 1, 0x01, 0x00000002 },
+ { 0x0002f2, 2, 0x01, 0x00000001 },
+ { 0x0002f5, 1, 0x01, 0x00000001 },
+ { 0x0002f7, 1, 0x01, 0x00000001 },
+ { 0x000303, 1, 0x01, 0x00000001 },
+ { 0x0002e6, 1, 0x01, 0x00000001 },
+ { 0x000466, 1, 0x01, 0x00000052 },
+ { 0x000301, 1, 0x01, 0x3f800000 },
+ { 0x000304, 1, 0x01, 0x30201000 },
+ { 0x000305, 1, 0x01, 0x70605040 },
+ { 0x000306, 1, 0x01, 0xb8a89888 },
+ { 0x000307, 1, 0x01, 0xf8e8d8c8 },
+ { 0x00030a, 1, 0x01, 0x00ffff00 },
+ { 0x0000de, 1, 0x01, 0x00000001 },
+ { 0x00030b, 1, 0x01, 0x0000001a },
+ { 0x00030c, 1, 0x01, 0x00000001 },
+ { 0x000318, 1, 0x01, 0x00000001 },
+ { 0x000340, 1, 0x01, 0x00000000 },
+ { 0x00037d, 1, 0x01, 0x00000006 },
+ { 0x0003a0, 1, 0x01, 0x00000002 },
+ { 0x0003aa, 1, 0x01, 0x00000001 },
+ { 0x0003a9, 1, 0x01, 0x00000001 },
+ { 0x000380, 1, 0x01, 0x00000001 },
+ { 0x000383, 1, 0x01, 0x00000011 },
+ { 0x000360, 1, 0x01, 0x00000040 },
+ { 0x000366, 2, 0x01, 0x00000000 },
+ { 0x000368, 1, 0x01, 0x00000fff },
+ { 0x000370, 2, 0x01, 0x00000000 },
+ { 0x000372, 1, 0x01, 0x000fffff },
+ { 0x00037a, 1, 0x01, 0x00000012 },
+ { 0x000619, 1, 0x01, 0x00000003 },
+ { 0x000811, 1, 0x01, 0x00000003 },
+ { 0x000812, 1, 0x01, 0x00000004 },
+ { 0x000813, 1, 0x01, 0x00000006 },
+ { 0x000814, 1, 0x01, 0x00000008 },
+ { 0x000815, 1, 0x01, 0x0000000b },
+ { 0x000800, 6, 0x01, 0x00000001 },
+ { 0x000632, 1, 0x01, 0x00000001 },
+ { 0x000633, 1, 0x01, 0x00000002 },
+ { 0x000634, 1, 0x01, 0x00000003 },
+ { 0x000635, 1, 0x01, 0x00000004 },
+ { 0x000654, 1, 0x01, 0x3f800000 },
+ { 0x000657, 1, 0x01, 0x3f800000 },
+ { 0x000655, 2, 0x01, 0x3f800000 },
+ { 0x0006cd, 1, 0x01, 0x3f800000 },
+ { 0x0007f5, 1, 0x01, 0x3f800000 },
+ { 0x0007dc, 1, 0x01, 0x39291909 },
+ { 0x0007dd, 1, 0x01, 0x79695949 },
+ { 0x0007de, 1, 0x01, 0xb9a99989 },
+ { 0x0007df, 1, 0x01, 0xf9e9d9c9 },
+ { 0x0007e8, 1, 0x01, 0x00003210 },
+ { 0x0007e9, 1, 0x01, 0x00007654 },
+ { 0x0007ea, 1, 0x01, 0x00000098 },
+ { 0x0007ec, 1, 0x01, 0x39291909 },
+ { 0x0007ed, 1, 0x01, 0x79695949 },
+ { 0x0007ee, 1, 0x01, 0xb9a99989 },
+ { 0x0007ef, 1, 0x01, 0xf9e9d9c9 },
+ { 0x0007f0, 1, 0x01, 0x00003210 },
+ { 0x0007f1, 1, 0x01, 0x00007654 },
+ { 0x0007f2, 1, 0x01, 0x00000098 },
+ { 0x0005a5, 1, 0x01, 0x00000001 },
+ { 0x0005d0, 1, 0x01, 0x20181008 },
+ { 0x0005d1, 1, 0x01, 0x40383028 },
+ { 0x0005d2, 1, 0x01, 0x60585048 },
+ { 0x0005d3, 1, 0x01, 0x80787068 },
+ { 0x000980, 128, 0x01, 0x00000000 },
+ { 0x000468, 1, 0x01, 0x00000004 },
+ { 0x00046c, 1, 0x01, 0x00000001 },
+ { 0x000470, 96, 0x01, 0x00000000 },
+ { 0x000510, 16, 0x01, 0x3f800000 },
+ { 0x000520, 1, 0x01, 0x000002b6 },
+ { 0x000529, 1, 0x01, 0x00000001 },
+ { 0x000530, 16, 0x01, 0xffff0000 },
+ { 0x000550, 32, 0x01, 0xffff0000 },
+ { 0x000585, 1, 0x01, 0x0000003f },
+ { 0x000576, 1, 0x01, 0x00000003 },
+ { 0x00057b, 1, 0x01, 0x00000059 },
+ { 0x000586, 1, 0x01, 0x00000040 },
+ { 0x000582, 2, 0x01, 0x00000080 },
+ { 0x000595, 1, 0x01, 0x00400040 },
+ { 0x000596, 1, 0x01, 0x00000492 },
+ { 0x000597, 1, 0x01, 0x08080203 },
+ { 0x0005ad, 1, 0x01, 0x00000008 },
+ { 0x000598, 1, 0x01, 0x00020001 },
+ { 0x0005c2, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
+ { 0x00063a, 1, 0x01, 0x00000002 },
+ { 0x00063b, 2, 0x01, 0x00000001 },
+ { 0x00063d, 1, 0x01, 0x00000002 },
+ { 0x00063e, 1, 0x01, 0x00000001 },
+ { 0x0008b8, 8, 0x01, 0x00000001 },
+ { 0x000900, 8, 0x01, 0x00000001 },
+ { 0x000908, 8, 0x01, 0x00000002 },
+ { 0x000910, 16, 0x01, 0x00000001 },
+ { 0x000920, 8, 0x01, 0x00000002 },
+ { 0x000928, 8, 0x01, 0x00000001 },
+ { 0x000662, 1, 0x01, 0x00000001 },
+ { 0x000648, 9, 0x01, 0x00000001 },
+ { 0x000658, 1, 0x01, 0x0000000f },
+ { 0x0007ff, 1, 0x01, 0x0000000a },
+ { 0x00066a, 1, 0x01, 0x40000000 },
+ { 0x00066b, 1, 0x01, 0x10000000 },
+ { 0x00066c, 2, 0x01, 0xffff0000 },
+ { 0x0007af, 2, 0x01, 0x00000008 },
+ { 0x0007f6, 1, 0x01, 0x00000001 },
+ { 0x0006b2, 1, 0x01, 0x00000055 },
+ { 0x0007ad, 1, 0x01, 0x00000003 },
+ { 0x000971, 1, 0x01, 0x00000008 },
+ { 0x000972, 1, 0x01, 0x00000040 },
+ { 0x000973, 1, 0x01, 0x0000012c },
+ { 0x00097c, 1, 0x01, 0x00000040 },
+ { 0x000975, 1, 0x01, 0x00000020 },
+ { 0x000976, 1, 0x01, 0x00000001 },
+ { 0x000977, 1, 0x01, 0x00000020 },
+ { 0x000978, 1, 0x01, 0x00000001 },
+ { 0x000957, 1, 0x01, 0x00000003 },
+ { 0x00095e, 1, 0x01, 0x20164010 },
+ { 0x00095f, 1, 0x01, 0x00000020 },
+ { 0x000a0d, 1, 0x01, 0x00000006 },
+ { 0x00097d, 1, 0x01, 0x0000000c },
+ { 0x000683, 1, 0x01, 0x00000006 },
+ { 0x000687, 1, 0x01, 0x003fffff },
+ { 0x0006a0, 1, 0x01, 0x00000005 },
+ { 0x000840, 1, 0x01, 0x00400008 },
+ { 0x000841, 1, 0x01, 0x08000080 },
+ { 0x000842, 1, 0x01, 0x00400008 },
+ { 0x000843, 1, 0x01, 0x08000080 },
+ { 0x000818, 8, 0x01, 0x00000000 },
+ { 0x000848, 16, 0x01, 0x00000000 },
+ { 0x000738, 1, 0x01, 0x00000000 },
+ { 0x0006aa, 1, 0x01, 0x00000001 },
+ { 0x0006ab, 1, 0x01, 0x00000002 },
+ { 0x0006ac, 1, 0x01, 0x00000080 },
+ { 0x0006ad, 2, 0x01, 0x00000100 },
+ { 0x0006b1, 1, 0x01, 0x00000011 },
+ { 0x0006bb, 1, 0x01, 0x000000cf },
+ { 0x0006ce, 1, 0x01, 0x2a712488 },
+ { 0x000739, 1, 0x01, 0x4085c000 },
+ { 0x00073a, 1, 0x01, 0x00000080 },
+ { 0x000786, 1, 0x01, 0x80000100 },
+ { 0x00073c, 1, 0x01, 0x00010100 },
+ { 0x00073d, 1, 0x01, 0x02800000 },
+ { 0x000787, 1, 0x01, 0x000000cf },
+ { 0x00078c, 1, 0x01, 0x00000008 },
+ { 0x000792, 1, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
+ { 0x000797, 1, 0x01, 0x000000cf },
+ { 0x000836, 1, 0x01, 0x00000001 },
+ { 0x00079a, 1, 0x01, 0x00000002 },
+ { 0x000833, 1, 0x01, 0x04444480 },
+ { 0x0007a1, 1, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
+ { 0x000831, 1, 0x01, 0x00000004 },
+ { 0x000b07, 1, 0x01, 0x00000002 },
+ { 0x000b08, 2, 0x01, 0x00000100 },
+ { 0x000b0a, 1, 0x01, 0x00000001 },
+ { 0x000a04, 1, 0x01, 0x000000ff },
+ { 0x000a0b, 1, 0x01, 0x00000040 },
+ { 0x00097f, 1, 0x01, 0x00000100 },
+ { 0x000a02, 1, 0x01, 0x00000001 },
+ { 0x000809, 1, 0x01, 0x00000007 },
+ { 0x00c221, 1, 0x01, 0x00000040 },
+ { 0x00c1b0, 8, 0x01, 0x0000000f },
+ { 0x00c1b8, 1, 0x01, 0x0fac6881 },
+ { 0x00c1b9, 1, 0x01, 0x00fac688 },
+ { 0x00c401, 1, 0x01, 0x00000001 },
+ { 0x00c402, 1, 0x01, 0x00010001 },
+ { 0x00c403, 2, 0x01, 0x00000001 },
+ { 0x00c40e, 1, 0x01, 0x00000020 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ { 0x001000, 1, 0x01, 0x00000002 },
+ { 0x0006aa, 1, 0x01, 0x00000001 },
+ { 0x0006ad, 2, 0x01, 0x00000100 },
+ { 0x0006b1, 1, 0x01, 0x00000011 },
+ { 0x00078c, 1, 0x01, 0x00000008 },
+ { 0x000792, 1, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
+ { 0x000797, 1, 0x01, 0x000000cf },
+ { 0x00079a, 1, 0x01, 0x00000002 },
+ { 0x0007a1, 1, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
+ { 0x000831, 1, 0x01, 0x00000004 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ { 0x001000, 1, 0x01, 0x00000008 },
+ { 0x000039, 3, 0x01, 0x00000000 },
+ { 0x000380, 1, 0x01, 0x00000001 },
+ { 0x000366, 2, 0x01, 0x00000000 },
+ { 0x000368, 1, 0x01, 0x00000fff },
+ { 0x000370, 2, 0x01, 0x00000000 },
+ { 0x000372, 1, 0x01, 0x000fffff },
+ { 0x000813, 1, 0x01, 0x00000006 },
+ { 0x000814, 1, 0x01, 0x00000008 },
+ { 0x000818, 8, 0x01, 0x00000000 },
+ { 0x000848, 16, 0x01, 0x00000000 },
+ { 0x000738, 1, 0x01, 0x00000000 },
+ { 0x000b07, 1, 0x01, 0x00000002 },
+ { 0x000b08, 2, 0x01, 0x00000100 },
+ { 0x000b0a, 1, 0x01, 0x00000001 },
+ { 0x000a04, 1, 0x01, 0x000000ff },
+ { 0x000a0b, 1, 0x01, 0x00000040 },
+ { 0x00097f, 1, 0x01, 0x00000100 },
+ { 0x000a02, 1, 0x01, 0x00000001 },
+ { 0x000809, 1, 0x01, 0x00000007 },
+ { 0x00c221, 1, 0x01, 0x00000040 },
+ { 0x00c401, 1, 0x01, 0x00000001 },
+ { 0x00c402, 1, 0x01, 0x00010001 },
+ { 0x00c403, 2, 0x01, 0x00000001 },
+ { 0x00c40e, 1, 0x01, 0x00000020 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ { 0x001000, 1, 0x01, 0x00000001 },
+ { 0x000b07, 1, 0x01, 0x00000002 },
+ { 0x000b08, 2, 0x01, 0x00000100 },
+ { 0x000b0a, 1, 0x01, 0x00000001 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_grctx_pack_icmd[] = {
+ { gm107_grctx_init_icmd_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_b097_0[] = {
+ { 0x000800, 8, 0x40, 0x00000000 },
+ { 0x000804, 8, 0x40, 0x00000000 },
+ { 0x000808, 8, 0x40, 0x00000400 },
+ { 0x00080c, 8, 0x40, 0x00000300 },
+ { 0x000810, 1, 0x04, 0x000000cf },
+ { 0x000850, 7, 0x40, 0x00000000 },
+ { 0x000814, 8, 0x40, 0x00000040 },
+ { 0x000818, 8, 0x40, 0x00000001 },
+ { 0x00081c, 8, 0x40, 0x00000000 },
+ { 0x000820, 8, 0x40, 0x00000000 },
+ { 0x001c00, 16, 0x10, 0x00000000 },
+ { 0x001c04, 16, 0x10, 0x00000000 },
+ { 0x001c08, 16, 0x10, 0x00000000 },
+ { 0x001c0c, 16, 0x10, 0x00000000 },
+ { 0x001d00, 16, 0x10, 0x00000000 },
+ { 0x001d04, 16, 0x10, 0x00000000 },
+ { 0x001d08, 16, 0x10, 0x00000000 },
+ { 0x001d0c, 16, 0x10, 0x00000000 },
+ { 0x001f00, 16, 0x08, 0x00000000 },
+ { 0x001f04, 16, 0x08, 0x00000000 },
+ { 0x001f80, 16, 0x08, 0x00000000 },
+ { 0x001f84, 16, 0x08, 0x00000000 },
+ { 0x002000, 1, 0x04, 0x00000000 },
+ { 0x002040, 1, 0x04, 0x00000011 },
+ { 0x002080, 1, 0x04, 0x00000020 },
+ { 0x0020c0, 1, 0x04, 0x00000030 },
+ { 0x002100, 1, 0x04, 0x00000040 },
+ { 0x002140, 1, 0x04, 0x00000051 },
+ { 0x00200c, 6, 0x40, 0x00000001 },
+ { 0x002010, 1, 0x04, 0x00000000 },
+ { 0x002050, 1, 0x04, 0x00000000 },
+ { 0x002090, 1, 0x04, 0x00000001 },
+ { 0x0020d0, 1, 0x04, 0x00000002 },
+ { 0x002110, 1, 0x04, 0x00000003 },
+ { 0x002150, 1, 0x04, 0x00000004 },
+ { 0x000380, 4, 0x20, 0x00000000 },
+ { 0x000384, 4, 0x20, 0x00000000 },
+ { 0x000388, 4, 0x20, 0x00000000 },
+ { 0x00038c, 4, 0x20, 0x00000000 },
+ { 0x000700, 4, 0x10, 0x00000000 },
+ { 0x000704, 4, 0x10, 0x00000000 },
+ { 0x000708, 4, 0x10, 0x00000000 },
+ { 0x002800, 128, 0x04, 0x00000000 },
+ { 0x000a00, 16, 0x20, 0x00000000 },
+ { 0x000a04, 16, 0x20, 0x00000000 },
+ { 0x000a08, 16, 0x20, 0x00000000 },
+ { 0x000a0c, 16, 0x20, 0x00000000 },
+ { 0x000a10, 16, 0x20, 0x00000000 },
+ { 0x000a14, 16, 0x20, 0x00000000 },
+ { 0x000c00, 16, 0x10, 0x00000000 },
+ { 0x000c04, 16, 0x10, 0x00000000 },
+ { 0x000c08, 16, 0x10, 0x00000000 },
+ { 0x000c0c, 16, 0x10, 0x3f800000 },
+ { 0x000d00, 8, 0x08, 0xffff0000 },
+ { 0x000d04, 8, 0x08, 0xffff0000 },
+ { 0x000e00, 16, 0x10, 0x00000000 },
+ { 0x000e04, 16, 0x10, 0xffff0000 },
+ { 0x000e08, 16, 0x10, 0xffff0000 },
+ { 0x000d40, 4, 0x08, 0x00000000 },
+ { 0x000d44, 4, 0x08, 0x00000000 },
+ { 0x001e00, 8, 0x20, 0x00000001 },
+ { 0x001e04, 8, 0x20, 0x00000001 },
+ { 0x001e08, 8, 0x20, 0x00000002 },
+ { 0x001e0c, 8, 0x20, 0x00000001 },
+ { 0x001e10, 8, 0x20, 0x00000001 },
+ { 0x001e14, 8, 0x20, 0x00000002 },
+ { 0x001e18, 8, 0x20, 0x00000001 },
+ { 0x001480, 8, 0x10, 0x00000000 },
+ { 0x001484, 8, 0x10, 0x00000000 },
+ { 0x001488, 8, 0x10, 0x00000000 },
+ { 0x003400, 128, 0x04, 0x00000000 },
+ { 0x00030c, 1, 0x04, 0x00000001 },
+ { 0x001944, 1, 0x04, 0x00000000 },
+ { 0x001514, 1, 0x04, 0x00000000 },
+ { 0x000d68, 1, 0x04, 0x0000ffff },
+ { 0x00121c, 1, 0x04, 0x0fac6881 },
+ { 0x000fac, 1, 0x04, 0x00000001 },
+ { 0x001538, 1, 0x04, 0x00000001 },
+ { 0x000fe0, 2, 0x04, 0x00000000 },
+ { 0x000fe8, 1, 0x04, 0x00000014 },
+ { 0x000fec, 1, 0x04, 0x00000040 },
+ { 0x000ff0, 1, 0x04, 0x00000000 },
+ { 0x00179c, 1, 0x04, 0x00000000 },
+ { 0x001228, 1, 0x04, 0x00000400 },
+ { 0x00122c, 1, 0x04, 0x00000300 },
+ { 0x001230, 1, 0x04, 0x00010001 },
+ { 0x0007f8, 1, 0x04, 0x00000000 },
+ { 0x0015b4, 1, 0x04, 0x00000001 },
+ { 0x0015cc, 1, 0x04, 0x00000000 },
+ { 0x001534, 1, 0x04, 0x00000000 },
+ { 0x000754, 1, 0x04, 0x00000001 },
+ { 0x000fb0, 1, 0x04, 0x00000000 },
+ { 0x0015d0, 1, 0x04, 0x00000000 },
+ { 0x00153c, 1, 0x04, 0x00000000 },
+ { 0x0016b4, 1, 0x04, 0x00000003 },
+ { 0x000fbc, 4, 0x04, 0x0000ffff },
+ { 0x000df8, 2, 0x04, 0x00000000 },
+ { 0x001948, 1, 0x04, 0x00000000 },
+ { 0x001970, 1, 0x04, 0x00000001 },
+ { 0x00161c, 1, 0x04, 0x000009f0 },
+ { 0x000dcc, 1, 0x04, 0x00000010 },
+ { 0x0015e4, 1, 0x04, 0x00000000 },
+ { 0x001160, 32, 0x04, 0x25e00040 },
+ { 0x001880, 32, 0x04, 0x00000000 },
+ { 0x000f84, 2, 0x04, 0x00000000 },
+ { 0x0017c8, 2, 0x04, 0x00000000 },
+ { 0x0017d0, 1, 0x04, 0x000000ff },
+ { 0x0017d4, 1, 0x04, 0xffffffff },
+ { 0x0017d8, 1, 0x04, 0x00000002 },
+ { 0x0017dc, 1, 0x04, 0x00000000 },
+ { 0x0015f4, 2, 0x04, 0x00000000 },
+ { 0x001434, 2, 0x04, 0x00000000 },
+ { 0x000d74, 1, 0x04, 0x00000000 },
+ { 0x0013a4, 1, 0x04, 0x00000000 },
+ { 0x001318, 1, 0x04, 0x00000001 },
+ { 0x001080, 2, 0x04, 0x00000000 },
+ { 0x001088, 2, 0x04, 0x00000001 },
+ { 0x001090, 1, 0x04, 0x00000000 },
+ { 0x001094, 1, 0x04, 0x00000001 },
+ { 0x001098, 1, 0x04, 0x00000000 },
+ { 0x00109c, 1, 0x04, 0x00000001 },
+ { 0x0010a0, 2, 0x04, 0x00000000 },
+ { 0x001644, 1, 0x04, 0x00000000 },
+ { 0x000748, 1, 0x04, 0x00000000 },
+ { 0x000de8, 1, 0x04, 0x00000000 },
+ { 0x001648, 1, 0x04, 0x00000000 },
+ { 0x0012a4, 1, 0x04, 0x00000000 },
+ { 0x001120, 4, 0x04, 0x00000000 },
+ { 0x001118, 1, 0x04, 0x00000000 },
+ { 0x00164c, 1, 0x04, 0x00000000 },
+ { 0x001658, 1, 0x04, 0x00000000 },
+ { 0x001910, 1, 0x04, 0x00000290 },
+ { 0x001518, 1, 0x04, 0x00000000 },
+ { 0x00165c, 1, 0x04, 0x00000001 },
+ { 0x001520, 1, 0x04, 0x00000000 },
+ { 0x001604, 1, 0x04, 0x00000000 },
+ { 0x001570, 1, 0x04, 0x00000000 },
+ { 0x0013b0, 2, 0x04, 0x3f800000 },
+ { 0x00020c, 1, 0x04, 0x00000000 },
+ { 0x001670, 1, 0x04, 0x30201000 },
+ { 0x001674, 1, 0x04, 0x70605040 },
+ { 0x001678, 1, 0x04, 0xb8a89888 },
+ { 0x00167c, 1, 0x04, 0xf8e8d8c8 },
+ { 0x00166c, 1, 0x04, 0x00000000 },
+ { 0x001680, 1, 0x04, 0x00ffff00 },
+ { 0x0012d0, 1, 0x04, 0x00000003 },
+ { 0x0012d4, 1, 0x04, 0x00000002 },
+ { 0x001684, 2, 0x04, 0x00000000 },
+ { 0x000dac, 2, 0x04, 0x00001b02 },
+ { 0x000db4, 1, 0x04, 0x00000000 },
+ { 0x00168c, 1, 0x04, 0x00000000 },
+ { 0x0015bc, 1, 0x04, 0x00000000 },
+ { 0x00156c, 1, 0x04, 0x00000000 },
+ { 0x00187c, 1, 0x04, 0x00000000 },
+ { 0x001110, 1, 0x04, 0x00000001 },
+ { 0x000dc0, 3, 0x04, 0x00000000 },
+ { 0x000f40, 5, 0x04, 0x00000000 },
+ { 0x001234, 1, 0x04, 0x00000000 },
+ { 0x001690, 1, 0x04, 0x00000000 },
+ { 0x000790, 5, 0x04, 0x00000000 },
+ { 0x00077c, 1, 0x04, 0x00000000 },
+ { 0x001000, 1, 0x04, 0x00000010 },
+ { 0x0010fc, 1, 0x04, 0x00000000 },
+ { 0x001290, 1, 0x04, 0x00000000 },
+ { 0x000218, 1, 0x04, 0x00000010 },
+ { 0x0012d8, 1, 0x04, 0x00000000 },
+ { 0x0012dc, 1, 0x04, 0x00000010 },
+ { 0x000d94, 1, 0x04, 0x00000001 },
+ { 0x00155c, 2, 0x04, 0x00000000 },
+ { 0x001564, 1, 0x04, 0x00000fff },
+ { 0x001574, 2, 0x04, 0x00000000 },
+ { 0x00157c, 1, 0x04, 0x000fffff },
+ { 0x001354, 1, 0x04, 0x00000000 },
+ { 0x001610, 1, 0x04, 0x00000012 },
+ { 0x001608, 2, 0x04, 0x00000000 },
+ { 0x00260c, 1, 0x04, 0x00000000 },
+ { 0x0007ac, 1, 0x04, 0x00000000 },
+ { 0x00162c, 1, 0x04, 0x00000003 },
+ { 0x000210, 1, 0x04, 0x00000000 },
+ { 0x000320, 1, 0x04, 0x00000000 },
+ { 0x000324, 6, 0x04, 0x3f800000 },
+ { 0x000750, 1, 0x04, 0x00000000 },
+ { 0x000760, 1, 0x04, 0x39291909 },
+ { 0x000764, 1, 0x04, 0x79695949 },
+ { 0x000768, 1, 0x04, 0xb9a99989 },
+ { 0x00076c, 1, 0x04, 0xf9e9d9c9 },
+ { 0x000770, 1, 0x04, 0x30201000 },
+ { 0x000774, 1, 0x04, 0x70605040 },
+ { 0x000778, 1, 0x04, 0x00009080 },
+ { 0x000780, 1, 0x04, 0x39291909 },
+ { 0x000784, 1, 0x04, 0x79695949 },
+ { 0x000788, 1, 0x04, 0xb9a99989 },
+ { 0x00078c, 1, 0x04, 0xf9e9d9c9 },
+ { 0x0007d0, 1, 0x04, 0x30201000 },
+ { 0x0007d4, 1, 0x04, 0x70605040 },
+ { 0x0007d8, 1, 0x04, 0x00009080 },
+ { 0x00037c, 1, 0x04, 0x00000001 },
+ { 0x000740, 2, 0x04, 0x00000000 },
+ { 0x002600, 1, 0x04, 0x00000000 },
+ { 0x001918, 1, 0x04, 0x00000000 },
+ { 0x00191c, 1, 0x04, 0x00000900 },
+ { 0x001920, 1, 0x04, 0x00000405 },
+ { 0x001308, 1, 0x04, 0x00000001 },
+ { 0x001924, 1, 0x04, 0x00000000 },
+ { 0x0013ac, 1, 0x04, 0x00000000 },
+ { 0x00192c, 1, 0x04, 0x00000001 },
+ { 0x00193c, 1, 0x04, 0x00002c1c },
+ { 0x000d7c, 1, 0x04, 0x00000000 },
+ { 0x000f8c, 1, 0x04, 0x00000000 },
+ { 0x0002c0, 1, 0x04, 0x00000001 },
+ { 0x001510, 1, 0x04, 0x00000000 },
+ { 0x001940, 1, 0x04, 0x00000000 },
+ { 0x000ff4, 2, 0x04, 0x00000000 },
+ { 0x00194c, 2, 0x04, 0x00000000 },
+ { 0x001968, 1, 0x04, 0x00000000 },
+ { 0x001590, 1, 0x04, 0x0000003f },
+ { 0x0007e8, 4, 0x04, 0x00000000 },
+ { 0x00196c, 1, 0x04, 0x00000011 },
+ { 0x0002e4, 1, 0x04, 0x0000b001 },
+ { 0x00036c, 2, 0x04, 0x00000000 },
+ { 0x00197c, 1, 0x04, 0x00000000 },
+ { 0x000fcc, 2, 0x04, 0x00000000 },
+ { 0x0002d8, 1, 0x04, 0x00000040 },
+ { 0x001980, 1, 0x04, 0x00000080 },
+ { 0x001504, 1, 0x04, 0x00000080 },
+ { 0x001984, 1, 0x04, 0x00000000 },
+ { 0x000f60, 1, 0x04, 0x00000000 },
+ { 0x000f64, 1, 0x04, 0x00400040 },
+ { 0x000f68, 1, 0x04, 0x00002212 },
+ { 0x000f6c, 1, 0x04, 0x08080203 },
+ { 0x001108, 1, 0x04, 0x00000008 },
+ { 0x000f70, 1, 0x04, 0x00080001 },
+ { 0x000ffc, 1, 0x04, 0x00000000 },
+ { 0x000300, 1, 0x04, 0x00000001 },
+ { 0x0013a8, 1, 0x04, 0x00000000 },
+ { 0x0012ec, 1, 0x04, 0x00000000 },
+ { 0x001310, 1, 0x04, 0x00000000 },
+ { 0x001314, 1, 0x04, 0x00000001 },
+ { 0x001380, 1, 0x04, 0x00000000 },
+ { 0x001384, 4, 0x04, 0x00000001 },
+ { 0x001394, 1, 0x04, 0x00000000 },
+ { 0x00139c, 1, 0x04, 0x00000000 },
+ { 0x001398, 1, 0x04, 0x00000000 },
+ { 0x001594, 1, 0x04, 0x00000000 },
+ { 0x001598, 4, 0x04, 0x00000001 },
+ { 0x000f54, 3, 0x04, 0x00000000 },
+ { 0x0019bc, 1, 0x04, 0x00000000 },
+ { 0x000f9c, 2, 0x04, 0x00000000 },
+ { 0x0012cc, 1, 0x04, 0x00000000 },
+ { 0x0012e8, 1, 0x04, 0x00000000 },
+ { 0x00130c, 1, 0x04, 0x00000001 },
+ { 0x001360, 8, 0x04, 0x00000000 },
+ { 0x00133c, 2, 0x04, 0x00000001 },
+ { 0x001344, 1, 0x04, 0x00000002 },
+ { 0x001348, 2, 0x04, 0x00000001 },
+ { 0x001350, 1, 0x04, 0x00000002 },
+ { 0x001358, 1, 0x04, 0x00000001 },
+ { 0x0012e4, 1, 0x04, 0x00000000 },
+ { 0x00131c, 4, 0x04, 0x00000000 },
+ { 0x0019c0, 1, 0x04, 0x00000000 },
+ { 0x001140, 1, 0x04, 0x00000000 },
+ { 0x000dd0, 1, 0x04, 0x00000000 },
+ { 0x000dd4, 1, 0x04, 0x00000001 },
+ { 0x0002f4, 1, 0x04, 0x00000000 },
+ { 0x0019c4, 1, 0x04, 0x00000000 },
+ { 0x0019c8, 1, 0x04, 0x00001500 },
+ { 0x00135c, 1, 0x04, 0x00000000 },
+ { 0x000f90, 1, 0x04, 0x00000000 },
+ { 0x0019e0, 8, 0x04, 0x00000001 },
+ { 0x0019cc, 1, 0x04, 0x00000001 },
+ { 0x0015b8, 1, 0x04, 0x00000000 },
+ { 0x001a00, 1, 0x04, 0x00001111 },
+ { 0x001a04, 7, 0x04, 0x00000000 },
+ { 0x000d6c, 2, 0x04, 0xffff0000 },
+ { 0x0010f8, 1, 0x04, 0x00001010 },
+ { 0x000d80, 5, 0x04, 0x00000000 },
+ { 0x000da0, 1, 0x04, 0x00000000 },
+ { 0x0007a4, 2, 0x04, 0x00000000 },
+ { 0x001508, 1, 0x04, 0x80000000 },
+ { 0x00150c, 1, 0x04, 0x40000000 },
+ { 0x001668, 1, 0x04, 0x00000000 },
+ { 0x000318, 2, 0x04, 0x00000008 },
+ { 0x000d9c, 1, 0x04, 0x00000001 },
+ { 0x000f14, 1, 0x04, 0x00000000 },
+ { 0x000374, 1, 0x04, 0x00000000 },
+ { 0x000378, 1, 0x04, 0x0000000c },
+ { 0x0007dc, 1, 0x04, 0x00000000 },
+ { 0x00074c, 1, 0x04, 0x00000055 },
+ { 0x001420, 1, 0x04, 0x00000003 },
+ { 0x001008, 1, 0x04, 0x00000008 },
+ { 0x00100c, 1, 0x04, 0x00000040 },
+ { 0x001010, 1, 0x04, 0x0000012c },
+ { 0x000d60, 1, 0x04, 0x00000040 },
+ { 0x001018, 1, 0x04, 0x00000020 },
+ { 0x00101c, 1, 0x04, 0x00000001 },
+ { 0x001020, 1, 0x04, 0x00000020 },
+ { 0x001024, 1, 0x04, 0x00000001 },
+ { 0x001444, 3, 0x04, 0x00000000 },
+ { 0x000360, 1, 0x04, 0x20164010 },
+ { 0x000364, 1, 0x04, 0x00000020 },
+ { 0x000368, 1, 0x04, 0x00000000 },
+ { 0x000da8, 1, 0x04, 0x00000030 },
+ { 0x000de4, 1, 0x04, 0x00000000 },
+ { 0x000204, 1, 0x04, 0x00000006 },
+ { 0x0002d0, 1, 0x04, 0x003fffff },
+ { 0x001220, 1, 0x04, 0x00000005 },
+ { 0x000fdc, 1, 0x04, 0x00000000 },
+ { 0x000f98, 1, 0x04, 0x00400008 },
+ { 0x001284, 1, 0x04, 0x08000080 },
+ { 0x001450, 1, 0x04, 0x00400008 },
+ { 0x001454, 1, 0x04, 0x08000080 },
+ { 0x000214, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_grctx_pack_mthd[] = {
+ { gm107_grctx_init_b097_0, 0xb097 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_fe_0[] = {
+ { 0x404004, 8, 0x04, 0x00000000 },
+ { 0x404024, 1, 0x04, 0x0000e000 },
+ { 0x404028, 8, 0x04, 0x00000000 },
+ { 0x4040a8, 8, 0x04, 0x00000000 },
+ { 0x4040c8, 1, 0x04, 0xf800008f },
+ { 0x4040d0, 6, 0x04, 0x00000000 },
+ { 0x4040f8, 1, 0x04, 0x00000000 },
+ { 0x404100, 10, 0x04, 0x00000000 },
+ { 0x404130, 2, 0x04, 0x00000000 },
+ { 0x404150, 1, 0x04, 0x0000002e },
+ { 0x404154, 1, 0x04, 0x00000400 },
+ { 0x404158, 1, 0x04, 0x00000200 },
+ { 0x404164, 1, 0x04, 0x00000045 },
+ { 0x40417c, 2, 0x04, 0x00000000 },
+ { 0x404194, 1, 0x04, 0x01000700 },
+ { 0x4041a0, 4, 0x04, 0x00000000 },
+ { 0x404200, 4, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_ds_0[] = {
+ { 0x405800, 1, 0x04, 0x0f8001bf },
+ { 0x405830, 1, 0x04, 0x0aa01000 },
+ { 0x405834, 1, 0x04, 0x08000000 },
+ { 0x405838, 1, 0x04, 0x00000000 },
+ { 0x405854, 1, 0x04, 0x00000000 },
+ { 0x405870, 4, 0x04, 0x00000001 },
+ { 0x405a00, 2, 0x04, 0x00000000 },
+ { 0x405a18, 1, 0x04, 0x00000000 },
+ { 0x405a1c, 1, 0x04, 0x000000ff },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_pd_0[] = {
+ { 0x406020, 1, 0x04, 0x07410001 },
+ { 0x406028, 4, 0x04, 0x00000001 },
+ { 0x4064a8, 1, 0x04, 0x00000000 },
+ { 0x4064ac, 1, 0x04, 0x00003fff },
+ { 0x4064b0, 3, 0x04, 0x00000000 },
+ { 0x4064c0, 1, 0x04, 0x80400280 },
+ { 0x4064c4, 1, 0x04, 0x0400ffff },
+ { 0x4064c8, 1, 0x04, 0x018001ff },
+ { 0x4064cc, 9, 0x04, 0x00000000 },
+ { 0x4064fc, 1, 0x04, 0x0000022a },
+ { 0x406500, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_be_0[] = {
+ { 0x408800, 1, 0x04, 0x32802a3c },
+ { 0x408804, 1, 0x04, 0x00000040 },
+ { 0x408808, 1, 0x04, 0x1003e005 },
+ { 0x408840, 1, 0x04, 0x0000000b },
+ { 0x408900, 1, 0x04, 0xb080b801 },
+ { 0x408904, 1, 0x04, 0x63038001 },
+ { 0x408908, 1, 0x04, 0x02c8102f },
+ { 0x408980, 1, 0x04, 0x0000011d },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { gm107_grctx_init_fe_0 },
+ { nvf0_grctx_init_pri_0 },
+ { nve4_grctx_init_memfmt_0 },
+ { gm107_grctx_init_ds_0 },
+ { nvf0_grctx_init_cwd_0 },
+ { gm107_grctx_init_pd_0 },
+ { nv108_grctx_init_rstr2d_0 },
+ { nve4_grctx_init_scc_0 },
+ { gm107_grctx_init_be_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_gpc_unk_0[] = {
+ { 0x418380, 1, 0x04, 0x00000056 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_gpc_unk_1[] = {
+ { 0x418600, 1, 0x04, 0x0000007f },
+ { 0x418684, 1, 0x04, 0x0000001f },
+ { 0x418700, 1, 0x04, 0x00000002 },
+ { 0x418704, 1, 0x04, 0x00000080 },
+ { 0x418708, 1, 0x04, 0x40000000 },
+ { 0x41870c, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_setup_0[] = {
+ { 0x418800, 1, 0x04, 0x7006863a },
+ { 0x418810, 1, 0x04, 0x00000000 },
+ { 0x418828, 1, 0x04, 0x00000044 },
+ { 0x418830, 1, 0x04, 0x10000001 },
+ { 0x4188d8, 1, 0x04, 0x00000008 },
+ { 0x4188e0, 1, 0x04, 0x01000000 },
+ { 0x4188e8, 5, 0x04, 0x00000000 },
+ { 0x4188fc, 1, 0x04, 0x20100058 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_gpc_unk_2[] = {
+ { 0x418d24, 1, 0x04, 0x00000000 },
+ { 0x418e00, 1, 0x04, 0x90000000 },
+ { 0x418e24, 1, 0x04, 0x00000000 },
+ { 0x418e28, 1, 0x04, 0x00000030 },
+ { 0x418e30, 1, 0x04, 0x00000000 },
+ { 0x418e34, 1, 0x04, 0x00010000 },
+ { 0x418e38, 1, 0x04, 0x00000000 },
+ { 0x418e40, 22, 0x04, 0x00000000 },
+ { 0x418ea0, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_grctx_pack_gpc[] = {
+ { gm107_grctx_init_gpc_unk_0 },
+ { nv108_grctx_init_prop_0 },
+ { gm107_grctx_init_gpc_unk_1 },
+ { gm107_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nv108_grctx_init_crstr_0 },
+ { nve4_grctx_init_gpm_0 },
+ { gm107_grctx_init_gpc_unk_2 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_tex_0[] = {
+ { 0x419a00, 1, 0x04, 0x000300f0 },
+ { 0x419a04, 1, 0x04, 0x00000005 },
+ { 0x419a08, 1, 0x04, 0x00000421 },
+ { 0x419a0c, 1, 0x04, 0x00120000 },
+ { 0x419a10, 1, 0x04, 0x00000000 },
+ { 0x419a14, 1, 0x04, 0x00002200 },
+ { 0x419a1c, 1, 0x04, 0x0000c000 },
+ { 0x419a20, 1, 0x04, 0x20008a00 },
+ { 0x419a30, 1, 0x04, 0x00000001 },
+ { 0x419a3c, 1, 0x04, 0x00000002 },
+ { 0x419ac4, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_mpc_0[] = {
+ { 0x419c00, 1, 0x04, 0x0000001a },
+ { 0x419c04, 1, 0x04, 0x80000006 },
+ { 0x419c08, 1, 0x04, 0x00000002 },
+ { 0x419c20, 1, 0x04, 0x00000000 },
+ { 0x419c24, 1, 0x04, 0x00084210 },
+ { 0x419c28, 1, 0x04, 0x3efbefbe },
+ { 0x419c2c, 1, 0x04, 0x00000000 },
+ { 0x419c34, 1, 0x04, 0x01ff1ff3 },
+ { 0x419c3c, 1, 0x04, 0x00001919 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_l1c_0[] = {
+ { 0x419c84, 1, 0x04, 0x00000020 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_sm_0[] = {
+ { 0x419e04, 3, 0x04, 0x00000000 },
+ { 0x419e10, 1, 0x04, 0x00001c02 },
+ { 0x419e44, 1, 0x04, 0x00d3eff2 },
+ { 0x419e48, 1, 0x04, 0x00000000 },
+ { 0x419e4c, 1, 0x04, 0x0000007f },
+ { 0x419e50, 1, 0x04, 0x00000000 },
+ { 0x419e60, 4, 0x04, 0x00000000 },
+ { 0x419e74, 10, 0x04, 0x00000000 },
+ { 0x419eac, 1, 0x04, 0x0001cf8b },
+ { 0x419eb0, 1, 0x04, 0x00030300 },
+ { 0x419eb8, 1, 0x04, 0x00000000 },
+ { 0x419ef0, 24, 0x04, 0x00000000 },
+ { 0x419f68, 2, 0x04, 0x00000000 },
+ { 0x419f70, 1, 0x04, 0x00000020 },
+ { 0x419f78, 1, 0x04, 0x000003eb },
+ { 0x419f7c, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_grctx_pack_tpc[] = {
+ { nvd7_grctx_init_pe_0 },
+ { gm107_grctx_init_tex_0 },
+ { gm107_grctx_init_mpc_0 },
+ { gm107_grctx_init_l1c_0 },
+ { gm107_grctx_init_sm_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_cbm_0[] = {
+ { 0x41bec0, 1, 0x04, 0x00000000 },
+ { 0x41bec4, 1, 0x04, 0x01050000 },
+ { 0x41bee4, 1, 0x04, 0x00000000 },
+ { 0x41bef0, 1, 0x04, 0x000003ff },
+ { 0x41bef4, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_grctx_init_wwdx_0[] = {
+ { 0x41bf00, 1, 0x04, 0x0a418820 },
+ { 0x41bf04, 1, 0x04, 0x062080e6 },
+ { 0x41bf08, 1, 0x04, 0x020398a4 },
+ { 0x41bf0c, 1, 0x04, 0x0e629062 },
+ { 0x41bf10, 1, 0x04, 0x0a418820 },
+ { 0x41bf14, 1, 0x04, 0x000000e6 },
+ { 0x41bfd0, 1, 0x04, 0x00900103 },
+ { 0x41bfe0, 1, 0x04, 0x80000000 },
+ { 0x41bfe4, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_grctx_pack_ppc[] = {
+ { nve4_grctx_init_pes_0 },
+ { gm107_grctx_init_cbm_0 },
+ { gm107_grctx_init_wwdx_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
+static void
+gm107_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+ mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+ mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+ mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
+
+ mmio_list(0x40800c, 0x00000000, 8, 1);
+ mmio_list(0x408010, 0x80000000, 0, 0);
+ mmio_list(0x419004, 0x00000000, 8, 1);
+ mmio_list(0x419008, 0x00000000, 0, 0);
+ mmio_list(0x4064cc, 0x80000000, 0, 0);
+ mmio_list(0x418e30, 0x80000000, 0, 0);
+
+ mmio_list(0x408004, 0x00000000, 8, 0);
+ mmio_list(0x408008, 0x80000030, 0, 0);
+ mmio_list(0x418e24, 0x00000000, 8, 0);
+ mmio_list(0x418e28, 0x80000030, 0, 0);
+
+ mmio_list(0x418810, 0x80000000, 12, 2);
+ mmio_list(0x419848, 0x10000000, 12, 2);
+ mmio_list(0x419c2c, 0x10000000, 12, 2);
+
+ mmio_list(0x405830, 0x0aa01000, 0, 0);
+ mmio_list(0x4064c4, 0x0400ffff, 0, 0);
+
+ /*XXX*/
+ mmio_list(0x5030c0, 0x00001540, 0, 0);
+ mmio_list(0x5030f4, 0x00000000, 0, 0);
+ mmio_list(0x5030e4, 0x00002000, 0, 0);
+ mmio_list(0x5030f8, 0x00003fc0, 0, 0);
+ mmio_list(0x418ea0, 0x07151540, 0, 0);
+
+ mmio_list(0x5032c0, 0x00001540, 0, 0);
+ mmio_list(0x5032f4, 0x00001fe0, 0, 0);
+ mmio_list(0x5032e4, 0x00002000, 0, 0);
+ mmio_list(0x5032f8, 0x00006fc0, 0, 0);
+ mmio_list(0x418ea4, 0x07151540, 0, 0);
+}
+
+static void
+gm107_grctx_generate_tpcid(struct nvc0_graph_priv *priv)
+{
+ int gpc, tpc, id;
+
+ for (tpc = 0, id = 0; tpc < 4; tpc++) {
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ if (tpc < priv->tpc_nr[gpc]) {
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id);
+ id++;
+ }
+
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]);
+ }
+ }
+}
+
+static void
+gm107_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+ struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
+ int i;
+
+ nvc0_graph_mmio(priv, oclass->hub);
+ nvc0_graph_mmio(priv, oclass->gpc);
+ nvc0_graph_mmio(priv, oclass->zcull);
+ nvc0_graph_mmio(priv, oclass->tpc);
+ nvc0_graph_mmio(priv, oclass->ppc);
+
+ nv_wr32(priv, 0x404154, 0x00000000);
+
+ oclass->mods(priv, info);
+ oclass->unkn(priv);
+
+ gm107_grctx_generate_tpcid(priv);
+ nvc0_grctx_generate_r406028(priv);
+ nve4_grctx_generate_r418bb8(priv);
+ nvc0_grctx_generate_r406800(priv);
+
+ nv_wr32(priv, 0x4064d0, 0x00000001);
+ for (i = 1; i < 8; i++)
+ nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000);
+ nv_wr32(priv, 0x406500, 0x00000001);
+
+ nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr);
+
+ if (priv->gpc_nr == 1) {
+ nv_mask(priv, 0x408850, 0x0000000f, priv->tpc_nr[0]);
+ nv_mask(priv, 0x408958, 0x0000000f, priv->tpc_nr[0]);
+ } else {
+ nv_mask(priv, 0x408850, 0x0000000f, priv->gpc_nr);
+ nv_mask(priv, 0x408958, 0x0000000f, priv->gpc_nr);
+ }
+
+ nvc0_graph_icmd(priv, oclass->icmd);
+ nv_wr32(priv, 0x404154, 0x00000400);
+ nvc0_graph_mthd(priv, oclass->mthd);
+
+ nv_mask(priv, 0x419e00, 0x00808080, 0x00808080);
+ nv_mask(priv, 0x419ccc, 0x80000000, 0x80000000);
+ nv_mask(priv, 0x419f80, 0x80000000, 0x80000000);
+ nv_mask(priv, 0x419f88, 0x80000000, 0x80000000);
+}
+
+struct nouveau_oclass *
+gm107_grctx_oclass = &(struct nvc0_grctx_oclass) {
+ .base.handle = NV_ENGCTX(GR, 0x08),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_graph_context_ctor,
+ .dtor = nvc0_graph_context_dtor,
+ .init = _nouveau_graph_context_init,
+ .fini = _nouveau_graph_context_fini,
+ .rd32 = _nouveau_graph_context_rd32,
+ .wr32 = _nouveau_graph_context_wr32,
+ },
+ .main = gm107_grctx_generate_main,
+ .mods = gm107_grctx_generate_mods,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = gm107_grctx_pack_hub,
+ .gpc = gm107_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = gm107_grctx_pack_tpc,
+ .ppc = gm107_grctx_pack_ppc,
+ .icmd = gm107_grctx_pack_icmd,
+ .mthd = gm107_grctx_pack_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
index a86bd3352bf8..48351b4d6d6b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
@@ -22,10 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-static struct nvc0_graph_init
-nv108_grctx_init_icmd[] = {
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nv108_grctx_init_icmd_0[] = {
{ 0x001000, 1, 0x01, 0x00000004 },
{ 0x000039, 3, 0x01, 0x00000000 },
{ 0x0000a9, 1, 0x01, 0x0000ffff },
@@ -274,839 +278,14 @@ nv108_grctx_init_icmd[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_a197[] = {
- { 0x000800, 1, 0x04, 0x00000000 },
- { 0x000840, 1, 0x04, 0x00000000 },
- { 0x000880, 1, 0x04, 0x00000000 },
- { 0x0008c0, 1, 0x04, 0x00000000 },
- { 0x000900, 1, 0x04, 0x00000000 },
- { 0x000940, 1, 0x04, 0x00000000 },
- { 0x000980, 1, 0x04, 0x00000000 },
- { 0x0009c0, 1, 0x04, 0x00000000 },
- { 0x000804, 1, 0x04, 0x00000000 },
- { 0x000844, 1, 0x04, 0x00000000 },
- { 0x000884, 1, 0x04, 0x00000000 },
- { 0x0008c4, 1, 0x04, 0x00000000 },
- { 0x000904, 1, 0x04, 0x00000000 },
- { 0x000944, 1, 0x04, 0x00000000 },
- { 0x000984, 1, 0x04, 0x00000000 },
- { 0x0009c4, 1, 0x04, 0x00000000 },
- { 0x000808, 1, 0x04, 0x00000400 },
- { 0x000848, 1, 0x04, 0x00000400 },
- { 0x000888, 1, 0x04, 0x00000400 },
- { 0x0008c8, 1, 0x04, 0x00000400 },
- { 0x000908, 1, 0x04, 0x00000400 },
- { 0x000948, 1, 0x04, 0x00000400 },
- { 0x000988, 1, 0x04, 0x00000400 },
- { 0x0009c8, 1, 0x04, 0x00000400 },
- { 0x00080c, 1, 0x04, 0x00000300 },
- { 0x00084c, 1, 0x04, 0x00000300 },
- { 0x00088c, 1, 0x04, 0x00000300 },
- { 0x0008cc, 1, 0x04, 0x00000300 },
- { 0x00090c, 1, 0x04, 0x00000300 },
- { 0x00094c, 1, 0x04, 0x00000300 },
- { 0x00098c, 1, 0x04, 0x00000300 },
- { 0x0009cc, 1, 0x04, 0x00000300 },
- { 0x000810, 1, 0x04, 0x000000cf },
- { 0x000850, 1, 0x04, 0x00000000 },
- { 0x000890, 1, 0x04, 0x00000000 },
- { 0x0008d0, 1, 0x04, 0x00000000 },
- { 0x000910, 1, 0x04, 0x00000000 },
- { 0x000950, 1, 0x04, 0x00000000 },
- { 0x000990, 1, 0x04, 0x00000000 },
- { 0x0009d0, 1, 0x04, 0x00000000 },
- { 0x000814, 1, 0x04, 0x00000040 },
- { 0x000854, 1, 0x04, 0x00000040 },
- { 0x000894, 1, 0x04, 0x00000040 },
- { 0x0008d4, 1, 0x04, 0x00000040 },
- { 0x000914, 1, 0x04, 0x00000040 },
- { 0x000954, 1, 0x04, 0x00000040 },
- { 0x000994, 1, 0x04, 0x00000040 },
- { 0x0009d4, 1, 0x04, 0x00000040 },
- { 0x000818, 1, 0x04, 0x00000001 },
- { 0x000858, 1, 0x04, 0x00000001 },
- { 0x000898, 1, 0x04, 0x00000001 },
- { 0x0008d8, 1, 0x04, 0x00000001 },
- { 0x000918, 1, 0x04, 0x00000001 },
- { 0x000958, 1, 0x04, 0x00000001 },
- { 0x000998, 1, 0x04, 0x00000001 },
- { 0x0009d8, 1, 0x04, 0x00000001 },
- { 0x00081c, 1, 0x04, 0x00000000 },
- { 0x00085c, 1, 0x04, 0x00000000 },
- { 0x00089c, 1, 0x04, 0x00000000 },
- { 0x0008dc, 1, 0x04, 0x00000000 },
- { 0x00091c, 1, 0x04, 0x00000000 },
- { 0x00095c, 1, 0x04, 0x00000000 },
- { 0x00099c, 1, 0x04, 0x00000000 },
- { 0x0009dc, 1, 0x04, 0x00000000 },
- { 0x000820, 1, 0x04, 0x00000000 },
- { 0x000860, 1, 0x04, 0x00000000 },
- { 0x0008a0, 1, 0x04, 0x00000000 },
- { 0x0008e0, 1, 0x04, 0x00000000 },
- { 0x000920, 1, 0x04, 0x00000000 },
- { 0x000960, 1, 0x04, 0x00000000 },
- { 0x0009a0, 1, 0x04, 0x00000000 },
- { 0x0009e0, 1, 0x04, 0x00000000 },
- { 0x001c00, 1, 0x04, 0x00000000 },
- { 0x001c10, 1, 0x04, 0x00000000 },
- { 0x001c20, 1, 0x04, 0x00000000 },
- { 0x001c30, 1, 0x04, 0x00000000 },
- { 0x001c40, 1, 0x04, 0x00000000 },
- { 0x001c50, 1, 0x04, 0x00000000 },
- { 0x001c60, 1, 0x04, 0x00000000 },
- { 0x001c70, 1, 0x04, 0x00000000 },
- { 0x001c80, 1, 0x04, 0x00000000 },
- { 0x001c90, 1, 0x04, 0x00000000 },
- { 0x001ca0, 1, 0x04, 0x00000000 },
- { 0x001cb0, 1, 0x04, 0x00000000 },
- { 0x001cc0, 1, 0x04, 0x00000000 },
- { 0x001cd0, 1, 0x04, 0x00000000 },
- { 0x001ce0, 1, 0x04, 0x00000000 },
- { 0x001cf0, 1, 0x04, 0x00000000 },
- { 0x001c04, 1, 0x04, 0x00000000 },
- { 0x001c14, 1, 0x04, 0x00000000 },
- { 0x001c24, 1, 0x04, 0x00000000 },
- { 0x001c34, 1, 0x04, 0x00000000 },
- { 0x001c44, 1, 0x04, 0x00000000 },
- { 0x001c54, 1, 0x04, 0x00000000 },
- { 0x001c64, 1, 0x04, 0x00000000 },
- { 0x001c74, 1, 0x04, 0x00000000 },
- { 0x001c84, 1, 0x04, 0x00000000 },
- { 0x001c94, 1, 0x04, 0x00000000 },
- { 0x001ca4, 1, 0x04, 0x00000000 },
- { 0x001cb4, 1, 0x04, 0x00000000 },
- { 0x001cc4, 1, 0x04, 0x00000000 },
- { 0x001cd4, 1, 0x04, 0x00000000 },
- { 0x001ce4, 1, 0x04, 0x00000000 },
- { 0x001cf4, 1, 0x04, 0x00000000 },
- { 0x001c08, 1, 0x04, 0x00000000 },
- { 0x001c18, 1, 0x04, 0x00000000 },
- { 0x001c28, 1, 0x04, 0x00000000 },
- { 0x001c38, 1, 0x04, 0x00000000 },
- { 0x001c48, 1, 0x04, 0x00000000 },
- { 0x001c58, 1, 0x04, 0x00000000 },
- { 0x001c68, 1, 0x04, 0x00000000 },
- { 0x001c78, 1, 0x04, 0x00000000 },
- { 0x001c88, 1, 0x04, 0x00000000 },
- { 0x001c98, 1, 0x04, 0x00000000 },
- { 0x001ca8, 1, 0x04, 0x00000000 },
- { 0x001cb8, 1, 0x04, 0x00000000 },
- { 0x001cc8, 1, 0x04, 0x00000000 },
- { 0x001cd8, 1, 0x04, 0x00000000 },
- { 0x001ce8, 1, 0x04, 0x00000000 },
- { 0x001cf8, 1, 0x04, 0x00000000 },
- { 0x001c0c, 1, 0x04, 0x00000000 },
- { 0x001c1c, 1, 0x04, 0x00000000 },
- { 0x001c2c, 1, 0x04, 0x00000000 },
- { 0x001c3c, 1, 0x04, 0x00000000 },
- { 0x001c4c, 1, 0x04, 0x00000000 },
- { 0x001c5c, 1, 0x04, 0x00000000 },
- { 0x001c6c, 1, 0x04, 0x00000000 },
- { 0x001c7c, 1, 0x04, 0x00000000 },
- { 0x001c8c, 1, 0x04, 0x00000000 },
- { 0x001c9c, 1, 0x04, 0x00000000 },
- { 0x001cac, 1, 0x04, 0x00000000 },
- { 0x001cbc, 1, 0x04, 0x00000000 },
- { 0x001ccc, 1, 0x04, 0x00000000 },
- { 0x001cdc, 1, 0x04, 0x00000000 },
- { 0x001cec, 1, 0x04, 0x00000000 },
- { 0x001cfc, 2, 0x04, 0x00000000 },
- { 0x001d10, 1, 0x04, 0x00000000 },
- { 0x001d20, 1, 0x04, 0x00000000 },
- { 0x001d30, 1, 0x04, 0x00000000 },
- { 0x001d40, 1, 0x04, 0x00000000 },
- { 0x001d50, 1, 0x04, 0x00000000 },
- { 0x001d60, 1, 0x04, 0x00000000 },
- { 0x001d70, 1, 0x04, 0x00000000 },
- { 0x001d80, 1, 0x04, 0x00000000 },
- { 0x001d90, 1, 0x04, 0x00000000 },
- { 0x001da0, 1, 0x04, 0x00000000 },
- { 0x001db0, 1, 0x04, 0x00000000 },
- { 0x001dc0, 1, 0x04, 0x00000000 },
- { 0x001dd0, 1, 0x04, 0x00000000 },
- { 0x001de0, 1, 0x04, 0x00000000 },
- { 0x001df0, 1, 0x04, 0x00000000 },
- { 0x001d04, 1, 0x04, 0x00000000 },
- { 0x001d14, 1, 0x04, 0x00000000 },
- { 0x001d24, 1, 0x04, 0x00000000 },
- { 0x001d34, 1, 0x04, 0x00000000 },
- { 0x001d44, 1, 0x04, 0x00000000 },
- { 0x001d54, 1, 0x04, 0x00000000 },
- { 0x001d64, 1, 0x04, 0x00000000 },
- { 0x001d74, 1, 0x04, 0x00000000 },
- { 0x001d84, 1, 0x04, 0x00000000 },
- { 0x001d94, 1, 0x04, 0x00000000 },
- { 0x001da4, 1, 0x04, 0x00000000 },
- { 0x001db4, 1, 0x04, 0x00000000 },
- { 0x001dc4, 1, 0x04, 0x00000000 },
- { 0x001dd4, 1, 0x04, 0x00000000 },
- { 0x001de4, 1, 0x04, 0x00000000 },
- { 0x001df4, 1, 0x04, 0x00000000 },
- { 0x001d08, 1, 0x04, 0x00000000 },
- { 0x001d18, 1, 0x04, 0x00000000 },
- { 0x001d28, 1, 0x04, 0x00000000 },
- { 0x001d38, 1, 0x04, 0x00000000 },
- { 0x001d48, 1, 0x04, 0x00000000 },
- { 0x001d58, 1, 0x04, 0x00000000 },
- { 0x001d68, 1, 0x04, 0x00000000 },
- { 0x001d78, 1, 0x04, 0x00000000 },
- { 0x001d88, 1, 0x04, 0x00000000 },
- { 0x001d98, 1, 0x04, 0x00000000 },
- { 0x001da8, 1, 0x04, 0x00000000 },
- { 0x001db8, 1, 0x04, 0x00000000 },
- { 0x001dc8, 1, 0x04, 0x00000000 },
- { 0x001dd8, 1, 0x04, 0x00000000 },
- { 0x001de8, 1, 0x04, 0x00000000 },
- { 0x001df8, 1, 0x04, 0x00000000 },
- { 0x001d0c, 1, 0x04, 0x00000000 },
- { 0x001d1c, 1, 0x04, 0x00000000 },
- { 0x001d2c, 1, 0x04, 0x00000000 },
- { 0x001d3c, 1, 0x04, 0x00000000 },
- { 0x001d4c, 1, 0x04, 0x00000000 },
- { 0x001d5c, 1, 0x04, 0x00000000 },
- { 0x001d6c, 1, 0x04, 0x00000000 },
- { 0x001d7c, 1, 0x04, 0x00000000 },
- { 0x001d8c, 1, 0x04, 0x00000000 },
- { 0x001d9c, 1, 0x04, 0x00000000 },
- { 0x001dac, 1, 0x04, 0x00000000 },
- { 0x001dbc, 1, 0x04, 0x00000000 },
- { 0x001dcc, 1, 0x04, 0x00000000 },
- { 0x001ddc, 1, 0x04, 0x00000000 },
- { 0x001dec, 1, 0x04, 0x00000000 },
- { 0x001dfc, 1, 0x04, 0x00000000 },
- { 0x001f00, 1, 0x04, 0x00000000 },
- { 0x001f08, 1, 0x04, 0x00000000 },
- { 0x001f10, 1, 0x04, 0x00000000 },
- { 0x001f18, 1, 0x04, 0x00000000 },
- { 0x001f20, 1, 0x04, 0x00000000 },
- { 0x001f28, 1, 0x04, 0x00000000 },
- { 0x001f30, 1, 0x04, 0x00000000 },
- { 0x001f38, 1, 0x04, 0x00000000 },
- { 0x001f40, 1, 0x04, 0x00000000 },
- { 0x001f48, 1, 0x04, 0x00000000 },
- { 0x001f50, 1, 0x04, 0x00000000 },
- { 0x001f58, 1, 0x04, 0x00000000 },
- { 0x001f60, 1, 0x04, 0x00000000 },
- { 0x001f68, 1, 0x04, 0x00000000 },
- { 0x001f70, 1, 0x04, 0x00000000 },
- { 0x001f78, 1, 0x04, 0x00000000 },
- { 0x001f04, 1, 0x04, 0x00000000 },
- { 0x001f0c, 1, 0x04, 0x00000000 },
- { 0x001f14, 1, 0x04, 0x00000000 },
- { 0x001f1c, 1, 0x04, 0x00000000 },
- { 0x001f24, 1, 0x04, 0x00000000 },
- { 0x001f2c, 1, 0x04, 0x00000000 },
- { 0x001f34, 1, 0x04, 0x00000000 },
- { 0x001f3c, 1, 0x04, 0x00000000 },
- { 0x001f44, 1, 0x04, 0x00000000 },
- { 0x001f4c, 1, 0x04, 0x00000000 },
- { 0x001f54, 1, 0x04, 0x00000000 },
- { 0x001f5c, 1, 0x04, 0x00000000 },
- { 0x001f64, 1, 0x04, 0x00000000 },
- { 0x001f6c, 1, 0x04, 0x00000000 },
- { 0x001f74, 1, 0x04, 0x00000000 },
- { 0x001f7c, 2, 0x04, 0x00000000 },
- { 0x001f88, 1, 0x04, 0x00000000 },
- { 0x001f90, 1, 0x04, 0x00000000 },
- { 0x001f98, 1, 0x04, 0x00000000 },
- { 0x001fa0, 1, 0x04, 0x00000000 },
- { 0x001fa8, 1, 0x04, 0x00000000 },
- { 0x001fb0, 1, 0x04, 0x00000000 },
- { 0x001fb8, 1, 0x04, 0x00000000 },
- { 0x001fc0, 1, 0x04, 0x00000000 },
- { 0x001fc8, 1, 0x04, 0x00000000 },
- { 0x001fd0, 1, 0x04, 0x00000000 },
- { 0x001fd8, 1, 0x04, 0x00000000 },
- { 0x001fe0, 1, 0x04, 0x00000000 },
- { 0x001fe8, 1, 0x04, 0x00000000 },
- { 0x001ff0, 1, 0x04, 0x00000000 },
- { 0x001ff8, 1, 0x04, 0x00000000 },
- { 0x001f84, 1, 0x04, 0x00000000 },
- { 0x001f8c, 1, 0x04, 0x00000000 },
- { 0x001f94, 1, 0x04, 0x00000000 },
- { 0x001f9c, 1, 0x04, 0x00000000 },
- { 0x001fa4, 1, 0x04, 0x00000000 },
- { 0x001fac, 1, 0x04, 0x00000000 },
- { 0x001fb4, 1, 0x04, 0x00000000 },
- { 0x001fbc, 1, 0x04, 0x00000000 },
- { 0x001fc4, 1, 0x04, 0x00000000 },
- { 0x001fcc, 1, 0x04, 0x00000000 },
- { 0x001fd4, 1, 0x04, 0x00000000 },
- { 0x001fdc, 1, 0x04, 0x00000000 },
- { 0x001fe4, 1, 0x04, 0x00000000 },
- { 0x001fec, 1, 0x04, 0x00000000 },
- { 0x001ff4, 1, 0x04, 0x00000000 },
- { 0x001ffc, 2, 0x04, 0x00000000 },
- { 0x002040, 1, 0x04, 0x00000011 },
- { 0x002080, 1, 0x04, 0x00000020 },
- { 0x0020c0, 1, 0x04, 0x00000030 },
- { 0x002100, 1, 0x04, 0x00000040 },
- { 0x002140, 1, 0x04, 0x00000051 },
- { 0x00200c, 1, 0x04, 0x00000001 },
- { 0x00204c, 1, 0x04, 0x00000001 },
- { 0x00208c, 1, 0x04, 0x00000001 },
- { 0x0020cc, 1, 0x04, 0x00000001 },
- { 0x00210c, 1, 0x04, 0x00000001 },
- { 0x00214c, 1, 0x04, 0x00000001 },
- { 0x002010, 1, 0x04, 0x00000000 },
- { 0x002050, 1, 0x04, 0x00000000 },
- { 0x002090, 1, 0x04, 0x00000001 },
- { 0x0020d0, 1, 0x04, 0x00000002 },
- { 0x002110, 1, 0x04, 0x00000003 },
- { 0x002150, 1, 0x04, 0x00000004 },
- { 0x000380, 1, 0x04, 0x00000000 },
- { 0x0003a0, 1, 0x04, 0x00000000 },
- { 0x0003c0, 1, 0x04, 0x00000000 },
- { 0x0003e0, 1, 0x04, 0x00000000 },
- { 0x000384, 1, 0x04, 0x00000000 },
- { 0x0003a4, 1, 0x04, 0x00000000 },
- { 0x0003c4, 1, 0x04, 0x00000000 },
- { 0x0003e4, 1, 0x04, 0x00000000 },
- { 0x000388, 1, 0x04, 0x00000000 },
- { 0x0003a8, 1, 0x04, 0x00000000 },
- { 0x0003c8, 1, 0x04, 0x00000000 },
- { 0x0003e8, 1, 0x04, 0x00000000 },
- { 0x00038c, 1, 0x04, 0x00000000 },
- { 0x0003ac, 1, 0x04, 0x00000000 },
- { 0x0003cc, 1, 0x04, 0x00000000 },
- { 0x0003ec, 1, 0x04, 0x00000000 },
- { 0x000700, 1, 0x04, 0x00000000 },
- { 0x000710, 1, 0x04, 0x00000000 },
- { 0x000720, 1, 0x04, 0x00000000 },
- { 0x000730, 1, 0x04, 0x00000000 },
- { 0x000704, 1, 0x04, 0x00000000 },
- { 0x000714, 1, 0x04, 0x00000000 },
- { 0x000724, 1, 0x04, 0x00000000 },
- { 0x000734, 1, 0x04, 0x00000000 },
- { 0x000708, 1, 0x04, 0x00000000 },
- { 0x000718, 1, 0x04, 0x00000000 },
- { 0x000728, 1, 0x04, 0x00000000 },
- { 0x000738, 1, 0x04, 0x00000000 },
- { 0x002800, 128, 0x04, 0x00000000 },
- { 0x000a00, 1, 0x04, 0x00000000 },
- { 0x000a20, 1, 0x04, 0x00000000 },
- { 0x000a40, 1, 0x04, 0x00000000 },
- { 0x000a60, 1, 0x04, 0x00000000 },
- { 0x000a80, 1, 0x04, 0x00000000 },
- { 0x000aa0, 1, 0x04, 0x00000000 },
- { 0x000ac0, 1, 0x04, 0x00000000 },
- { 0x000ae0, 1, 0x04, 0x00000000 },
- { 0x000b00, 1, 0x04, 0x00000000 },
- { 0x000b20, 1, 0x04, 0x00000000 },
- { 0x000b40, 1, 0x04, 0x00000000 },
- { 0x000b60, 1, 0x04, 0x00000000 },
- { 0x000b80, 1, 0x04, 0x00000000 },
- { 0x000ba0, 1, 0x04, 0x00000000 },
- { 0x000bc0, 1, 0x04, 0x00000000 },
- { 0x000be0, 1, 0x04, 0x00000000 },
- { 0x000a04, 1, 0x04, 0x00000000 },
- { 0x000a24, 1, 0x04, 0x00000000 },
- { 0x000a44, 1, 0x04, 0x00000000 },
- { 0x000a64, 1, 0x04, 0x00000000 },
- { 0x000a84, 1, 0x04, 0x00000000 },
- { 0x000aa4, 1, 0x04, 0x00000000 },
- { 0x000ac4, 1, 0x04, 0x00000000 },
- { 0x000ae4, 1, 0x04, 0x00000000 },
- { 0x000b04, 1, 0x04, 0x00000000 },
- { 0x000b24, 1, 0x04, 0x00000000 },
- { 0x000b44, 1, 0x04, 0x00000000 },
- { 0x000b64, 1, 0x04, 0x00000000 },
- { 0x000b84, 1, 0x04, 0x00000000 },
- { 0x000ba4, 1, 0x04, 0x00000000 },
- { 0x000bc4, 1, 0x04, 0x00000000 },
- { 0x000be4, 1, 0x04, 0x00000000 },
- { 0x000a08, 1, 0x04, 0x00000000 },
- { 0x000a28, 1, 0x04, 0x00000000 },
- { 0x000a48, 1, 0x04, 0x00000000 },
- { 0x000a68, 1, 0x04, 0x00000000 },
- { 0x000a88, 1, 0x04, 0x00000000 },
- { 0x000aa8, 1, 0x04, 0x00000000 },
- { 0x000ac8, 1, 0x04, 0x00000000 },
- { 0x000ae8, 1, 0x04, 0x00000000 },
- { 0x000b08, 1, 0x04, 0x00000000 },
- { 0x000b28, 1, 0x04, 0x00000000 },
- { 0x000b48, 1, 0x04, 0x00000000 },
- { 0x000b68, 1, 0x04, 0x00000000 },
- { 0x000b88, 1, 0x04, 0x00000000 },
- { 0x000ba8, 1, 0x04, 0x00000000 },
- { 0x000bc8, 1, 0x04, 0x00000000 },
- { 0x000be8, 1, 0x04, 0x00000000 },
- { 0x000a0c, 1, 0x04, 0x00000000 },
- { 0x000a2c, 1, 0x04, 0x00000000 },
- { 0x000a4c, 1, 0x04, 0x00000000 },
- { 0x000a6c, 1, 0x04, 0x00000000 },
- { 0x000a8c, 1, 0x04, 0x00000000 },
- { 0x000aac, 1, 0x04, 0x00000000 },
- { 0x000acc, 1, 0x04, 0x00000000 },
- { 0x000aec, 1, 0x04, 0x00000000 },
- { 0x000b0c, 1, 0x04, 0x00000000 },
- { 0x000b2c, 1, 0x04, 0x00000000 },
- { 0x000b4c, 1, 0x04, 0x00000000 },
- { 0x000b6c, 1, 0x04, 0x00000000 },
- { 0x000b8c, 1, 0x04, 0x00000000 },
- { 0x000bac, 1, 0x04, 0x00000000 },
- { 0x000bcc, 1, 0x04, 0x00000000 },
- { 0x000bec, 1, 0x04, 0x00000000 },
- { 0x000a10, 1, 0x04, 0x00000000 },
- { 0x000a30, 1, 0x04, 0x00000000 },
- { 0x000a50, 1, 0x04, 0x00000000 },
- { 0x000a70, 1, 0x04, 0x00000000 },
- { 0x000a90, 1, 0x04, 0x00000000 },
- { 0x000ab0, 1, 0x04, 0x00000000 },
- { 0x000ad0, 1, 0x04, 0x00000000 },
- { 0x000af0, 1, 0x04, 0x00000000 },
- { 0x000b10, 1, 0x04, 0x00000000 },
- { 0x000b30, 1, 0x04, 0x00000000 },
- { 0x000b50, 1, 0x04, 0x00000000 },
- { 0x000b70, 1, 0x04, 0x00000000 },
- { 0x000b90, 1, 0x04, 0x00000000 },
- { 0x000bb0, 1, 0x04, 0x00000000 },
- { 0x000bd0, 1, 0x04, 0x00000000 },
- { 0x000bf0, 1, 0x04, 0x00000000 },
- { 0x000a14, 1, 0x04, 0x00000000 },
- { 0x000a34, 1, 0x04, 0x00000000 },
- { 0x000a54, 1, 0x04, 0x00000000 },
- { 0x000a74, 1, 0x04, 0x00000000 },
- { 0x000a94, 1, 0x04, 0x00000000 },
- { 0x000ab4, 1, 0x04, 0x00000000 },
- { 0x000ad4, 1, 0x04, 0x00000000 },
- { 0x000af4, 1, 0x04, 0x00000000 },
- { 0x000b14, 1, 0x04, 0x00000000 },
- { 0x000b34, 1, 0x04, 0x00000000 },
- { 0x000b54, 1, 0x04, 0x00000000 },
- { 0x000b74, 1, 0x04, 0x00000000 },
- { 0x000b94, 1, 0x04, 0x00000000 },
- { 0x000bb4, 1, 0x04, 0x00000000 },
- { 0x000bd4, 1, 0x04, 0x00000000 },
- { 0x000bf4, 1, 0x04, 0x00000000 },
- { 0x000c00, 1, 0x04, 0x00000000 },
- { 0x000c10, 1, 0x04, 0x00000000 },
- { 0x000c20, 1, 0x04, 0x00000000 },
- { 0x000c30, 1, 0x04, 0x00000000 },
- { 0x000c40, 1, 0x04, 0x00000000 },
- { 0x000c50, 1, 0x04, 0x00000000 },
- { 0x000c60, 1, 0x04, 0x00000000 },
- { 0x000c70, 1, 0x04, 0x00000000 },
- { 0x000c80, 1, 0x04, 0x00000000 },
- { 0x000c90, 1, 0x04, 0x00000000 },
- { 0x000ca0, 1, 0x04, 0x00000000 },
- { 0x000cb0, 1, 0x04, 0x00000000 },
- { 0x000cc0, 1, 0x04, 0x00000000 },
- { 0x000cd0, 1, 0x04, 0x00000000 },
- { 0x000ce0, 1, 0x04, 0x00000000 },
- { 0x000cf0, 1, 0x04, 0x00000000 },
- { 0x000c04, 1, 0x04, 0x00000000 },
- { 0x000c14, 1, 0x04, 0x00000000 },
- { 0x000c24, 1, 0x04, 0x00000000 },
- { 0x000c34, 1, 0x04, 0x00000000 },
- { 0x000c44, 1, 0x04, 0x00000000 },
- { 0x000c54, 1, 0x04, 0x00000000 },
- { 0x000c64, 1, 0x04, 0x00000000 },
- { 0x000c74, 1, 0x04, 0x00000000 },
- { 0x000c84, 1, 0x04, 0x00000000 },
- { 0x000c94, 1, 0x04, 0x00000000 },
- { 0x000ca4, 1, 0x04, 0x00000000 },
- { 0x000cb4, 1, 0x04, 0x00000000 },
- { 0x000cc4, 1, 0x04, 0x00000000 },
- { 0x000cd4, 1, 0x04, 0x00000000 },
- { 0x000ce4, 1, 0x04, 0x00000000 },
- { 0x000cf4, 1, 0x04, 0x00000000 },
- { 0x000c08, 1, 0x04, 0x00000000 },
- { 0x000c18, 1, 0x04, 0x00000000 },
- { 0x000c28, 1, 0x04, 0x00000000 },
- { 0x000c38, 1, 0x04, 0x00000000 },
- { 0x000c48, 1, 0x04, 0x00000000 },
- { 0x000c58, 1, 0x04, 0x00000000 },
- { 0x000c68, 1, 0x04, 0x00000000 },
- { 0x000c78, 1, 0x04, 0x00000000 },
- { 0x000c88, 1, 0x04, 0x00000000 },
- { 0x000c98, 1, 0x04, 0x00000000 },
- { 0x000ca8, 1, 0x04, 0x00000000 },
- { 0x000cb8, 1, 0x04, 0x00000000 },
- { 0x000cc8, 1, 0x04, 0x00000000 },
- { 0x000cd8, 1, 0x04, 0x00000000 },
- { 0x000ce8, 1, 0x04, 0x00000000 },
- { 0x000cf8, 1, 0x04, 0x00000000 },
- { 0x000c0c, 1, 0x04, 0x3f800000 },
- { 0x000c1c, 1, 0x04, 0x3f800000 },
- { 0x000c2c, 1, 0x04, 0x3f800000 },
- { 0x000c3c, 1, 0x04, 0x3f800000 },
- { 0x000c4c, 1, 0x04, 0x3f800000 },
- { 0x000c5c, 1, 0x04, 0x3f800000 },
- { 0x000c6c, 1, 0x04, 0x3f800000 },
- { 0x000c7c, 1, 0x04, 0x3f800000 },
- { 0x000c8c, 1, 0x04, 0x3f800000 },
- { 0x000c9c, 1, 0x04, 0x3f800000 },
- { 0x000cac, 1, 0x04, 0x3f800000 },
- { 0x000cbc, 1, 0x04, 0x3f800000 },
- { 0x000ccc, 1, 0x04, 0x3f800000 },
- { 0x000cdc, 1, 0x04, 0x3f800000 },
- { 0x000cec, 1, 0x04, 0x3f800000 },
- { 0x000cfc, 1, 0x04, 0x3f800000 },
- { 0x000d00, 1, 0x04, 0xffff0000 },
- { 0x000d08, 1, 0x04, 0xffff0000 },
- { 0x000d10, 1, 0x04, 0xffff0000 },
- { 0x000d18, 1, 0x04, 0xffff0000 },
- { 0x000d20, 1, 0x04, 0xffff0000 },
- { 0x000d28, 1, 0x04, 0xffff0000 },
- { 0x000d30, 1, 0x04, 0xffff0000 },
- { 0x000d38, 1, 0x04, 0xffff0000 },
- { 0x000d04, 1, 0x04, 0xffff0000 },
- { 0x000d0c, 1, 0x04, 0xffff0000 },
- { 0x000d14, 1, 0x04, 0xffff0000 },
- { 0x000d1c, 1, 0x04, 0xffff0000 },
- { 0x000d24, 1, 0x04, 0xffff0000 },
- { 0x000d2c, 1, 0x04, 0xffff0000 },
- { 0x000d34, 1, 0x04, 0xffff0000 },
- { 0x000d3c, 1, 0x04, 0xffff0000 },
- { 0x000e00, 1, 0x04, 0x00000000 },
- { 0x000e10, 1, 0x04, 0x00000000 },
- { 0x000e20, 1, 0x04, 0x00000000 },
- { 0x000e30, 1, 0x04, 0x00000000 },
- { 0x000e40, 1, 0x04, 0x00000000 },
- { 0x000e50, 1, 0x04, 0x00000000 },
- { 0x000e60, 1, 0x04, 0x00000000 },
- { 0x000e70, 1, 0x04, 0x00000000 },
- { 0x000e80, 1, 0x04, 0x00000000 },
- { 0x000e90, 1, 0x04, 0x00000000 },
- { 0x000ea0, 1, 0x04, 0x00000000 },
- { 0x000eb0, 1, 0x04, 0x00000000 },
- { 0x000ec0, 1, 0x04, 0x00000000 },
- { 0x000ed0, 1, 0x04, 0x00000000 },
- { 0x000ee0, 1, 0x04, 0x00000000 },
- { 0x000ef0, 1, 0x04, 0x00000000 },
- { 0x000e04, 1, 0x04, 0xffff0000 },
- { 0x000e14, 1, 0x04, 0xffff0000 },
- { 0x000e24, 1, 0x04, 0xffff0000 },
- { 0x000e34, 1, 0x04, 0xffff0000 },
- { 0x000e44, 1, 0x04, 0xffff0000 },
- { 0x000e54, 1, 0x04, 0xffff0000 },
- { 0x000e64, 1, 0x04, 0xffff0000 },
- { 0x000e74, 1, 0x04, 0xffff0000 },
- { 0x000e84, 1, 0x04, 0xffff0000 },
- { 0x000e94, 1, 0x04, 0xffff0000 },
- { 0x000ea4, 1, 0x04, 0xffff0000 },
- { 0x000eb4, 1, 0x04, 0xffff0000 },
- { 0x000ec4, 1, 0x04, 0xffff0000 },
- { 0x000ed4, 1, 0x04, 0xffff0000 },
- { 0x000ee4, 1, 0x04, 0xffff0000 },
- { 0x000ef4, 1, 0x04, 0xffff0000 },
- { 0x000e08, 1, 0x04, 0xffff0000 },
- { 0x000e18, 1, 0x04, 0xffff0000 },
- { 0x000e28, 1, 0x04, 0xffff0000 },
- { 0x000e38, 1, 0x04, 0xffff0000 },
- { 0x000e48, 1, 0x04, 0xffff0000 },
- { 0x000e58, 1, 0x04, 0xffff0000 },
- { 0x000e68, 1, 0x04, 0xffff0000 },
- { 0x000e78, 1, 0x04, 0xffff0000 },
- { 0x000e88, 1, 0x04, 0xffff0000 },
- { 0x000e98, 1, 0x04, 0xffff0000 },
- { 0x000ea8, 1, 0x04, 0xffff0000 },
- { 0x000eb8, 1, 0x04, 0xffff0000 },
- { 0x000ec8, 1, 0x04, 0xffff0000 },
- { 0x000ed8, 1, 0x04, 0xffff0000 },
- { 0x000ee8, 1, 0x04, 0xffff0000 },
- { 0x000ef8, 1, 0x04, 0xffff0000 },
- { 0x000d40, 1, 0x04, 0x00000000 },
- { 0x000d48, 1, 0x04, 0x00000000 },
- { 0x000d50, 1, 0x04, 0x00000000 },
- { 0x000d58, 1, 0x04, 0x00000000 },
- { 0x000d44, 1, 0x04, 0x00000000 },
- { 0x000d4c, 1, 0x04, 0x00000000 },
- { 0x000d54, 1, 0x04, 0x00000000 },
- { 0x000d5c, 1, 0x04, 0x00000000 },
- { 0x001e00, 1, 0x04, 0x00000001 },
- { 0x001e20, 1, 0x04, 0x00000001 },
- { 0x001e40, 1, 0x04, 0x00000001 },
- { 0x001e60, 1, 0x04, 0x00000001 },
- { 0x001e80, 1, 0x04, 0x00000001 },
- { 0x001ea0, 1, 0x04, 0x00000001 },
- { 0x001ec0, 1, 0x04, 0x00000001 },
- { 0x001ee0, 1, 0x04, 0x00000001 },
- { 0x001e04, 1, 0x04, 0x00000001 },
- { 0x001e24, 1, 0x04, 0x00000001 },
- { 0x001e44, 1, 0x04, 0x00000001 },
- { 0x001e64, 1, 0x04, 0x00000001 },
- { 0x001e84, 1, 0x04, 0x00000001 },
- { 0x001ea4, 1, 0x04, 0x00000001 },
- { 0x001ec4, 1, 0x04, 0x00000001 },
- { 0x001ee4, 1, 0x04, 0x00000001 },
- { 0x001e08, 1, 0x04, 0x00000002 },
- { 0x001e28, 1, 0x04, 0x00000002 },
- { 0x001e48, 1, 0x04, 0x00000002 },
- { 0x001e68, 1, 0x04, 0x00000002 },
- { 0x001e88, 1, 0x04, 0x00000002 },
- { 0x001ea8, 1, 0x04, 0x00000002 },
- { 0x001ec8, 1, 0x04, 0x00000002 },
- { 0x001ee8, 1, 0x04, 0x00000002 },
- { 0x001e0c, 1, 0x04, 0x00000001 },
- { 0x001e2c, 1, 0x04, 0x00000001 },
- { 0x001e4c, 1, 0x04, 0x00000001 },
- { 0x001e6c, 1, 0x04, 0x00000001 },
- { 0x001e8c, 1, 0x04, 0x00000001 },
- { 0x001eac, 1, 0x04, 0x00000001 },
- { 0x001ecc, 1, 0x04, 0x00000001 },
- { 0x001eec, 1, 0x04, 0x00000001 },
- { 0x001e10, 1, 0x04, 0x00000001 },
- { 0x001e30, 1, 0x04, 0x00000001 },
- { 0x001e50, 1, 0x04, 0x00000001 },
- { 0x001e70, 1, 0x04, 0x00000001 },
- { 0x001e90, 1, 0x04, 0x00000001 },
- { 0x001eb0, 1, 0x04, 0x00000001 },
- { 0x001ed0, 1, 0x04, 0x00000001 },
- { 0x001ef0, 1, 0x04, 0x00000001 },
- { 0x001e14, 1, 0x04, 0x00000002 },
- { 0x001e34, 1, 0x04, 0x00000002 },
- { 0x001e54, 1, 0x04, 0x00000002 },
- { 0x001e74, 1, 0x04, 0x00000002 },
- { 0x001e94, 1, 0x04, 0x00000002 },
- { 0x001eb4, 1, 0x04, 0x00000002 },
- { 0x001ed4, 1, 0x04, 0x00000002 },
- { 0x001ef4, 1, 0x04, 0x00000002 },
- { 0x001e18, 1, 0x04, 0x00000001 },
- { 0x001e38, 1, 0x04, 0x00000001 },
- { 0x001e58, 1, 0x04, 0x00000001 },
- { 0x001e78, 1, 0x04, 0x00000001 },
- { 0x001e98, 1, 0x04, 0x00000001 },
- { 0x001eb8, 1, 0x04, 0x00000001 },
- { 0x001ed8, 1, 0x04, 0x00000001 },
- { 0x001ef8, 1, 0x04, 0x00000001 },
- { 0x003400, 128, 0x04, 0x00000000 },
- { 0x00030c, 1, 0x04, 0x00000001 },
- { 0x001944, 1, 0x04, 0x00000000 },
- { 0x001514, 1, 0x04, 0x00000000 },
- { 0x000d68, 1, 0x04, 0x0000ffff },
- { 0x00121c, 1, 0x04, 0x0fac6881 },
- { 0x000fac, 1, 0x04, 0x00000001 },
- { 0x001538, 1, 0x04, 0x00000001 },
- { 0x000fe0, 2, 0x04, 0x00000000 },
- { 0x000fe8, 1, 0x04, 0x00000014 },
- { 0x000fec, 1, 0x04, 0x00000040 },
- { 0x000ff0, 1, 0x04, 0x00000000 },
- { 0x00179c, 1, 0x04, 0x00000000 },
- { 0x001228, 1, 0x04, 0x00000400 },
- { 0x00122c, 1, 0x04, 0x00000300 },
- { 0x001230, 1, 0x04, 0x00010001 },
- { 0x0007f8, 1, 0x04, 0x00000000 },
- { 0x0015b4, 1, 0x04, 0x00000001 },
- { 0x0015cc, 1, 0x04, 0x00000000 },
- { 0x001534, 1, 0x04, 0x00000000 },
- { 0x000fb0, 1, 0x04, 0x00000000 },
- { 0x0015d0, 1, 0x04, 0x00000000 },
- { 0x00153c, 1, 0x04, 0x00000000 },
- { 0x0016b4, 1, 0x04, 0x00000003 },
- { 0x000fbc, 4, 0x04, 0x0000ffff },
- { 0x000df8, 2, 0x04, 0x00000000 },
- { 0x001948, 1, 0x04, 0x00000000 },
- { 0x001970, 1, 0x04, 0x00000001 },
- { 0x00161c, 1, 0x04, 0x000009f0 },
- { 0x000dcc, 1, 0x04, 0x00000010 },
- { 0x00163c, 1, 0x04, 0x00000000 },
- { 0x0015e4, 1, 0x04, 0x00000000 },
- { 0x001160, 32, 0x04, 0x25e00040 },
- { 0x001880, 32, 0x04, 0x00000000 },
- { 0x000f84, 2, 0x04, 0x00000000 },
- { 0x0017c8, 2, 0x04, 0x00000000 },
- { 0x0017d0, 1, 0x04, 0x000000ff },
- { 0x0017d4, 1, 0x04, 0xffffffff },
- { 0x0017d8, 1, 0x04, 0x00000002 },
- { 0x0017dc, 1, 0x04, 0x00000000 },
- { 0x0015f4, 2, 0x04, 0x00000000 },
- { 0x001434, 2, 0x04, 0x00000000 },
- { 0x000d74, 1, 0x04, 0x00000000 },
- { 0x000dec, 1, 0x04, 0x00000001 },
- { 0x0013a4, 1, 0x04, 0x00000000 },
- { 0x001318, 1, 0x04, 0x00000001 },
- { 0x001644, 1, 0x04, 0x00000000 },
- { 0x000748, 1, 0x04, 0x00000000 },
- { 0x000de8, 1, 0x04, 0x00000000 },
- { 0x001648, 1, 0x04, 0x00000000 },
- { 0x0012a4, 1, 0x04, 0x00000000 },
- { 0x001120, 4, 0x04, 0x00000000 },
- { 0x001118, 1, 0x04, 0x00000000 },
- { 0x00164c, 1, 0x04, 0x00000000 },
- { 0x001658, 1, 0x04, 0x00000000 },
- { 0x001910, 1, 0x04, 0x00000290 },
- { 0x001518, 1, 0x04, 0x00000000 },
- { 0x00165c, 1, 0x04, 0x00000001 },
- { 0x001520, 1, 0x04, 0x00000000 },
- { 0x001604, 1, 0x04, 0x00000000 },
- { 0x001570, 1, 0x04, 0x00000000 },
- { 0x0013b0, 2, 0x04, 0x3f800000 },
- { 0x00020c, 1, 0x04, 0x00000000 },
- { 0x001670, 1, 0x04, 0x30201000 },
- { 0x001674, 1, 0x04, 0x70605040 },
- { 0x001678, 1, 0x04, 0xb8a89888 },
- { 0x00167c, 1, 0x04, 0xf8e8d8c8 },
- { 0x00166c, 1, 0x04, 0x00000000 },
- { 0x001680, 1, 0x04, 0x00ffff00 },
- { 0x0012d0, 1, 0x04, 0x00000003 },
- { 0x0012d4, 1, 0x04, 0x00000002 },
- { 0x001684, 2, 0x04, 0x00000000 },
- { 0x000dac, 2, 0x04, 0x00001b02 },
- { 0x000db4, 1, 0x04, 0x00000000 },
- { 0x00168c, 1, 0x04, 0x00000000 },
- { 0x0015bc, 1, 0x04, 0x00000000 },
- { 0x00156c, 1, 0x04, 0x00000000 },
- { 0x00187c, 1, 0x04, 0x00000000 },
- { 0x001110, 1, 0x04, 0x00000001 },
- { 0x000dc0, 3, 0x04, 0x00000000 },
- { 0x001234, 1, 0x04, 0x00000000 },
- { 0x001690, 1, 0x04, 0x00000000 },
- { 0x0012ac, 1, 0x04, 0x00000001 },
- { 0x0002c4, 1, 0x04, 0x00000000 },
- { 0x000790, 5, 0x04, 0x00000000 },
- { 0x00077c, 1, 0x04, 0x00000000 },
- { 0x001000, 1, 0x04, 0x00000010 },
- { 0x0010fc, 1, 0x04, 0x00000000 },
- { 0x001290, 1, 0x04, 0x00000000 },
- { 0x000218, 1, 0x04, 0x00000010 },
- { 0x0012d8, 1, 0x04, 0x00000000 },
- { 0x0012dc, 1, 0x04, 0x00000010 },
- { 0x000d94, 1, 0x04, 0x00000001 },
- { 0x00155c, 2, 0x04, 0x00000000 },
- { 0x001564, 1, 0x04, 0x00000fff },
- { 0x001574, 2, 0x04, 0x00000000 },
- { 0x00157c, 1, 0x04, 0x000fffff },
- { 0x001354, 1, 0x04, 0x00000000 },
- { 0x001610, 1, 0x04, 0x00000012 },
- { 0x001608, 2, 0x04, 0x00000000 },
- { 0x00260c, 1, 0x04, 0x00000000 },
- { 0x0007ac, 1, 0x04, 0x00000000 },
- { 0x00162c, 1, 0x04, 0x00000003 },
- { 0x000210, 1, 0x04, 0x00000000 },
- { 0x000320, 1, 0x04, 0x00000000 },
- { 0x000324, 6, 0x04, 0x3f800000 },
- { 0x000750, 1, 0x04, 0x00000000 },
- { 0x000760, 1, 0x04, 0x39291909 },
- { 0x000764, 1, 0x04, 0x79695949 },
- { 0x000768, 1, 0x04, 0xb9a99989 },
- { 0x00076c, 1, 0x04, 0xf9e9d9c9 },
- { 0x000770, 1, 0x04, 0x30201000 },
- { 0x000774, 1, 0x04, 0x70605040 },
- { 0x000778, 1, 0x04, 0x00009080 },
- { 0x000780, 1, 0x04, 0x39291909 },
- { 0x000784, 1, 0x04, 0x79695949 },
- { 0x000788, 1, 0x04, 0xb9a99989 },
- { 0x00078c, 1, 0x04, 0xf9e9d9c9 },
- { 0x0007d0, 1, 0x04, 0x30201000 },
- { 0x0007d4, 1, 0x04, 0x70605040 },
- { 0x0007d8, 1, 0x04, 0x00009080 },
- { 0x00037c, 1, 0x04, 0x00000001 },
- { 0x000740, 2, 0x04, 0x00000000 },
- { 0x002600, 1, 0x04, 0x00000000 },
- { 0x001918, 1, 0x04, 0x00000000 },
- { 0x00191c, 1, 0x04, 0x00000900 },
- { 0x001920, 1, 0x04, 0x00000405 },
- { 0x001308, 1, 0x04, 0x00000001 },
- { 0x001924, 1, 0x04, 0x00000000 },
- { 0x0013ac, 1, 0x04, 0x00000000 },
- { 0x00192c, 1, 0x04, 0x00000001 },
- { 0x00193c, 1, 0x04, 0x00002c1c },
- { 0x000d7c, 1, 0x04, 0x00000000 },
- { 0x000f8c, 1, 0x04, 0x00000000 },
- { 0x0002c0, 1, 0x04, 0x00000001 },
- { 0x001510, 1, 0x04, 0x00000000 },
- { 0x001940, 1, 0x04, 0x00000000 },
- { 0x000ff4, 2, 0x04, 0x00000000 },
- { 0x00194c, 2, 0x04, 0x00000000 },
- { 0x001968, 1, 0x04, 0x00000000 },
- { 0x001590, 1, 0x04, 0x0000003f },
- { 0x0007e8, 4, 0x04, 0x00000000 },
- { 0x00196c, 1, 0x04, 0x00000011 },
- { 0x0002e4, 1, 0x04, 0x0000b001 },
- { 0x00036c, 2, 0x04, 0x00000000 },
- { 0x00197c, 1, 0x04, 0x00000000 },
- { 0x000fcc, 2, 0x04, 0x00000000 },
- { 0x0002d8, 1, 0x04, 0x00000040 },
- { 0x001980, 1, 0x04, 0x00000080 },
- { 0x001504, 1, 0x04, 0x00000080 },
- { 0x001984, 1, 0x04, 0x00000000 },
- { 0x000300, 1, 0x04, 0x00000001 },
- { 0x0013a8, 1, 0x04, 0x00000000 },
- { 0x0012ec, 1, 0x04, 0x00000000 },
- { 0x001310, 1, 0x04, 0x00000000 },
- { 0x001314, 1, 0x04, 0x00000001 },
- { 0x001380, 1, 0x04, 0x00000000 },
- { 0x001384, 4, 0x04, 0x00000001 },
- { 0x001394, 1, 0x04, 0x00000000 },
- { 0x00139c, 1, 0x04, 0x00000000 },
- { 0x001398, 1, 0x04, 0x00000000 },
- { 0x001594, 1, 0x04, 0x00000000 },
- { 0x001598, 4, 0x04, 0x00000001 },
- { 0x000f54, 3, 0x04, 0x00000000 },
- { 0x0019bc, 1, 0x04, 0x00000000 },
- { 0x000f9c, 2, 0x04, 0x00000000 },
- { 0x0012cc, 1, 0x04, 0x00000000 },
- { 0x0012e8, 1, 0x04, 0x00000000 },
- { 0x00130c, 1, 0x04, 0x00000001 },
- { 0x001360, 8, 0x04, 0x00000000 },
- { 0x00133c, 2, 0x04, 0x00000001 },
- { 0x001344, 1, 0x04, 0x00000002 },
- { 0x001348, 2, 0x04, 0x00000001 },
- { 0x001350, 1, 0x04, 0x00000002 },
- { 0x001358, 1, 0x04, 0x00000001 },
- { 0x0012e4, 1, 0x04, 0x00000000 },
- { 0x00131c, 4, 0x04, 0x00000000 },
- { 0x0019c0, 1, 0x04, 0x00000000 },
- { 0x001140, 1, 0x04, 0x00000000 },
- { 0x0019c4, 1, 0x04, 0x00000000 },
- { 0x0019c8, 1, 0x04, 0x00001500 },
- { 0x00135c, 1, 0x04, 0x00000000 },
- { 0x000f90, 1, 0x04, 0x00000000 },
- { 0x0019e0, 8, 0x04, 0x00000001 },
- { 0x0019cc, 1, 0x04, 0x00000001 },
- { 0x0015b8, 1, 0x04, 0x00000000 },
- { 0x001a00, 1, 0x04, 0x00001111 },
- { 0x001a04, 7, 0x04, 0x00000000 },
- { 0x000d6c, 2, 0x04, 0xffff0000 },
- { 0x0010f8, 1, 0x04, 0x00001010 },
- { 0x000d80, 5, 0x04, 0x00000000 },
- { 0x000da0, 1, 0x04, 0x00000000 },
- { 0x0007a4, 2, 0x04, 0x00000000 },
- { 0x001508, 1, 0x04, 0x80000000 },
- { 0x00150c, 1, 0x04, 0x40000000 },
- { 0x001668, 1, 0x04, 0x00000000 },
- { 0x000318, 2, 0x04, 0x00000008 },
- { 0x000d9c, 1, 0x04, 0x00000001 },
- { 0x000ddc, 1, 0x04, 0x00000002 },
- { 0x000374, 1, 0x04, 0x00000000 },
- { 0x000378, 1, 0x04, 0x00000020 },
- { 0x0007dc, 1, 0x04, 0x00000000 },
- { 0x00074c, 1, 0x04, 0x00000055 },
- { 0x001420, 1, 0x04, 0x00000003 },
- { 0x0017bc, 2, 0x04, 0x00000000 },
- { 0x0017c4, 1, 0x04, 0x00000001 },
- { 0x001008, 1, 0x04, 0x00000008 },
- { 0x00100c, 1, 0x04, 0x00000040 },
- { 0x001010, 1, 0x04, 0x0000012c },
- { 0x000d60, 1, 0x04, 0x00000040 },
- { 0x00075c, 1, 0x04, 0x00000003 },
- { 0x001018, 1, 0x04, 0x00000020 },
- { 0x00101c, 1, 0x04, 0x00000001 },
- { 0x001020, 1, 0x04, 0x00000020 },
- { 0x001024, 1, 0x04, 0x00000001 },
- { 0x001444, 3, 0x04, 0x00000000 },
- { 0x000360, 1, 0x04, 0x20164010 },
- { 0x000364, 1, 0x04, 0x00000020 },
- { 0x000368, 1, 0x04, 0x00000000 },
- { 0x000de4, 1, 0x04, 0x00000000 },
- { 0x000204, 1, 0x04, 0x00000006 },
- { 0x000208, 1, 0x04, 0x00000000 },
- { 0x0002cc, 2, 0x04, 0x003fffff },
- { 0x001220, 1, 0x04, 0x00000005 },
- { 0x000fdc, 1, 0x04, 0x00000000 },
- { 0x000f98, 1, 0x04, 0x00400008 },
- { 0x001284, 1, 0x04, 0x08000080 },
- { 0x001450, 1, 0x04, 0x00400008 },
- { 0x001454, 1, 0x04, 0x08000080 },
- { 0x000214, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nv108_grctx_pack_icmd[] = {
+ { nv108_grctx_init_icmd_0 },
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_unk40xx[] = {
+static const struct nvc0_graph_init
+nv108_grctx_init_fe_0[] = {
{ 0x404004, 8, 0x04, 0x00000000 },
{ 0x404024, 1, 0x04, 0x0000e000 },
{ 0x404028, 8, 0x04, 0x00000000 },
@@ -1132,8 +311,8 @@ nv108_grctx_init_unk40xx[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nv108_grctx_init_ds_0[] = {
{ 0x405800, 1, 0x04, 0x0f8000bf },
{ 0x405830, 1, 0x04, 0x02180648 },
{ 0x405834, 1, 0x04, 0x08000000 },
@@ -1146,8 +325,10 @@ nv108_grctx_init_unk58xx[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_unk64xx[] = {
+static const struct nvc0_graph_init
+nv108_grctx_init_pd_0[] = {
+ { 0x406020, 1, 0x04, 0x034103c1 },
+ { 0x406028, 4, 0x04, 0x00000001 },
{ 0x4064a8, 1, 0x04, 0x00000000 },
{ 0x4064ac, 1, 0x04, 0x00003fff },
{ 0x4064b0, 3, 0x04, 0x00000000 },
@@ -1159,8 +340,8 @@ nv108_grctx_init_unk64xx[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_unk78xx[] = {
+const struct nvc0_graph_init
+nv108_grctx_init_rstr2d_0[] = {
{ 0x407804, 1, 0x04, 0x00000063 },
{ 0x40780c, 1, 0x04, 0x0a418820 },
{ 0x407810, 1, 0x04, 0x062080e6 },
@@ -1172,8 +353,8 @@ nv108_grctx_init_unk78xx[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_unk88xx[] = {
+static const struct nvc0_graph_init
+nv108_grctx_init_be_0[] = {
{ 0x408800, 1, 0x04, 0x32802a3c },
{ 0x408804, 1, 0x04, 0x00000040 },
{ 0x408808, 1, 0x04, 0x1003e005 },
@@ -1185,9 +366,23 @@ nv108_grctx_init_unk88xx[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_gpc_0[] = {
- { 0x418380, 1, 0x04, 0x00000016 },
+static const struct nvc0_graph_pack
+nv108_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nv108_grctx_init_fe_0 },
+ { nvf0_grctx_init_pri_0 },
+ { nve4_grctx_init_memfmt_0 },
+ { nv108_grctx_init_ds_0 },
+ { nvf0_grctx_init_cwd_0 },
+ { nv108_grctx_init_pd_0 },
+ { nv108_grctx_init_rstr2d_0 },
+ { nve4_grctx_init_scc_0 },
+ { nv108_grctx_init_be_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nv108_grctx_init_prop_0[] = {
{ 0x418400, 1, 0x04, 0x38005e00 },
{ 0x418404, 1, 0x04, 0x71e0ffff },
{ 0x41840c, 1, 0x04, 0x00001008 },
@@ -1196,11 +391,21 @@ nv108_grctx_init_gpc_0[] = {
{ 0x418450, 6, 0x04, 0x00000000 },
{ 0x418468, 1, 0x04, 0x00000001 },
{ 0x41846c, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_grctx_init_gpc_unk_1[] = {
{ 0x418600, 1, 0x04, 0x0000007f },
{ 0x418684, 1, 0x04, 0x0000001f },
{ 0x418700, 1, 0x04, 0x00000002 },
{ 0x418704, 2, 0x04, 0x00000080 },
{ 0x41870c, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x7006863a },
{ 0x418808, 1, 0x04, 0x00000000 },
{ 0x41880c, 1, 0x04, 0x00000030 },
@@ -1211,10 +416,11 @@ nv108_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x20100058 },
- { 0x41891c, 1, 0x04, 0x00ff00ff },
- { 0x418924, 1, 0x04, 0x00000000 },
- { 0x418928, 1, 0x04, 0x00ffff00 },
- { 0x41892c, 1, 0x04, 0x0000ff00 },
+ {}
+};
+
+const struct nvc0_graph_init
+nv108_grctx_init_crstr_0[] = {
{ 0x418b00, 1, 0x04, 0x0000001e },
{ 0x418b08, 1, 0x04, 0x0a418820 },
{ 0x418b0c, 1, 0x04, 0x062080e6 },
@@ -1223,24 +429,36 @@ nv108_grctx_init_gpc_0[] = {
{ 0x418b18, 1, 0x04, 0x0a418820 },
{ 0x418b1c, 1, 0x04, 0x000000e6 },
{ 0x418bb8, 1, 0x04, 0x00000103 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_grctx_init_gpm_0[] = {
{ 0x418c08, 1, 0x04, 0x00000001 },
{ 0x418c10, 8, 0x04, 0x00000000 },
{ 0x418c40, 1, 0x04, 0xffffffff },
{ 0x418c6c, 1, 0x04, 0x00000001 },
{ 0x418c80, 1, 0x04, 0x2020000c },
{ 0x418c8c, 1, 0x04, 0x00000001 },
- { 0x418d24, 1, 0x04, 0x00000000 },
- { 0x419000, 1, 0x04, 0x00000780 },
- { 0x419004, 2, 0x04, 0x00000000 },
- { 0x419014, 1, 0x04, 0x00000004 },
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_tpc[] = {
- { 0x419848, 1, 0x04, 0x00000000 },
- { 0x419864, 1, 0x04, 0x00000129 },
- { 0x419888, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nv108_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nv108_grctx_init_prop_0 },
+ { nv108_grctx_init_gpc_unk_1 },
+ { nv108_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nv108_grctx_init_crstr_0 },
+ { nv108_grctx_init_gpm_0 },
+ { nvf0_grctx_init_gpc_unk_2 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000100f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000421 },
@@ -1251,14 +469,11 @@ nv108_grctx_init_tpc[] = {
{ 0x419a20, 1, 0x04, 0x00000800 },
{ 0x419a30, 1, 0x04, 0x00000001 },
{ 0x419ac4, 1, 0x04, 0x0037f440 },
- { 0x419c00, 1, 0x04, 0x0000001a },
- { 0x419c04, 1, 0x04, 0x80000006 },
- { 0x419c08, 1, 0x04, 0x00000002 },
- { 0x419c20, 1, 0x04, 0x00000000 },
- { 0x419c24, 1, 0x04, 0x00084210 },
- { 0x419c28, 1, 0x04, 0x3efbefbe },
- { 0x419ce8, 1, 0x04, 0x00000000 },
- { 0x419cf4, 1, 0x04, 0x00000203 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_grctx_init_sm_0[] = {
{ 0x419e04, 1, 0x04, 0x00000000 },
{ 0x419e08, 1, 0x04, 0x0000001d },
{ 0x419e0c, 1, 0x04, 0x00000000 },
@@ -1272,7 +487,7 @@ nv108_grctx_init_tpc[] = {
{ 0x419e68, 1, 0x04, 0x00000002 },
{ 0x419e6c, 12, 0x04, 0x00000000 },
{ 0x419eac, 1, 0x04, 0x00001f8f },
- { 0x419eb0, 1, 0x04, 0x0db00da0 },
+ { 0x419eb0, 1, 0x04, 0x0db00d2f },
{ 0x419eb8, 1, 0x04, 0x00000000 },
{ 0x419ec8, 1, 0x04, 0x0001304f },
{ 0x419f30, 4, 0x04, 0x00000000 },
@@ -1285,25 +500,37 @@ nv108_grctx_init_tpc[] = {
{}
};
-static struct nvc0_graph_init
-nv108_grctx_init_unk[] = {
- { 0x41be24, 1, 0x04, 0x00000006 },
+static const struct nvc0_graph_pack
+nv108_grctx_pack_tpc[] = {
+ { nvd7_grctx_init_pe_0 },
+ { nv108_grctx_init_tex_0 },
+ { nvf0_grctx_init_mpc_0 },
+ { nvf0_grctx_init_l1c_0 },
+ { nv108_grctx_init_sm_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_grctx_init_cbm_0[] = {
{ 0x41bec0, 1, 0x04, 0x10000000 },
{ 0x41bec4, 1, 0x04, 0x00037f7f },
{ 0x41bee4, 1, 0x04, 0x00000000 },
{ 0x41bef0, 1, 0x04, 0x000003ff },
- { 0x41bf00, 1, 0x04, 0x0a418820 },
- { 0x41bf04, 1, 0x04, 0x062080e6 },
- { 0x41bf08, 1, 0x04, 0x020398a4 },
- { 0x41bf0c, 1, 0x04, 0x0e629062 },
- { 0x41bf10, 1, 0x04, 0x0a418820 },
- { 0x41bf14, 1, 0x04, 0x000000e6 },
- { 0x41bfd0, 1, 0x04, 0x00900103 },
- { 0x41bfe0, 1, 0x04, 0x00400001 },
- { 0x41bfe4, 1, 0x04, 0x00000000 },
{}
};
+static const struct nvc0_graph_pack
+nv108_grctx_pack_ppc[] = {
+ { nve4_grctx_init_pes_0 },
+ { nv108_grctx_init_cbm_0 },
+ { nvd7_grctx_init_wwdx_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
static void
nv108_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
@@ -1346,47 +573,6 @@ nv108_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
mmio_list(0x17e920, 0x00090d08, 0, 0);
}
-static struct nvc0_graph_init *
-nv108_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nv108_grctx_init_unk40xx,
- nvf0_grctx_init_unk44xx,
- nve4_grctx_init_unk46xx,
- nve4_grctx_init_unk47xx,
- nv108_grctx_init_unk58xx,
- nvf0_grctx_init_unk5bxx,
- nvf0_grctx_init_unk60xx,
- nv108_grctx_init_unk64xx,
- nv108_grctx_init_unk78xx,
- nve4_grctx_init_unk80xx,
- nv108_grctx_init_unk88xx,
- NULL
-};
-
-struct nvc0_graph_init *
-nv108_grctx_init_gpc[] = {
- nv108_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nv108_grctx_init_tpc,
- nv108_grctx_init_unk,
- NULL
-};
-
-struct nvc0_graph_init
-nv108_grctx_init_mthd_magic[] = {
- { 0x3410, 1, 0x04, 0x8e0e2006 },
- { 0x3414, 1, 0x04, 0x00000038 },
- {}
-};
-
-static struct nvc0_graph_mthd
-nv108_grctx_init_mthd[] = {
- { 0xa197, nv108_grctx_init_a197, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x902d, nv108_grctx_init_mthd_magic, },
- {}
-};
-
struct nouveau_oclass *
nv108_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0x08),
@@ -1398,11 +584,14 @@ nv108_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nve4_grctx_generate_main,
- .mods = nv108_grctx_generate_mods,
- .unkn = nve4_grctx_generate_unkn,
- .hub = nv108_grctx_init_hub,
- .gpc = nv108_grctx_init_gpc,
- .icmd = nv108_grctx_init_icmd,
- .mthd = nv108_grctx_init_mthd,
+ .main = nve4_grctx_generate_main,
+ .mods = nv108_grctx_generate_mods,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = nv108_grctx_pack_hub,
+ .gpc = nv108_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nv108_grctx_pack_tpc,
+ .ppc = nv108_grctx_pack_ppc,
+ .icmd = nv108_grctx_pack_icmd,
+ .mthd = nvf0_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
index fe67415c3e17..833a96508c4e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
@@ -22,10 +22,14 @@
* Authors: Ben Skeggs
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-struct nvc0_graph_init
-nvc0_grctx_init_icmd[] = {
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_icmd_0[] = {
{ 0x001000, 1, 0x01, 0x00000004 },
{ 0x0000a9, 1, 0x01, 0x0000ffff },
{ 0x000038, 1, 0x01, 0x0fac6881 },
@@ -140,8 +144,7 @@ nvc0_grctx_init_icmd[] = {
{ 0x000586, 1, 0x01, 0x00000040 },
{ 0x000582, 2, 0x01, 0x00000080 },
{ 0x0005c2, 1, 0x01, 0x00000001 },
- { 0x000638, 1, 0x01, 0x00000001 },
- { 0x000639, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
{ 0x00063a, 1, 0x01, 0x00000002 },
{ 0x00063b, 2, 0x01, 0x00000001 },
{ 0x00063d, 1, 0x01, 0x00000002 },
@@ -201,15 +204,13 @@ nvc0_grctx_init_icmd[] = {
{ 0x000787, 1, 0x01, 0x000000cf },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x000836, 1, 0x01, 0x00000001 },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x00080c, 1, 0x01, 0x00000002 },
{ 0x00080d, 2, 0x01, 0x00000100 },
@@ -235,14 +236,12 @@ nvc0_grctx_init_icmd[] = {
{ 0x0006b1, 1, 0x01, 0x00000011 },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x01e100, 1, 0x01, 0x00000001 },
{ 0x001000, 1, 0x01, 0x00000014 },
@@ -267,8 +266,14 @@ nvc0_grctx_init_icmd[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_9097[] = {
+const struct nvc0_graph_pack
+nvc0_grctx_pack_icmd[] = {
+ { nvc0_grctx_init_icmd_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_9097_0[] = {
{ 0x000800, 8, 0x40, 0x00000000 },
{ 0x000804, 8, 0x40, 0x00000000 },
{ 0x000808, 8, 0x40, 0x00000400 },
@@ -516,8 +521,7 @@ nvc0_grctx_init_9097[] = {
{ 0x001350, 1, 0x04, 0x00000002 },
{ 0x001358, 1, 0x04, 0x00000001 },
{ 0x0012e4, 1, 0x04, 0x00000000 },
- { 0x00131c, 1, 0x04, 0x00000000 },
- { 0x001320, 3, 0x04, 0x00000000 },
+ { 0x00131c, 4, 0x04, 0x00000000 },
{ 0x0019c0, 1, 0x04, 0x00000000 },
{ 0x001140, 1, 0x04, 0x00000000 },
{ 0x0019c4, 1, 0x04, 0x00000000 },
@@ -571,8 +575,8 @@ nvc0_grctx_init_9097[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_902d[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_902d_0[] = {
{ 0x000200, 1, 0x04, 0x000000cf },
{ 0x000204, 1, 0x04, 0x00000001 },
{ 0x000208, 1, 0x04, 0x00000020 },
@@ -590,8 +594,8 @@ nvc0_grctx_init_902d[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_9039[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_9039_0[] = {
{ 0x00030c, 3, 0x04, 0x00000000 },
{ 0x000320, 1, 0x04, 0x00000000 },
{ 0x000238, 2, 0x04, 0x00000000 },
@@ -599,8 +603,8 @@ nvc0_grctx_init_9039[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_90c0[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_90c0_0[] = {
{ 0x00270c, 8, 0x20, 0x00000000 },
{ 0x00030c, 1, 0x04, 0x00000001 },
{ 0x001944, 1, 0x04, 0x00000000 },
@@ -617,38 +621,44 @@ nvc0_grctx_init_90c0[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_base[] = {
+const struct nvc0_graph_pack
+nvc0_grctx_pack_mthd[] = {
+ { nvc0_grctx_init_9097_0, 0x9097 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ { nvc0_grctx_init_9039_0, 0x9039 },
+ { nvc0_grctx_init_90c0_0, 0x90c0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_main_0[] = {
{ 0x400204, 2, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk40xx[] = {
- { 0x404004, 10, 0x04, 0x00000000 },
+const struct nvc0_graph_init
+nvc0_grctx_init_fe_0[] = {
+ { 0x404004, 11, 0x04, 0x00000000 },
{ 0x404044, 1, 0x04, 0x00000000 },
- { 0x404094, 1, 0x04, 0x00000000 },
- { 0x404098, 12, 0x04, 0x00000000 },
+ { 0x404094, 13, 0x04, 0x00000000 },
{ 0x4040c8, 1, 0x04, 0xf0000087 },
{ 0x4040d0, 6, 0x04, 0x00000000 },
{ 0x4040e8, 1, 0x04, 0x00001000 },
{ 0x4040f8, 1, 0x04, 0x00000000 },
- { 0x404130, 1, 0x04, 0x00000000 },
- { 0x404134, 1, 0x04, 0x00000000 },
+ { 0x404130, 2, 0x04, 0x00000000 },
{ 0x404138, 1, 0x04, 0x20000040 },
{ 0x404150, 1, 0x04, 0x0000002e },
{ 0x404154, 1, 0x04, 0x00000400 },
{ 0x404158, 1, 0x04, 0x00000200 },
{ 0x404164, 1, 0x04, 0x00000055 },
{ 0x404168, 1, 0x04, 0x00000000 },
- { 0x404174, 1, 0x04, 0x00000000 },
- { 0x404178, 2, 0x04, 0x00000000 },
+ { 0x404174, 3, 0x04, 0x00000000 },
{ 0x404200, 8, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk44xx[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_pri_0[] = {
{ 0x404404, 14, 0x04, 0x00000000 },
{ 0x404460, 2, 0x04, 0x00000000 },
{ 0x404468, 1, 0x04, 0x00ffffff },
@@ -658,8 +668,8 @@ nvc0_grctx_init_unk44xx[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk46xx[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_memfmt_0[] = {
{ 0x404604, 1, 0x04, 0x00000015 },
{ 0x404608, 1, 0x04, 0x00000000 },
{ 0x40460c, 1, 0x04, 0x00002e00 },
@@ -674,19 +684,14 @@ nvc0_grctx_init_unk46xx[] = {
{ 0x4046a0, 1, 0x04, 0x007f0080 },
{ 0x4046a4, 18, 0x04, 0x00000000 },
{ 0x4046f0, 2, 0x04, 0x00000000 },
- {}
-};
-
-struct nvc0_graph_init
-nvc0_grctx_init_unk47xx[] = {
{ 0x404700, 13, 0x04, 0x00000000 },
{ 0x404734, 1, 0x04, 0x00000100 },
{ 0x404738, 8, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nvc0_grctx_init_ds_0[] = {
{ 0x405800, 1, 0x04, 0x078000bf },
{ 0x405830, 1, 0x04, 0x02180000 },
{ 0x405834, 2, 0x04, 0x00000000 },
@@ -697,23 +702,18 @@ nvc0_grctx_init_unk58xx[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk60xx[] = {
+static const struct nvc0_graph_init
+nvc0_grctx_init_pd_0[] = {
{ 0x406020, 1, 0x04, 0x000103c1 },
{ 0x406028, 4, 0x04, 0x00000001 },
- {}
-};
-
-struct nvc0_graph_init
-nvc0_grctx_init_unk64xx[] = {
{ 0x4064a8, 1, 0x04, 0x00000000 },
{ 0x4064ac, 1, 0x04, 0x00003fff },
{ 0x4064b4, 2, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk78xx[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_rstr2d_0[] = {
{ 0x407804, 1, 0x04, 0x00000023 },
{ 0x40780c, 1, 0x04, 0x0a418820 },
{ 0x407810, 1, 0x04, 0x062080e6 },
@@ -725,8 +725,8 @@ nvc0_grctx_init_unk78xx[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_unk80xx[] = {
+const struct nvc0_graph_init
+nvc0_grctx_init_scc_0[] = {
{ 0x408000, 2, 0x04, 0x00000000 },
{ 0x408008, 1, 0x04, 0x00000018 },
{ 0x40800c, 2, 0x04, 0x00000000 },
@@ -736,8 +736,8 @@ nvc0_grctx_init_unk80xx[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_rop[] = {
+static const struct nvc0_graph_init
+nvc0_grctx_init_be_0[] = {
{ 0x408800, 1, 0x04, 0x02802a3c },
{ 0x408804, 1, 0x04, 0x00000040 },
{ 0x408808, 1, 0x04, 0x0003e00d },
@@ -748,9 +748,28 @@ nvc0_grctx_init_rop[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_gpc_0[] = {
+const struct nvc0_graph_pack
+nvc0_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nvc0_grctx_init_fe_0 },
+ { nvc0_grctx_init_pri_0 },
+ { nvc0_grctx_init_memfmt_0 },
+ { nvc0_grctx_init_ds_0 },
+ { nvc0_grctx_init_pd_0 },
+ { nvc0_grctx_init_rstr2d_0 },
+ { nvc0_grctx_init_scc_0 },
+ { nvc0_grctx_init_be_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_gpc_unk_0[] = {
{ 0x418380, 1, 0x04, 0x00000016 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_prop_0[] = {
{ 0x418400, 1, 0x04, 0x38004e00 },
{ 0x418404, 1, 0x04, 0x71e0ffff },
{ 0x418408, 1, 0x04, 0x00000000 },
@@ -760,6 +779,11 @@ nvc0_grctx_init_gpc_0[] = {
{ 0x418450, 6, 0x04, 0x00000000 },
{ 0x418468, 1, 0x04, 0x00000001 },
{ 0x41846c, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_gpc_unk_1[] = {
{ 0x418600, 1, 0x04, 0x0000001f },
{ 0x418684, 1, 0x04, 0x0000000f },
{ 0x418700, 1, 0x04, 0x00000002 },
@@ -767,6 +791,11 @@ nvc0_grctx_init_gpc_0[] = {
{ 0x418708, 1, 0x04, 0x00000000 },
{ 0x41870c, 1, 0x04, 0x07c80000 },
{ 0x418710, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x0006860a },
{ 0x418808, 3, 0x04, 0x00000000 },
{ 0x418828, 1, 0x04, 0x00008442 },
@@ -775,10 +804,20 @@ nvc0_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x00100000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_zcull_0[] = {
{ 0x41891c, 1, 0x04, 0x00ff00ff },
{ 0x418924, 1, 0x04, 0x00000000 },
{ 0x418928, 1, 0x04, 0x00ffff00 },
{ 0x41892c, 1, 0x04, 0x0000ff00 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_crstr_0[] = {
{ 0x418b00, 1, 0x04, 0x00000000 },
{ 0x418b08, 1, 0x04, 0x0a418820 },
{ 0x418b0c, 1, 0x04, 0x062080e6 },
@@ -787,18 +826,41 @@ nvc0_grctx_init_gpc_0[] = {
{ 0x418b18, 1, 0x04, 0x0a418820 },
{ 0x418b1c, 1, 0x04, 0x000000e6 },
{ 0x418bb8, 1, 0x04, 0x00000103 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_gpm_0[] = {
{ 0x418c08, 1, 0x04, 0x00000001 },
{ 0x418c10, 8, 0x04, 0x00000000 },
{ 0x418c80, 1, 0x04, 0x20200004 },
{ 0x418c8c, 1, 0x04, 0x00000001 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_gcc_0[] = {
{ 0x419000, 1, 0x04, 0x00000780 },
{ 0x419004, 2, 0x04, 0x00000000 },
{ 0x419014, 1, 0x04, 0x00000004 },
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_gpc_1[] = {
+const struct nvc0_graph_pack
+nvc0_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvc0_grctx_init_prop_0 },
+ { nvc0_grctx_init_gpc_unk_1 },
+ { nvc0_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvc0_grctx_init_crstr_0 },
+ { nvc0_grctx_init_gpm_0 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_zcullr_0[] = {
{ 0x418a00, 3, 0x04, 0x00000000 },
{ 0x418a0c, 1, 0x04, 0x00010000 },
{ 0x418a10, 3, 0x04, 0x00000000 },
@@ -826,19 +888,35 @@ nvc0_grctx_init_gpc_1[] = {
{}
};
-struct nvc0_graph_init
-nvc0_grctx_init_tpc[] = {
+const struct nvc0_graph_pack
+nvc0_grctx_pack_zcull[] = {
+ { nvc0_grctx_init_zcullr_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_pe_0[] = {
{ 0x419818, 1, 0x04, 0x00000000 },
{ 0x41983c, 1, 0x04, 0x00038bc7 },
{ 0x419848, 1, 0x04, 0x00000000 },
{ 0x419864, 1, 0x04, 0x0000012a },
{ 0x419888, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000001f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000023 },
{ 0x419a0c, 1, 0x04, 0x00020000 },
{ 0x419a10, 1, 0x04, 0x00000000 },
{ 0x419a14, 1, 0x04, 0x00000200 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_wwdx_0[] = {
{ 0x419b00, 1, 0x04, 0x0a418820 },
{ 0x419b04, 1, 0x04, 0x062080e6 },
{ 0x419b08, 1, 0x04, 0x020398a4 },
@@ -848,15 +926,35 @@ nvc0_grctx_init_tpc[] = {
{ 0x419bd0, 1, 0x04, 0x00900103 },
{ 0x419be0, 1, 0x04, 0x00000001 },
{ 0x419be4, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_mpc_0[] = {
{ 0x419c00, 1, 0x04, 0x00000002 },
{ 0x419c04, 1, 0x04, 0x00000006 },
{ 0x419c08, 1, 0x04, 0x00000002 },
{ 0x419c20, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_l1c_0[] = {
{ 0x419cb0, 1, 0x04, 0x00060048 },
{ 0x419ce8, 1, 0x04, 0x00000000 },
{ 0x419cf4, 1, 0x04, 0x00000183 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_grctx_init_tpccs_0[] = {
{ 0x419d20, 1, 0x04, 0x02180000 },
{ 0x419d24, 1, 0x04, 0x00001fff },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_grctx_init_sm_0[] = {
{ 0x419e04, 3, 0x04, 0x00000000 },
{ 0x419e10, 1, 0x04, 0x00000002 },
{ 0x419e44, 1, 0x04, 0x001beff2 },
@@ -868,6 +966,22 @@ nvc0_grctx_init_tpc[] = {
{}
};
+const struct nvc0_graph_pack
+nvc0_grctx_pack_tpc[] = {
+ { nvc0_grctx_init_pe_0 },
+ { nvc0_grctx_init_tex_0 },
+ { nvc0_grctx_init_wwdx_0 },
+ { nvc0_grctx_init_mpc_0 },
+ { nvc0_grctx_init_l1c_0 },
+ { nvc0_grctx_init_tpccs_0 },
+ { nvc0_grctx_init_sm_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
void
nvc0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
@@ -1055,14 +1169,14 @@ void
nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
- int i;
nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
- for (i = 0; oclass->hub[i]; i++)
- nvc0_graph_mmio(priv, oclass->hub[i]);
- for (i = 0; oclass->gpc[i]; i++)
- nvc0_graph_mmio(priv, oclass->gpc[i]);
+ nvc0_graph_mmio(priv, oclass->hub);
+ nvc0_graph_mmio(priv, oclass->gpc);
+ nvc0_graph_mmio(priv, oclass->zcull);
+ nvc0_graph_mmio(priv, oclass->tpc);
+ nvc0_graph_mmio(priv, oclass->ppc);
nv_wr32(priv, 0x404154, 0x00000000);
@@ -1182,46 +1296,6 @@ done:
return ret;
}
-struct nvc0_graph_init *
-nvc0_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nvc0_grctx_init_unk40xx,
- nvc0_grctx_init_unk44xx,
- nvc0_grctx_init_unk46xx,
- nvc0_grctx_init_unk47xx,
- nvc0_grctx_init_unk58xx,
- nvc0_grctx_init_unk60xx,
- nvc0_grctx_init_unk64xx,
- nvc0_grctx_init_unk78xx,
- nvc0_grctx_init_unk80xx,
- nvc0_grctx_init_rop,
- NULL
-};
-
-static struct nvc0_graph_init *
-nvc0_grctx_init_gpc[] = {
- nvc0_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvc0_grctx_init_tpc,
- NULL
-};
-
-struct nvc0_graph_init
-nvc0_grctx_init_mthd_magic[] = {
- { 0x3410, 1, 0x04, 0x00000000 },
- {}
-};
-
-struct nvc0_graph_mthd
-nvc0_grctx_init_mthd[] = {
- { 0x9097, nvc0_grctx_init_9097, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x9039, nvc0_grctx_init_9039, },
- { 0x90c0, nvc0_grctx_init_90c0, },
- { 0x902d, nvc0_grctx_init_mthd_magic, },
- {}
-};
-
struct nouveau_oclass *
nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xc0),
@@ -1233,11 +1307,13 @@ nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nvc0_grctx_generate_main,
- .mods = nvc0_grctx_generate_mods,
- .unkn = nvc0_grctx_generate_unkn,
- .hub = nvc0_grctx_init_hub,
- .gpc = nvc0_grctx_init_gpc,
- .icmd = nvc0_grctx_init_icmd,
- .mthd = nvc0_grctx_init_mthd,
+ .main = nvc0_grctx_generate_main,
+ .mods = nvc0_grctx_generate_mods,
+ .unkn = nvc0_grctx_generate_unkn,
+ .hub = nvc0_grctx_pack_hub,
+ .gpc = nvc0_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvc0_grctx_pack_tpc,
+ .icmd = nvc0_grctx_pack_icmd,
+ .mthd = nvc0_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
new file mode 100644
index 000000000000..9c815d1f99ef
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
@@ -0,0 +1,170 @@
+#ifndef __NVKM_GRCTX_NVC0_H__
+#define __NVKM_GRCTX_NVC0_H__
+
+#include "nvc0.h"
+
+struct nvc0_grctx {
+ struct nvc0_graph_priv *priv;
+ struct nvc0_graph_data *data;
+ struct nvc0_graph_mmio *mmio;
+ int buffer_nr;
+ u64 buffer[4];
+ u64 addr;
+};
+
+struct nvc0_grctx_oclass {
+ struct nouveau_oclass base;
+ /* main context generation function */
+ void (*main)(struct nvc0_graph_priv *, struct nvc0_grctx *);
+ /* context-specific modify-on-first-load list generation function */
+ void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *);
+ void (*unkn)(struct nvc0_graph_priv *);
+ /* mmio context data */
+ const struct nvc0_graph_pack *hub;
+ const struct nvc0_graph_pack *gpc;
+ const struct nvc0_graph_pack *zcull;
+ const struct nvc0_graph_pack *tpc;
+ const struct nvc0_graph_pack *ppc;
+ /* indirect context data, generated with icmds/mthds */
+ const struct nvc0_graph_pack *icmd;
+ const struct nvc0_graph_pack *mthd;
+};
+
+#define mmio_data(s,a,p) do { \
+ info->buffer[info->buffer_nr] = round_up(info->addr, (a)); \
+ info->addr = info->buffer[info->buffer_nr++] + (s); \
+ info->data->size = (s); \
+ info->data->align = (a); \
+ info->data->access = (p); \
+ info->data++; \
+} while(0)
+
+#define mmio_list(r,d,s,b) do { \
+ info->mmio->addr = (r); \
+ info->mmio->data = (d); \
+ info->mmio->shift = (s); \
+ info->mmio->buffer = (b); \
+ info->mmio++; \
+ nv_wr32(priv, (r), (d) | ((s) ? (info->buffer[(b)] >> (s)) : 0)); \
+} while(0)
+
+extern struct nouveau_oclass *nvc0_grctx_oclass;
+int nvc0_grctx_generate(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc0_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc0_grctx_generate_unkn(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *);
+
+extern struct nouveau_oclass *nvc1_grctx_oclass;
+void nvc1_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc1_grctx_generate_unkn(struct nvc0_graph_priv *);
+
+extern struct nouveau_oclass *nvc4_grctx_oclass;
+extern struct nouveau_oclass *nvc8_grctx_oclass;
+extern struct nouveau_oclass *nvd7_grctx_oclass;
+extern struct nouveau_oclass *nvd9_grctx_oclass;
+
+extern struct nouveau_oclass *nve4_grctx_oclass;
+void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nve4_grctx_generate_unkn(struct nvc0_graph_priv *);
+void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *);
+
+extern struct nouveau_oclass *nvf0_grctx_oclass;
+extern struct nouveau_oclass *nv108_grctx_oclass;
+extern struct nouveau_oclass *gm107_grctx_oclass;
+
+/* context init value lists */
+
+extern const struct nvc0_graph_pack nvc0_grctx_pack_icmd[];
+
+extern const struct nvc0_graph_pack nvc0_grctx_pack_mthd[];
+extern const struct nvc0_graph_init nvc0_grctx_init_902d_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_9039_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_90c0_0[];
+
+extern const struct nvc0_graph_pack nvc0_grctx_pack_hub[];
+extern const struct nvc0_graph_init nvc0_grctx_init_main_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_fe_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_pri_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_memfmt_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_rstr2d_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_scc_0[];
+
+extern const struct nvc0_graph_pack nvc0_grctx_pack_gpc[];
+extern const struct nvc0_graph_init nvc0_grctx_init_gpc_unk_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_prop_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_gpc_unk_1[];
+extern const struct nvc0_graph_init nvc0_grctx_init_zcull_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_crstr_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_gpm_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_gcc_0[];
+
+extern const struct nvc0_graph_pack nvc0_grctx_pack_zcull[];
+
+extern const struct nvc0_graph_pack nvc0_grctx_pack_tpc[];
+extern const struct nvc0_graph_init nvc0_grctx_init_pe_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_wwdx_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_mpc_0[];
+extern const struct nvc0_graph_init nvc0_grctx_init_tpccs_0[];
+
+extern const struct nvc0_graph_init nvc4_grctx_init_tex_0[];
+extern const struct nvc0_graph_init nvc4_grctx_init_l1c_0[];
+extern const struct nvc0_graph_init nvc4_grctx_init_sm_0[];
+
+extern const struct nvc0_graph_init nvc1_grctx_init_9097_0[];
+
+extern const struct nvc0_graph_init nvc1_grctx_init_gpm_0[];
+
+extern const struct nvc0_graph_init nvc1_grctx_init_pe_0[];
+extern const struct nvc0_graph_init nvc1_grctx_init_wwdx_0[];
+extern const struct nvc0_graph_init nvc1_grctx_init_tpccs_0[];
+
+extern const struct nvc0_graph_init nvc8_grctx_init_9197_0[];
+extern const struct nvc0_graph_init nvc8_grctx_init_9297_0[];
+
+extern const struct nvc0_graph_pack nvd9_grctx_pack_icmd[];
+
+extern const struct nvc0_graph_pack nvd9_grctx_pack_mthd[];
+
+extern const struct nvc0_graph_init nvd9_grctx_init_fe_0[];
+extern const struct nvc0_graph_init nvd9_grctx_init_be_0[];
+
+extern const struct nvc0_graph_init nvd9_grctx_init_prop_0[];
+extern const struct nvc0_graph_init nvd9_grctx_init_gpc_unk_1[];
+extern const struct nvc0_graph_init nvd9_grctx_init_crstr_0[];
+
+extern const struct nvc0_graph_init nvd9_grctx_init_sm_0[];
+
+extern const struct nvc0_graph_init nvd7_grctx_init_pe_0[];
+
+extern const struct nvc0_graph_init nvd7_grctx_init_wwdx_0[];
+
+extern const struct nvc0_graph_init nve4_grctx_init_memfmt_0[];
+extern const struct nvc0_graph_init nve4_grctx_init_ds_0[];
+extern const struct nvc0_graph_init nve4_grctx_init_scc_0[];
+
+extern const struct nvc0_graph_init nve4_grctx_init_gpm_0[];
+
+extern const struct nvc0_graph_init nve4_grctx_init_pes_0[];
+
+extern const struct nvc0_graph_pack nvf0_grctx_pack_mthd[];
+
+extern const struct nvc0_graph_init nvf0_grctx_init_pri_0[];
+extern const struct nvc0_graph_init nvf0_grctx_init_cwd_0[];
+
+extern const struct nvc0_graph_init nvf0_grctx_init_gpc_unk_2[];
+
+extern const struct nvc0_graph_init nvf0_grctx_init_mpc_0[];
+extern const struct nvc0_graph_init nvf0_grctx_init_l1c_0[];
+
+extern const struct nvc0_graph_init nv108_grctx_init_rstr2d_0[];
+
+extern const struct nvc0_graph_init nv108_grctx_init_prop_0[];
+extern const struct nvc0_graph_init nv108_grctx_init_crstr_0[];
+
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
index 71b4283f7fad..24a92c569c0a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
@@ -22,10 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-static struct nvc0_graph_init
-nvc1_grctx_init_icmd[] = {
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nvc1_grctx_init_icmd_0[] = {
{ 0x001000, 1, 0x01, 0x00000004 },
{ 0x0000a9, 1, 0x01, 0x0000ffff },
{ 0x000038, 1, 0x01, 0x0fac6881 },
@@ -141,8 +145,7 @@ nvc1_grctx_init_icmd[] = {
{ 0x000586, 1, 0x01, 0x00000040 },
{ 0x000582, 2, 0x01, 0x00000080 },
{ 0x0005c2, 1, 0x01, 0x00000001 },
- { 0x000638, 1, 0x01, 0x00000001 },
- { 0x000639, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
{ 0x00063a, 1, 0x01, 0x00000002 },
{ 0x00063b, 2, 0x01, 0x00000001 },
{ 0x00063d, 1, 0x01, 0x00000002 },
@@ -202,15 +205,13 @@ nvc1_grctx_init_icmd[] = {
{ 0x000787, 1, 0x01, 0x000000cf },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x000836, 1, 0x01, 0x00000001 },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x00080c, 1, 0x01, 0x00000002 },
{ 0x00080d, 2, 0x01, 0x00000100 },
@@ -236,14 +237,12 @@ nvc1_grctx_init_icmd[] = {
{ 0x0006b1, 1, 0x01, 0x00000011 },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x01e100, 1, 0x01, 0x00000001 },
{ 0x001000, 1, 0x01, 0x00000014 },
@@ -268,8 +267,14 @@ nvc1_grctx_init_icmd[] = {
{}
};
-struct nvc0_graph_init
-nvc1_grctx_init_9097[] = {
+static const struct nvc0_graph_pack
+nvc1_grctx_pack_icmd[] = {
+ { nvc1_grctx_init_icmd_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc1_grctx_init_9097_0[] = {
{ 0x000800, 8, 0x40, 0x00000000 },
{ 0x000804, 8, 0x40, 0x00000000 },
{ 0x000808, 8, 0x40, 0x00000400 },
@@ -516,8 +521,7 @@ nvc1_grctx_init_9097[] = {
{ 0x001350, 1, 0x04, 0x00000002 },
{ 0x001358, 1, 0x04, 0x00000001 },
{ 0x0012e4, 1, 0x04, 0x00000000 },
- { 0x00131c, 1, 0x04, 0x00000000 },
- { 0x001320, 3, 0x04, 0x00000000 },
+ { 0x00131c, 4, 0x04, 0x00000000 },
{ 0x0019c0, 1, 0x04, 0x00000000 },
{ 0x001140, 1, 0x04, 0x00000000 },
{ 0x0019c4, 1, 0x04, 0x00000000 },
@@ -571,15 +575,25 @@ nvc1_grctx_init_9097[] = {
{}
};
-static struct nvc0_graph_init
-nvc1_grctx_init_9197[] = {
+static const struct nvc0_graph_init
+nvc1_grctx_init_9197_0[] = {
{ 0x003400, 128, 0x04, 0x00000000 },
{ 0x0002e4, 1, 0x04, 0x0000b001 },
{}
};
-static struct nvc0_graph_init
-nvc1_grctx_init_unk58xx[] = {
+static const struct nvc0_graph_pack
+nvc1_grctx_pack_mthd[] = {
+ { nvc1_grctx_init_9097_0, 0x9097 },
+ { nvc1_grctx_init_9197_0, 0x9197 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ { nvc0_grctx_init_9039_0, 0x9039 },
+ { nvc0_grctx_init_90c0_0, 0x90c0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc1_grctx_init_ds_0[] = {
{ 0x405800, 1, 0x04, 0x0f8000bf },
{ 0x405830, 1, 0x04, 0x02180218 },
{ 0x405834, 2, 0x04, 0x00000000 },
@@ -590,8 +604,20 @@ nvc1_grctx_init_unk58xx[] = {
{}
};
-static struct nvc0_graph_init
-nvc1_grctx_init_rop[] = {
+static const struct nvc0_graph_init
+nvc1_grctx_init_pd_0[] = {
+ { 0x406020, 1, 0x04, 0x000103c1 },
+ { 0x406028, 4, 0x04, 0x00000001 },
+ { 0x4064a8, 1, 0x04, 0x00000000 },
+ { 0x4064ac, 1, 0x04, 0x00003fff },
+ { 0x4064b4, 2, 0x04, 0x00000000 },
+ { 0x4064c0, 1, 0x04, 0x80140078 },
+ { 0x4064c4, 1, 0x04, 0x0086ffff },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc1_grctx_init_be_0[] = {
{ 0x408800, 1, 0x04, 0x02802a3c },
{ 0x408804, 1, 0x04, 0x00000040 },
{ 0x408808, 1, 0x04, 0x1003e005 },
@@ -602,25 +628,22 @@ nvc1_grctx_init_rop[] = {
{}
};
-static struct nvc0_graph_init
-nvc1_grctx_init_gpc_0[] = {
- { 0x418380, 1, 0x04, 0x00000016 },
- { 0x418400, 1, 0x04, 0x38004e00 },
- { 0x418404, 1, 0x04, 0x71e0ffff },
- { 0x418408, 1, 0x04, 0x00000000 },
- { 0x41840c, 1, 0x04, 0x00001008 },
- { 0x418410, 1, 0x04, 0x0fff0fff },
- { 0x418414, 1, 0x04, 0x00200fff },
- { 0x418450, 6, 0x04, 0x00000000 },
- { 0x418468, 1, 0x04, 0x00000001 },
- { 0x41846c, 2, 0x04, 0x00000000 },
- { 0x418600, 1, 0x04, 0x0000001f },
- { 0x418684, 1, 0x04, 0x0000000f },
- { 0x418700, 1, 0x04, 0x00000002 },
- { 0x418704, 1, 0x04, 0x00000080 },
- { 0x418708, 1, 0x04, 0x00000000 },
- { 0x41870c, 1, 0x04, 0x07c80000 },
- { 0x418710, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nvc1_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nvc0_grctx_init_fe_0 },
+ { nvc0_grctx_init_pri_0 },
+ { nvc0_grctx_init_memfmt_0 },
+ { nvc1_grctx_init_ds_0 },
+ { nvc1_grctx_init_pd_0 },
+ { nvc0_grctx_init_rstr2d_0 },
+ { nvc0_grctx_init_scc_0 },
+ { nvc1_grctx_init_be_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc1_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x0006860a },
{ 0x418808, 3, 0x04, 0x00000000 },
{ 0x418828, 1, 0x04, 0x00008442 },
@@ -629,69 +652,44 @@ nvc1_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x00100018 },
- { 0x41891c, 1, 0x04, 0x00ff00ff },
- { 0x418924, 1, 0x04, 0x00000000 },
- { 0x418928, 1, 0x04, 0x00ffff00 },
- { 0x41892c, 1, 0x04, 0x0000ff00 },
- { 0x418a00, 3, 0x04, 0x00000000 },
- { 0x418a0c, 1, 0x04, 0x00010000 },
- { 0x418a10, 3, 0x04, 0x00000000 },
- { 0x418a20, 3, 0x04, 0x00000000 },
- { 0x418a2c, 1, 0x04, 0x00010000 },
- { 0x418a30, 3, 0x04, 0x00000000 },
- { 0x418a40, 3, 0x04, 0x00000000 },
- { 0x418a4c, 1, 0x04, 0x00010000 },
- { 0x418a50, 3, 0x04, 0x00000000 },
- { 0x418a60, 3, 0x04, 0x00000000 },
- { 0x418a6c, 1, 0x04, 0x00010000 },
- { 0x418a70, 3, 0x04, 0x00000000 },
- { 0x418a80, 3, 0x04, 0x00000000 },
- { 0x418a8c, 1, 0x04, 0x00010000 },
- { 0x418a90, 3, 0x04, 0x00000000 },
- { 0x418aa0, 3, 0x04, 0x00000000 },
- { 0x418aac, 1, 0x04, 0x00010000 },
- { 0x418ab0, 3, 0x04, 0x00000000 },
- { 0x418ac0, 3, 0x04, 0x00000000 },
- { 0x418acc, 1, 0x04, 0x00010000 },
- { 0x418ad0, 3, 0x04, 0x00000000 },
- { 0x418ae0, 3, 0x04, 0x00000000 },
- { 0x418aec, 1, 0x04, 0x00010000 },
- { 0x418af0, 3, 0x04, 0x00000000 },
- { 0x418b00, 1, 0x04, 0x00000000 },
- { 0x418b08, 1, 0x04, 0x0a418820 },
- { 0x418b0c, 1, 0x04, 0x062080e6 },
- { 0x418b10, 1, 0x04, 0x020398a4 },
- { 0x418b14, 1, 0x04, 0x0e629062 },
- { 0x418b18, 1, 0x04, 0x0a418820 },
- { 0x418b1c, 1, 0x04, 0x000000e6 },
- { 0x418bb8, 1, 0x04, 0x00000103 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc1_grctx_init_gpm_0[] = {
{ 0x418c08, 1, 0x04, 0x00000001 },
{ 0x418c10, 8, 0x04, 0x00000000 },
{ 0x418c6c, 1, 0x04, 0x00000001 },
{ 0x418c80, 1, 0x04, 0x20200004 },
{ 0x418c8c, 1, 0x04, 0x00000001 },
- { 0x419000, 1, 0x04, 0x00000780 },
- { 0x419004, 2, 0x04, 0x00000000 },
- { 0x419014, 1, 0x04, 0x00000004 },
{}
};
-static struct nvc0_graph_init
-nvc1_grctx_init_tpc[] = {
+static const struct nvc0_graph_pack
+nvc1_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvc0_grctx_init_prop_0 },
+ { nvc0_grctx_init_gpc_unk_1 },
+ { nvc1_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvc0_grctx_init_crstr_0 },
+ { nvc1_grctx_init_gpm_0 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc1_grctx_init_pe_0[] = {
{ 0x419818, 1, 0x04, 0x00000000 },
{ 0x41983c, 1, 0x04, 0x00038bc7 },
{ 0x419848, 1, 0x04, 0x00000000 },
{ 0x419864, 1, 0x04, 0x00000129 },
{ 0x419888, 1, 0x04, 0x00000000 },
- { 0x419a00, 1, 0x04, 0x000001f0 },
- { 0x419a04, 1, 0x04, 0x00000001 },
- { 0x419a08, 1, 0x04, 0x00000023 },
- { 0x419a0c, 1, 0x04, 0x00020000 },
- { 0x419a10, 1, 0x04, 0x00000000 },
- { 0x419a14, 1, 0x04, 0x00000200 },
- { 0x419a1c, 1, 0x04, 0x00000000 },
- { 0x419a20, 1, 0x04, 0x00000800 },
- { 0x419ac4, 1, 0x04, 0x0007f440 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc1_grctx_init_wwdx_0[] = {
{ 0x419b00, 1, 0x04, 0x0a418820 },
{ 0x419b04, 1, 0x04, 0x062080e6 },
{ 0x419b08, 1, 0x04, 0x020398a4 },
@@ -701,28 +699,33 @@ nvc1_grctx_init_tpc[] = {
{ 0x419bd0, 1, 0x04, 0x00900103 },
{ 0x419be0, 1, 0x04, 0x00400001 },
{ 0x419be4, 1, 0x04, 0x00000000 },
- { 0x419c00, 1, 0x04, 0x00000002 },
- { 0x419c04, 1, 0x04, 0x00000006 },
- { 0x419c08, 1, 0x04, 0x00000002 },
- { 0x419c20, 1, 0x04, 0x00000000 },
- { 0x419cb0, 1, 0x04, 0x00020048 },
- { 0x419ce8, 1, 0x04, 0x00000000 },
- { 0x419cf4, 1, 0x04, 0x00000183 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc1_grctx_init_tpccs_0[] = {
{ 0x419d20, 1, 0x04, 0x12180000 },
{ 0x419d24, 1, 0x04, 0x00001fff },
{ 0x419d44, 1, 0x04, 0x02180218 },
- { 0x419e04, 3, 0x04, 0x00000000 },
- { 0x419e10, 1, 0x04, 0x00000002 },
- { 0x419e44, 1, 0x04, 0x001beff2 },
- { 0x419e48, 1, 0x04, 0x00000000 },
- { 0x419e4c, 1, 0x04, 0x0000000f },
- { 0x419e50, 17, 0x04, 0x00000000 },
- { 0x419e98, 1, 0x04, 0x00000000 },
- { 0x419ee0, 1, 0x04, 0x00011110 },
- { 0x419f30, 11, 0x04, 0x00000000 },
{}
};
+static const struct nvc0_graph_pack
+nvc1_grctx_pack_tpc[] = {
+ { nvc1_grctx_init_pe_0 },
+ { nvc4_grctx_init_tex_0 },
+ { nvc1_grctx_init_wwdx_0 },
+ { nvc0_grctx_init_mpc_0 },
+ { nvc4_grctx_init_l1c_0 },
+ { nvc1_grctx_init_tpccs_0 },
+ { nvc4_grctx_init_sm_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
void
nvc1_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
@@ -771,41 +774,6 @@ nvc1_grctx_generate_unkn(struct nvc0_graph_priv *priv)
nv_mask(priv, 0x419c00, 0x00000008, 0x00000008);
}
-static struct nvc0_graph_init *
-nvc1_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nvc0_grctx_init_unk40xx,
- nvc0_grctx_init_unk44xx,
- nvc0_grctx_init_unk46xx,
- nvc0_grctx_init_unk47xx,
- nvc1_grctx_init_unk58xx,
- nvc0_grctx_init_unk60xx,
- nvc0_grctx_init_unk64xx,
- nvc0_grctx_init_unk78xx,
- nvc0_grctx_init_unk80xx,
- nvc1_grctx_init_rop,
- NULL
-};
-
-struct nvc0_graph_init *
-nvc1_grctx_init_gpc[] = {
- nvc1_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvc1_grctx_init_tpc,
- NULL
-};
-
-static struct nvc0_graph_mthd
-nvc1_grctx_init_mthd[] = {
- { 0x9097, nvc1_grctx_init_9097, },
- { 0x9197, nvc1_grctx_init_9197, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x9039, nvc0_grctx_init_9039, },
- { 0x90c0, nvc0_grctx_init_90c0, },
- { 0x902d, nvc0_grctx_init_mthd_magic, },
- {}
-};
-
struct nouveau_oclass *
nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xc1),
@@ -817,11 +785,13 @@ nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nvc0_grctx_generate_main,
- .mods = nvc1_grctx_generate_mods,
- .unkn = nvc1_grctx_generate_unkn,
- .hub = nvc1_grctx_init_hub,
- .gpc = nvc1_grctx_init_gpc,
- .icmd = nvc1_grctx_init_icmd,
- .mthd = nvc1_grctx_init_mthd,
+ .main = nvc0_grctx_generate_main,
+ .mods = nvc1_grctx_generate_mods,
+ .unkn = nvc1_grctx_generate_unkn,
+ .hub = nvc1_grctx_pack_hub,
+ .gpc = nvc1_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvc1_grctx_pack_tpc,
+ .icmd = nvc1_grctx_pack_icmd,
+ .mthd = nvc1_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c
index 8f237b3bd8c6..e11ed5538193 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c
@@ -22,15 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-static struct nvc0_graph_init
-nvc3_grctx_init_tpc[] = {
- { 0x419818, 1, 0x04, 0x00000000 },
- { 0x41983c, 1, 0x04, 0x00038bc7 },
- { 0x419848, 1, 0x04, 0x00000000 },
- { 0x419864, 1, 0x04, 0x0000012a },
- { 0x419888, 1, 0x04, 0x00000000 },
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+const struct nvc0_graph_init
+nvc4_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000001f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000023 },
@@ -40,24 +39,19 @@ nvc3_grctx_init_tpc[] = {
{ 0x419a1c, 1, 0x04, 0x00000000 },
{ 0x419a20, 1, 0x04, 0x00000800 },
{ 0x419ac4, 1, 0x04, 0x0007f440 },
- { 0x419b00, 1, 0x04, 0x0a418820 },
- { 0x419b04, 1, 0x04, 0x062080e6 },
- { 0x419b08, 1, 0x04, 0x020398a4 },
- { 0x419b0c, 1, 0x04, 0x0e629062 },
- { 0x419b10, 1, 0x04, 0x0a418820 },
- { 0x419b14, 1, 0x04, 0x000000e6 },
- { 0x419bd0, 1, 0x04, 0x00900103 },
- { 0x419be0, 1, 0x04, 0x00000001 },
- { 0x419be4, 1, 0x04, 0x00000000 },
- { 0x419c00, 1, 0x04, 0x00000002 },
- { 0x419c04, 1, 0x04, 0x00000006 },
- { 0x419c08, 1, 0x04, 0x00000002 },
- { 0x419c20, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc4_grctx_init_l1c_0[] = {
{ 0x419cb0, 1, 0x04, 0x00020048 },
{ 0x419ce8, 1, 0x04, 0x00000000 },
{ 0x419cf4, 1, 0x04, 0x00000183 },
- { 0x419d20, 1, 0x04, 0x02180000 },
- { 0x419d24, 1, 0x04, 0x00001fff },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc4_grctx_init_sm_0[] = {
{ 0x419e04, 3, 0x04, 0x00000000 },
{ 0x419e10, 1, 0x04, 0x00000002 },
{ 0x419e44, 1, 0x04, 0x001beff2 },
@@ -70,16 +64,24 @@ nvc3_grctx_init_tpc[] = {
{}
};
-struct nvc0_graph_init *
-nvc3_grctx_init_gpc[] = {
- nvc0_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvc3_grctx_init_tpc,
- NULL
+static const struct nvc0_graph_pack
+nvc4_grctx_pack_tpc[] = {
+ { nvc0_grctx_init_pe_0 },
+ { nvc4_grctx_init_tex_0 },
+ { nvc0_grctx_init_wwdx_0 },
+ { nvc0_grctx_init_mpc_0 },
+ { nvc4_grctx_init_l1c_0 },
+ { nvc0_grctx_init_tpccs_0 },
+ { nvc4_grctx_init_sm_0 },
+ {}
};
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
struct nouveau_oclass *
-nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) {
+nvc4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xc3),
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvc0_graph_context_ctor,
@@ -89,11 +91,13 @@ nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nvc0_grctx_generate_main,
- .mods = nvc0_grctx_generate_mods,
- .unkn = nvc0_grctx_generate_unkn,
- .hub = nvc0_grctx_init_hub,
- .gpc = nvc3_grctx_init_gpc,
- .icmd = nvc0_grctx_init_icmd,
- .mthd = nvc0_grctx_init_mthd,
+ .main = nvc0_grctx_generate_main,
+ .mods = nvc0_grctx_generate_mods,
+ .unkn = nvc0_grctx_generate_unkn,
+ .hub = nvc0_grctx_pack_hub,
+ .gpc = nvc0_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvc4_grctx_pack_tpc,
+ .icmd = nvc0_grctx_pack_icmd,
+ .mthd = nvc0_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
index d0d4ce3c4892..feebd58dfe8d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
@@ -22,10 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-static struct nvc0_graph_init
-nvc8_grctx_init_icmd[] = {
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nvc8_grctx_init_icmd_0[] = {
{ 0x001000, 1, 0x01, 0x00000004 },
{ 0x0000a9, 1, 0x01, 0x0000ffff },
{ 0x000038, 1, 0x01, 0x0fac6881 },
@@ -141,8 +145,7 @@ nvc8_grctx_init_icmd[] = {
{ 0x000586, 1, 0x01, 0x00000040 },
{ 0x000582, 2, 0x01, 0x00000080 },
{ 0x0005c2, 1, 0x01, 0x00000001 },
- { 0x000638, 1, 0x01, 0x00000001 },
- { 0x000639, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
{ 0x00063a, 1, 0x01, 0x00000002 },
{ 0x00063b, 2, 0x01, 0x00000001 },
{ 0x00063d, 1, 0x01, 0x00000002 },
@@ -203,15 +206,13 @@ nvc8_grctx_init_icmd[] = {
{ 0x000787, 1, 0x01, 0x000000cf },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x000836, 1, 0x01, 0x00000001 },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x00080c, 1, 0x01, 0x00000002 },
{ 0x00080d, 2, 0x01, 0x00000100 },
@@ -237,14 +238,12 @@ nvc8_grctx_init_icmd[] = {
{ 0x0006b1, 1, 0x01, 0x00000011 },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x01e100, 1, 0x01, 0x00000001 },
{ 0x001000, 1, 0x01, 0x00000014 },
@@ -269,58 +268,20 @@ nvc8_grctx_init_icmd[] = {
{}
};
-static struct nvc0_graph_init
-nvc8_grctx_init_tpc[] = {
- { 0x419818, 1, 0x04, 0x00000000 },
- { 0x41983c, 1, 0x04, 0x00038bc7 },
- { 0x419848, 1, 0x04, 0x00000000 },
- { 0x419864, 1, 0x04, 0x0000012a },
- { 0x419888, 1, 0x04, 0x00000000 },
- { 0x419a00, 1, 0x04, 0x000001f0 },
- { 0x419a04, 1, 0x04, 0x00000001 },
- { 0x419a08, 1, 0x04, 0x00000023 },
- { 0x419a0c, 1, 0x04, 0x00020000 },
- { 0x419a10, 1, 0x04, 0x00000000 },
- { 0x419a14, 1, 0x04, 0x00000200 },
- { 0x419a1c, 1, 0x04, 0x00000000 },
- { 0x419a20, 1, 0x04, 0x00000800 },
- { 0x419b00, 1, 0x04, 0x0a418820 },
- { 0x419b04, 1, 0x04, 0x062080e6 },
- { 0x419b08, 1, 0x04, 0x020398a4 },
- { 0x419b0c, 1, 0x04, 0x0e629062 },
- { 0x419b10, 1, 0x04, 0x0a418820 },
- { 0x419b14, 1, 0x04, 0x000000e6 },
- { 0x419bd0, 1, 0x04, 0x00900103 },
- { 0x419be0, 1, 0x04, 0x00000001 },
- { 0x419be4, 1, 0x04, 0x00000000 },
- { 0x419c00, 1, 0x04, 0x00000002 },
- { 0x419c04, 1, 0x04, 0x00000006 },
- { 0x419c08, 1, 0x04, 0x00000002 },
- { 0x419c20, 1, 0x04, 0x00000000 },
- { 0x419cb0, 1, 0x04, 0x00060048 },
- { 0x419ce8, 1, 0x04, 0x00000000 },
- { 0x419cf4, 1, 0x04, 0x00000183 },
- { 0x419d20, 1, 0x04, 0x02180000 },
- { 0x419d24, 1, 0x04, 0x00001fff },
- { 0x419e04, 3, 0x04, 0x00000000 },
- { 0x419e10, 1, 0x04, 0x00000002 },
- { 0x419e44, 1, 0x04, 0x001beff2 },
- { 0x419e48, 1, 0x04, 0x00000000 },
- { 0x419e4c, 1, 0x04, 0x0000000f },
- { 0x419e50, 17, 0x04, 0x00000000 },
- { 0x419e98, 1, 0x04, 0x00000000 },
- { 0x419f50, 2, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nvc8_grctx_pack_icmd[] = {
+ { nvc8_grctx_init_icmd_0 },
{}
};
-struct nvc0_graph_init
-nvc8_grctx_init_9197[] = {
+const struct nvc0_graph_init
+nvc8_grctx_init_9197_0[] = {
{ 0x0002e4, 1, 0x04, 0x0000b001 },
{}
};
-struct nvc0_graph_init
-nvc8_grctx_init_9297[] = {
+const struct nvc0_graph_init
+nvc8_grctx_init_9297_0[] = {
{ 0x003400, 128, 0x04, 0x00000000 },
{ 0x00036c, 2, 0x04, 0x00000000 },
{ 0x0007a4, 2, 0x04, 0x00000000 },
@@ -329,26 +290,47 @@ nvc8_grctx_init_9297[] = {
{}
};
-static struct nvc0_graph_mthd
-nvc8_grctx_init_mthd[] = {
- { 0x9097, nvc1_grctx_init_9097, },
- { 0x9197, nvc8_grctx_init_9197, },
- { 0x9297, nvc8_grctx_init_9297, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x9039, nvc0_grctx_init_9039, },
- { 0x90c0, nvc0_grctx_init_90c0, },
- { 0x902d, nvc0_grctx_init_mthd_magic, },
+static const struct nvc0_graph_pack
+nvc8_grctx_pack_mthd[] = {
+ { nvc1_grctx_init_9097_0, 0x9097 },
+ { nvc8_grctx_init_9197_0, 0x9197 },
+ { nvc8_grctx_init_9297_0, 0x9297 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ { nvc0_grctx_init_9039_0, 0x9039 },
+ { nvc0_grctx_init_90c0_0, 0x90c0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc8_grctx_init_setup_0[] = {
+ { 0x418800, 1, 0x04, 0x0006860a },
+ { 0x418808, 3, 0x04, 0x00000000 },
+ { 0x418828, 1, 0x04, 0x00008442 },
+ { 0x418830, 1, 0x04, 0x00000001 },
+ { 0x4188d8, 1, 0x04, 0x00000008 },
+ { 0x4188e0, 1, 0x04, 0x01000000 },
+ { 0x4188e8, 5, 0x04, 0x00000000 },
+ { 0x4188fc, 1, 0x04, 0x20100000 },
{}
};
-static struct nvc0_graph_init *
-nvc8_grctx_init_gpc[] = {
- nvc0_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvc8_grctx_init_tpc,
- NULL
+static const struct nvc0_graph_pack
+nvc8_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvc0_grctx_init_prop_0 },
+ { nvc0_grctx_init_gpc_unk_1 },
+ { nvc8_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvc0_grctx_init_crstr_0 },
+ { nvc0_grctx_init_gpm_0 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
};
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
struct nouveau_oclass *
nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xc8),
@@ -360,11 +342,13 @@ nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nvc0_grctx_generate_main,
- .mods = nvc0_grctx_generate_mods,
- .unkn = nvc0_grctx_generate_unkn,
- .hub = nvc0_grctx_init_hub,
- .gpc = nvc8_grctx_init_gpc,
- .icmd = nvc8_grctx_init_icmd,
- .mthd = nvc8_grctx_init_mthd,
+ .main = nvc0_grctx_generate_main,
+ .mods = nvc0_grctx_generate_mods,
+ .unkn = nvc0_grctx_generate_unkn,
+ .hub = nvc0_grctx_pack_hub,
+ .gpc = nvc8_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvc0_grctx_pack_tpc,
+ .icmd = nvc8_grctx_pack_icmd,
+ .mthd = nvc8_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
index c4740d528532..1dbc8d7f2e86 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
@@ -22,33 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-struct nvc0_graph_init
-nvd7_grctx_init_unk40xx[] = {
- { 0x404004, 10, 0x04, 0x00000000 },
- { 0x404044, 1, 0x04, 0x00000000 },
- { 0x404094, 1, 0x04, 0x00000000 },
- { 0x404098, 12, 0x04, 0x00000000 },
- { 0x4040c8, 1, 0x04, 0xf0000087 },
- { 0x4040d0, 6, 0x04, 0x00000000 },
- { 0x4040e8, 1, 0x04, 0x00001000 },
- { 0x4040f8, 1, 0x04, 0x00000000 },
- { 0x404130, 1, 0x04, 0x00000000 },
- { 0x404134, 1, 0x04, 0x00000000 },
- { 0x404138, 1, 0x04, 0x20000040 },
- { 0x404150, 1, 0x04, 0x0000002e },
- { 0x404154, 1, 0x04, 0x00000400 },
- { 0x404158, 1, 0x04, 0x00000200 },
- { 0x404164, 1, 0x04, 0x00000055 },
- { 0x404168, 1, 0x04, 0x00000000 },
- { 0x404178, 2, 0x04, 0x00000000 },
- { 0x404200, 8, 0x04, 0x00000000 },
- {}
-};
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
-static struct nvc0_graph_init
-nvd7_grctx_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nvd7_grctx_init_ds_0[] = {
{ 0x405800, 1, 0x04, 0x0f8000bf },
{ 0x405830, 1, 0x04, 0x02180324 },
{ 0x405834, 1, 0x04, 0x08000000 },
@@ -60,8 +41,10 @@ nvd7_grctx_init_unk58xx[] = {
{}
};
-static struct nvc0_graph_init
-nvd7_grctx_init_unk64xx[] = {
+static const struct nvc0_graph_init
+nvd7_grctx_init_pd_0[] = {
+ { 0x406020, 1, 0x04, 0x000103c1 },
+ { 0x406028, 4, 0x04, 0x00000001 },
{ 0x4064a8, 1, 0x04, 0x00000000 },
{ 0x4064ac, 1, 0x04, 0x00003fff },
{ 0x4064b4, 3, 0x04, 0x00000000 },
@@ -71,22 +54,22 @@ nvd7_grctx_init_unk64xx[] = {
{}
};
-static struct nvc0_graph_init
-nvd7_grctx_init_gpc_0[] = {
- { 0x418380, 1, 0x04, 0x00000016 },
- { 0x418400, 1, 0x04, 0x38004e00 },
- { 0x418404, 1, 0x04, 0x71e0ffff },
- { 0x41840c, 1, 0x04, 0x00001008 },
- { 0x418410, 1, 0x04, 0x0fff0fff },
- { 0x418414, 1, 0x04, 0x02200fff },
- { 0x418450, 6, 0x04, 0x00000000 },
- { 0x418468, 1, 0x04, 0x00000001 },
- { 0x41846c, 2, 0x04, 0x00000000 },
- { 0x418600, 1, 0x04, 0x0000001f },
- { 0x418684, 1, 0x04, 0x0000000f },
- { 0x418700, 1, 0x04, 0x00000002 },
- { 0x418704, 1, 0x04, 0x00000080 },
- { 0x418708, 3, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nvd7_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nvd9_grctx_init_fe_0 },
+ { nvc0_grctx_init_pri_0 },
+ { nvc0_grctx_init_memfmt_0 },
+ { nvd7_grctx_init_ds_0 },
+ { nvd7_grctx_init_pd_0 },
+ { nvc0_grctx_init_rstr2d_0 },
+ { nvc0_grctx_init_scc_0 },
+ { nvd9_grctx_init_be_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd7_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x7006860a },
{ 0x418808, 3, 0x04, 0x00000000 },
{ 0x418828, 1, 0x04, 0x00008442 },
@@ -95,34 +78,32 @@ nvd7_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x20100018 },
- { 0x41891c, 1, 0x04, 0x00ff00ff },
- { 0x418924, 1, 0x04, 0x00000000 },
- { 0x418928, 1, 0x04, 0x00ffff00 },
- { 0x41892c, 1, 0x04, 0x0000ff00 },
- { 0x418b00, 1, 0x04, 0x00000006 },
- { 0x418b08, 1, 0x04, 0x0a418820 },
- { 0x418b0c, 1, 0x04, 0x062080e6 },
- { 0x418b10, 1, 0x04, 0x020398a4 },
- { 0x418b14, 1, 0x04, 0x0e629062 },
- { 0x418b18, 1, 0x04, 0x0a418820 },
- { 0x418b1c, 1, 0x04, 0x000000e6 },
- { 0x418bb8, 1, 0x04, 0x00000103 },
- { 0x418c08, 1, 0x04, 0x00000001 },
- { 0x418c10, 8, 0x04, 0x00000000 },
- { 0x418c6c, 1, 0x04, 0x00000001 },
- { 0x418c80, 1, 0x04, 0x20200004 },
- { 0x418c8c, 1, 0x04, 0x00000001 },
- { 0x419000, 1, 0x04, 0x00000780 },
- { 0x419004, 2, 0x04, 0x00000000 },
- { 0x419014, 1, 0x04, 0x00000004 },
{}
};
-static struct nvc0_graph_init
-nvd7_grctx_init_tpc[] = {
+static const struct nvc0_graph_pack
+nvd7_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvd9_grctx_init_prop_0 },
+ { nvd9_grctx_init_gpc_unk_1 },
+ { nvd7_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvd9_grctx_init_crstr_0 },
+ { nvc1_grctx_init_gpm_0 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd7_grctx_init_pe_0[] = {
{ 0x419848, 1, 0x04, 0x00000000 },
{ 0x419864, 1, 0x04, 0x00000129 },
{ 0x419888, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd7_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000001f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000023 },
@@ -132,33 +113,46 @@ nvd7_grctx_init_tpc[] = {
{ 0x419a1c, 1, 0x04, 0x00008000 },
{ 0x419a20, 1, 0x04, 0x00000800 },
{ 0x419ac4, 1, 0x04, 0x0017f440 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd7_grctx_init_mpc_0[] = {
{ 0x419c00, 1, 0x04, 0x0000000a },
{ 0x419c04, 1, 0x04, 0x00000006 },
{ 0x419c08, 1, 0x04, 0x00000002 },
{ 0x419c20, 1, 0x04, 0x00000000 },
{ 0x419c24, 1, 0x04, 0x00084210 },
{ 0x419c28, 1, 0x04, 0x3efbefbe },
- { 0x419cb0, 1, 0x04, 0x00020048 },
- { 0x419ce8, 1, 0x04, 0x00000000 },
- { 0x419cf4, 1, 0x04, 0x00000183 },
- { 0x419e04, 3, 0x04, 0x00000000 },
- { 0x419e10, 1, 0x04, 0x00000002 },
- { 0x419e44, 1, 0x04, 0x001beff2 },
- { 0x419e48, 1, 0x04, 0x00000000 },
- { 0x419e4c, 1, 0x04, 0x0000000f },
- { 0x419e50, 17, 0x04, 0x00000000 },
- { 0x419e98, 1, 0x04, 0x00000000 },
- { 0x419ee0, 1, 0x04, 0x00010110 },
- { 0x419f30, 11, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvd7_grctx_init_unk[] = {
+static const struct nvc0_graph_pack
+nvd7_grctx_pack_tpc[] = {
+ { nvd7_grctx_init_pe_0 },
+ { nvd7_grctx_init_tex_0 },
+ { nvd7_grctx_init_mpc_0 },
+ { nvc4_grctx_init_l1c_0 },
+ { nvd9_grctx_init_sm_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd7_grctx_init_pes_0[] = {
{ 0x41be24, 1, 0x04, 0x00000002 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd7_grctx_init_cbm_0[] = {
{ 0x41bec0, 1, 0x04, 0x12180000 },
{ 0x41bec4, 1, 0x04, 0x00003fff },
{ 0x41bee4, 1, 0x04, 0x03240218 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd7_grctx_init_wwdx_0[] = {
{ 0x41bf00, 1, 0x04, 0x0a418820 },
{ 0x41bf04, 1, 0x04, 0x062080e6 },
{ 0x41bf08, 1, 0x04, 0x020398a4 },
@@ -171,6 +165,18 @@ nvd7_grctx_init_unk[] = {
{}
};
+static const struct nvc0_graph_pack
+nvd7_grctx_pack_ppc[] = {
+ { nvd7_grctx_init_pes_0 },
+ { nvd7_grctx_init_cbm_0 },
+ { nvd7_grctx_init_wwdx_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
static void
nvd7_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
@@ -219,10 +225,11 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
- for (i = 0; oclass->hub[i]; i++)
- nvc0_graph_mmio(priv, oclass->hub[i]);
- for (i = 0; oclass->gpc[i]; i++)
- nvc0_graph_mmio(priv, oclass->gpc[i]);
+ nvc0_graph_mmio(priv, oclass->hub);
+ nvc0_graph_mmio(priv, oclass->gpc);
+ nvc0_graph_mmio(priv, oclass->zcull);
+ nvc0_graph_mmio(priv, oclass->tpc);
+ nvc0_graph_mmio(priv, oclass->ppc);
nv_wr32(priv, 0x404154, 0x00000000);
@@ -244,32 +251,6 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
}
-
-static struct nvc0_graph_init *
-nvd7_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nvd7_grctx_init_unk40xx,
- nvc0_grctx_init_unk44xx,
- nvc0_grctx_init_unk46xx,
- nvc0_grctx_init_unk47xx,
- nvd7_grctx_init_unk58xx,
- nvc0_grctx_init_unk60xx,
- nvd7_grctx_init_unk64xx,
- nvc0_grctx_init_unk78xx,
- nvc0_grctx_init_unk80xx,
- nvd9_grctx_init_rop,
- NULL
-};
-
-struct nvc0_graph_init *
-nvd7_grctx_init_gpc[] = {
- nvd7_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvd7_grctx_init_tpc,
- nvd7_grctx_init_unk,
- NULL
-};
-
struct nouveau_oclass *
nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xd7),
@@ -281,11 +262,14 @@ nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nvd7_grctx_generate_main,
- .mods = nvd7_grctx_generate_mods,
- .unkn = nve4_grctx_generate_unkn,
- .hub = nvd7_grctx_init_hub,
- .gpc = nvd7_grctx_init_gpc,
- .icmd = nvd9_grctx_init_icmd,
- .mthd = nvd9_grctx_init_mthd,
+ .main = nvd7_grctx_generate_main,
+ .mods = nvd7_grctx_generate_mods,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = nvd7_grctx_pack_hub,
+ .gpc = nvd7_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvd7_grctx_pack_tpc,
+ .ppc = nvd7_grctx_pack_ppc,
+ .icmd = nvd9_grctx_pack_icmd,
+ .mthd = nvd9_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
index a1102cbf2fdc..c665fb7e4660 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
@@ -22,38 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-struct nvc0_graph_init
-nvd9_grctx_init_90c0[] = {
- { 0x002700, 4, 0x40, 0x00000000 },
- { 0x002720, 4, 0x40, 0x00000000 },
- { 0x002704, 4, 0x40, 0x00000000 },
- { 0x002724, 4, 0x40, 0x00000000 },
- { 0x002708, 4, 0x40, 0x00000000 },
- { 0x002728, 4, 0x40, 0x00000000 },
- { 0x00270c, 8, 0x20, 0x00000000 },
- { 0x002710, 4, 0x40, 0x00014000 },
- { 0x002730, 4, 0x40, 0x00014000 },
- { 0x002714, 4, 0x40, 0x00000040 },
- { 0x002734, 4, 0x40, 0x00000040 },
- { 0x00030c, 1, 0x04, 0x00000001 },
- { 0x001944, 1, 0x04, 0x00000000 },
- { 0x000758, 1, 0x04, 0x00000100 },
- { 0x0002c4, 1, 0x04, 0x00000000 },
- { 0x000790, 5, 0x04, 0x00000000 },
- { 0x00077c, 1, 0x04, 0x00000000 },
- { 0x000204, 3, 0x04, 0x00000000 },
- { 0x000214, 1, 0x04, 0x00000000 },
- { 0x00024c, 1, 0x04, 0x00000000 },
- { 0x000d94, 1, 0x04, 0x00000001 },
- { 0x001608, 2, 0x04, 0x00000000 },
- { 0x001664, 1, 0x04, 0x00000000 },
- {}
-};
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
-struct nvc0_graph_init
-nvd9_grctx_init_icmd[] = {
+static const struct nvc0_graph_init
+nvd9_grctx_init_icmd_0[] = {
{ 0x001000, 1, 0x01, 0x00000004 },
{ 0x0000a9, 1, 0x01, 0x0000ffff },
{ 0x000038, 1, 0x01, 0x0fac6881 },
@@ -171,8 +147,7 @@ nvd9_grctx_init_icmd[] = {
{ 0x000586, 1, 0x01, 0x00000040 },
{ 0x000582, 2, 0x01, 0x00000080 },
{ 0x0005c2, 1, 0x01, 0x00000001 },
- { 0x000638, 1, 0x01, 0x00000001 },
- { 0x000639, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
{ 0x00063a, 1, 0x01, 0x00000002 },
{ 0x00063b, 2, 0x01, 0x00000001 },
{ 0x00063d, 1, 0x01, 0x00000002 },
@@ -233,15 +208,13 @@ nvd9_grctx_init_icmd[] = {
{ 0x000787, 1, 0x01, 0x000000cf },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x000836, 1, 0x01, 0x00000001 },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x00080c, 1, 0x01, 0x00000002 },
{ 0x00080d, 2, 0x01, 0x00000100 },
@@ -267,14 +240,12 @@ nvd9_grctx_init_icmd[] = {
{ 0x0006b1, 1, 0x01, 0x00000011 },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x01e100, 1, 0x01, 0x00000001 },
{ 0x001000, 1, 0x01, 0x00000014 },
@@ -299,18 +270,56 @@ nvd9_grctx_init_icmd[] = {
{}
};
-struct nvc0_graph_init
-nvd9_grctx_init_unk40xx[] = {
- { 0x404004, 11, 0x04, 0x00000000 },
+const struct nvc0_graph_pack
+nvd9_grctx_pack_icmd[] = {
+ { nvd9_grctx_init_icmd_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_grctx_init_90c0_0[] = {
+ { 0x002700, 8, 0x20, 0x00000000 },
+ { 0x002704, 8, 0x20, 0x00000000 },
+ { 0x002708, 8, 0x20, 0x00000000 },
+ { 0x00270c, 8, 0x20, 0x00000000 },
+ { 0x002710, 8, 0x20, 0x00014000 },
+ { 0x002714, 8, 0x20, 0x00000040 },
+ { 0x00030c, 1, 0x04, 0x00000001 },
+ { 0x001944, 1, 0x04, 0x00000000 },
+ { 0x000758, 1, 0x04, 0x00000100 },
+ { 0x0002c4, 1, 0x04, 0x00000000 },
+ { 0x000790, 5, 0x04, 0x00000000 },
+ { 0x00077c, 1, 0x04, 0x00000000 },
+ { 0x000204, 3, 0x04, 0x00000000 },
+ { 0x000214, 1, 0x04, 0x00000000 },
+ { 0x00024c, 1, 0x04, 0x00000000 },
+ { 0x000d94, 1, 0x04, 0x00000001 },
+ { 0x001608, 2, 0x04, 0x00000000 },
+ { 0x001664, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_pack
+nvd9_grctx_pack_mthd[] = {
+ { nvc1_grctx_init_9097_0, 0x9097 },
+ { nvc8_grctx_init_9197_0, 0x9197 },
+ { nvc8_grctx_init_9297_0, 0x9297 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ { nvc0_grctx_init_9039_0, 0x9039 },
+ { nvd9_grctx_init_90c0_0, 0x90c0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_grctx_init_fe_0[] = {
+ { 0x404004, 10, 0x04, 0x00000000 },
{ 0x404044, 1, 0x04, 0x00000000 },
- { 0x404094, 1, 0x04, 0x00000000 },
- { 0x404098, 12, 0x04, 0x00000000 },
+ { 0x404094, 13, 0x04, 0x00000000 },
{ 0x4040c8, 1, 0x04, 0xf0000087 },
{ 0x4040d0, 6, 0x04, 0x00000000 },
{ 0x4040e8, 1, 0x04, 0x00001000 },
{ 0x4040f8, 1, 0x04, 0x00000000 },
- { 0x404130, 1, 0x04, 0x00000000 },
- { 0x404134, 1, 0x04, 0x00000000 },
+ { 0x404130, 2, 0x04, 0x00000000 },
{ 0x404138, 1, 0x04, 0x20000040 },
{ 0x404150, 1, 0x04, 0x0000002e },
{ 0x404154, 1, 0x04, 0x00000400 },
@@ -322,8 +331,8 @@ nvd9_grctx_init_unk40xx[] = {
{}
};
-static struct nvc0_graph_init
-nvd9_grctx_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nvd9_grctx_init_ds_0[] = {
{ 0x405800, 1, 0x04, 0x0f8000bf },
{ 0x405830, 1, 0x04, 0x02180218 },
{ 0x405834, 1, 0x04, 0x08000000 },
@@ -335,8 +344,10 @@ nvd9_grctx_init_unk58xx[] = {
{}
};
-static struct nvc0_graph_init
-nvd9_grctx_init_unk64xx[] = {
+static const struct nvc0_graph_init
+nvd9_grctx_init_pd_0[] = {
+ { 0x406020, 1, 0x04, 0x000103c1 },
+ { 0x406028, 4, 0x04, 0x00000001 },
{ 0x4064a8, 1, 0x04, 0x00000000 },
{ 0x4064ac, 1, 0x04, 0x00003fff },
{ 0x4064b4, 3, 0x04, 0x00000000 },
@@ -345,21 +356,34 @@ nvd9_grctx_init_unk64xx[] = {
{}
};
-struct nvc0_graph_init
-nvd9_grctx_init_rop[] = {
+const struct nvc0_graph_init
+nvd9_grctx_init_be_0[] = {
{ 0x408800, 1, 0x04, 0x02802a3c },
{ 0x408804, 1, 0x04, 0x00000040 },
{ 0x408808, 1, 0x04, 0x1043e005 },
{ 0x408900, 1, 0x04, 0x3080b801 },
- { 0x408904, 1, 0x04, 0x1043e005 },
+ { 0x408904, 1, 0x04, 0x62000001 },
{ 0x408908, 1, 0x04, 0x00c8102f },
{ 0x408980, 1, 0x04, 0x0000011d },
{}
};
-static struct nvc0_graph_init
-nvd9_grctx_init_gpc_0[] = {
- { 0x418380, 1, 0x04, 0x00000016 },
+static const struct nvc0_graph_pack
+nvd9_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nvd9_grctx_init_fe_0 },
+ { nvc0_grctx_init_pri_0 },
+ { nvc0_grctx_init_memfmt_0 },
+ { nvd9_grctx_init_ds_0 },
+ { nvd9_grctx_init_pd_0 },
+ { nvc0_grctx_init_rstr2d_0 },
+ { nvc0_grctx_init_scc_0 },
+ { nvd9_grctx_init_be_0 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_grctx_init_prop_0[] = {
{ 0x418400, 1, 0x04, 0x38004e00 },
{ 0x418404, 1, 0x04, 0x71e0ffff },
{ 0x41840c, 1, 0x04, 0x00001008 },
@@ -368,11 +392,21 @@ nvd9_grctx_init_gpc_0[] = {
{ 0x418450, 6, 0x04, 0x00000000 },
{ 0x418468, 1, 0x04, 0x00000001 },
{ 0x41846c, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_grctx_init_gpc_unk_1[] = {
{ 0x418600, 1, 0x04, 0x0000001f },
{ 0x418684, 1, 0x04, 0x0000000f },
{ 0x418700, 1, 0x04, 0x00000002 },
{ 0x418704, 1, 0x04, 0x00000080 },
{ 0x418708, 3, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x7006860a },
{ 0x418808, 3, 0x04, 0x00000000 },
{ 0x418828, 1, 0x04, 0x00008442 },
@@ -381,10 +415,11 @@ nvd9_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x20100008 },
- { 0x41891c, 1, 0x04, 0x00ff00ff },
- { 0x418924, 1, 0x04, 0x00000000 },
- { 0x418928, 1, 0x04, 0x00ffff00 },
- { 0x41892c, 1, 0x04, 0x0000ff00 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_grctx_init_crstr_0[] = {
{ 0x418b00, 1, 0x04, 0x00000006 },
{ 0x418b08, 1, 0x04, 0x0a418820 },
{ 0x418b0c, 1, 0x04, 0x062080e6 },
@@ -393,24 +428,24 @@ nvd9_grctx_init_gpc_0[] = {
{ 0x418b18, 1, 0x04, 0x0a418820 },
{ 0x418b1c, 1, 0x04, 0x000000e6 },
{ 0x418bb8, 1, 0x04, 0x00000103 },
- { 0x418c08, 1, 0x04, 0x00000001 },
- { 0x418c10, 8, 0x04, 0x00000000 },
- { 0x418c6c, 1, 0x04, 0x00000001 },
- { 0x418c80, 1, 0x04, 0x20200004 },
- { 0x418c8c, 1, 0x04, 0x00000001 },
- { 0x419000, 1, 0x04, 0x00000780 },
- { 0x419004, 2, 0x04, 0x00000000 },
- { 0x419014, 1, 0x04, 0x00000004 },
{}
};
-static struct nvc0_graph_init
-nvd9_grctx_init_tpc[] = {
- { 0x419818, 1, 0x04, 0x00000000 },
- { 0x41983c, 1, 0x04, 0x00038bc7 },
- { 0x419848, 1, 0x04, 0x00000000 },
- { 0x419864, 1, 0x04, 0x00000129 },
- { 0x419888, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nvd9_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvd9_grctx_init_prop_0 },
+ { nvd9_grctx_init_gpc_unk_1 },
+ { nvd9_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvd9_grctx_init_crstr_0 },
+ { nvc1_grctx_init_gpm_0 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000001f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000023 },
@@ -420,27 +455,22 @@ nvd9_grctx_init_tpc[] = {
{ 0x419a1c, 1, 0x04, 0x00000000 },
{ 0x419a20, 1, 0x04, 0x00000800 },
{ 0x419ac4, 1, 0x04, 0x0017f440 },
- { 0x419b00, 1, 0x04, 0x0a418820 },
- { 0x419b04, 1, 0x04, 0x062080e6 },
- { 0x419b08, 1, 0x04, 0x020398a4 },
- { 0x419b0c, 1, 0x04, 0x0e629062 },
- { 0x419b10, 1, 0x04, 0x0a418820 },
- { 0x419b14, 1, 0x04, 0x000000e6 },
- { 0x419bd0, 1, 0x04, 0x00900103 },
- { 0x419be0, 1, 0x04, 0x00400001 },
- { 0x419be4, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_grctx_init_mpc_0[] = {
{ 0x419c00, 1, 0x04, 0x0000000a },
{ 0x419c04, 1, 0x04, 0x00000006 },
{ 0x419c08, 1, 0x04, 0x00000002 },
{ 0x419c20, 1, 0x04, 0x00000000 },
{ 0x419c24, 1, 0x04, 0x00084210 },
{ 0x419c28, 1, 0x04, 0x3cf3cf3c },
- { 0x419cb0, 1, 0x04, 0x00020048 },
- { 0x419ce8, 1, 0x04, 0x00000000 },
- { 0x419cf4, 1, 0x04, 0x00000183 },
- { 0x419d20, 1, 0x04, 0x12180000 },
- { 0x419d24, 1, 0x04, 0x00001fff },
- { 0x419d44, 1, 0x04, 0x02180218 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_grctx_init_sm_0[] = {
{ 0x419e04, 3, 0x04, 0x00000000 },
{ 0x419e10, 1, 0x04, 0x00000002 },
{ 0x419e44, 1, 0x04, 0x001beff2 },
@@ -453,47 +483,21 @@ nvd9_grctx_init_tpc[] = {
{}
};
-static struct nvc0_graph_init *
-nvd9_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nvd9_grctx_init_unk40xx,
- nvc0_grctx_init_unk44xx,
- nvc0_grctx_init_unk46xx,
- nvc0_grctx_init_unk47xx,
- nvd9_grctx_init_unk58xx,
- nvc0_grctx_init_unk60xx,
- nvd9_grctx_init_unk64xx,
- nvc0_grctx_init_unk78xx,
- nvc0_grctx_init_unk80xx,
- nvd9_grctx_init_rop,
- NULL
-};
-
-struct nvc0_graph_init *
-nvd9_grctx_init_gpc[] = {
- nvd9_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvd9_grctx_init_tpc,
- NULL
-};
-
-struct nvc0_graph_init
-nvd9_grctx_init_mthd_magic[] = {
- { 0x3410, 1, 0x04, 0x80002006 },
+static const struct nvc0_graph_pack
+nvd9_grctx_pack_tpc[] = {
+ { nvc1_grctx_init_pe_0 },
+ { nvd9_grctx_init_tex_0 },
+ { nvc1_grctx_init_wwdx_0 },
+ { nvd9_grctx_init_mpc_0 },
+ { nvc4_grctx_init_l1c_0 },
+ { nvc1_grctx_init_tpccs_0 },
+ { nvd9_grctx_init_sm_0 },
{}
};
-struct nvc0_graph_mthd
-nvd9_grctx_init_mthd[] = {
- { 0x9097, nvc1_grctx_init_9097, },
- { 0x9197, nvc8_grctx_init_9197, },
- { 0x9297, nvc8_grctx_init_9297, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x9039, nvc0_grctx_init_9039, },
- { 0x90c0, nvd9_grctx_init_90c0, },
- { 0x902d, nvd9_grctx_init_mthd_magic, },
- {}
-};
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
struct nouveau_oclass *
nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) {
@@ -506,11 +510,13 @@ nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nvc0_grctx_generate_main,
- .mods = nvc1_grctx_generate_mods,
- .unkn = nvc1_grctx_generate_unkn,
- .hub = nvd9_grctx_init_hub,
- .gpc = nvd9_grctx_init_gpc,
- .icmd = nvd9_grctx_init_icmd,
- .mthd = nvd9_grctx_init_mthd,
+ .main = nvc0_grctx_generate_main,
+ .mods = nvc1_grctx_generate_mods,
+ .unkn = nvc1_grctx_generate_unkn,
+ .hub = nvd9_grctx_pack_hub,
+ .gpc = nvd9_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvd9_grctx_pack_tpc,
+ .icmd = nvd9_grctx_pack_icmd,
+ .mthd = nvd9_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
index e2de73ee5eee..49a14b116a5f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
@@ -22,10 +22,14 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-struct nvc0_graph_init
-nve4_grctx_init_icmd[] = {
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nve4_grctx_init_icmd_0[] = {
{ 0x001000, 1, 0x01, 0x00000004 },
{ 0x000039, 3, 0x01, 0x00000000 },
{ 0x0000a9, 1, 0x01, 0x0000ffff },
@@ -138,8 +142,7 @@ nve4_grctx_init_icmd[] = {
{ 0x000586, 1, 0x01, 0x00000040 },
{ 0x000582, 2, 0x01, 0x00000080 },
{ 0x0005c2, 1, 0x01, 0x00000001 },
- { 0x000638, 1, 0x01, 0x00000001 },
- { 0x000639, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
{ 0x00063a, 1, 0x01, 0x00000002 },
{ 0x00063b, 2, 0x01, 0x00000001 },
{ 0x00063d, 1, 0x01, 0x00000002 },
@@ -197,15 +200,13 @@ nve4_grctx_init_icmd[] = {
{ 0x000787, 1, 0x01, 0x000000cf },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x000836, 1, 0x01, 0x00000001 },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x000b07, 1, 0x01, 0x00000002 },
{ 0x000b08, 2, 0x01, 0x00000100 },
@@ -231,14 +232,12 @@ nve4_grctx_init_icmd[] = {
{ 0x0006b1, 1, 0x01, 0x00000011 },
{ 0x00078c, 1, 0x01, 0x00000008 },
{ 0x000792, 1, 0x01, 0x00000001 },
- { 0x000794, 1, 0x01, 0x00000001 },
- { 0x000795, 2, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
{ 0x000797, 1, 0x01, 0x000000cf },
{ 0x00079a, 1, 0x01, 0x00000002 },
{ 0x000833, 1, 0x01, 0x04444480 },
{ 0x0007a1, 1, 0x01, 0x00000001 },
- { 0x0007a3, 1, 0x01, 0x00000001 },
- { 0x0007a4, 2, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
{ 0x000831, 1, 0x01, 0x00000004 },
{ 0x01e100, 1, 0x01, 0x00000001 },
{ 0x001000, 1, 0x01, 0x00000008 },
@@ -273,8 +272,14 @@ nve4_grctx_init_icmd[] = {
{}
};
-struct nvc0_graph_init
-nve4_grctx_init_a097[] = {
+static const struct nvc0_graph_pack
+nve4_grctx_pack_icmd[] = {
+ { nve4_grctx_init_icmd_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_a097_0[] = {
{ 0x000800, 8, 0x40, 0x00000000 },
{ 0x000804, 8, 0x40, 0x00000000 },
{ 0x000808, 8, 0x40, 0x00000400 },
@@ -517,8 +522,7 @@ nve4_grctx_init_a097[] = {
{ 0x001350, 1, 0x04, 0x00000002 },
{ 0x001358, 1, 0x04, 0x00000001 },
{ 0x0012e4, 1, 0x04, 0x00000000 },
- { 0x00131c, 1, 0x04, 0x00000000 },
- { 0x001320, 3, 0x04, 0x00000000 },
+ { 0x00131c, 4, 0x04, 0x00000000 },
{ 0x0019c0, 1, 0x04, 0x00000000 },
{ 0x001140, 1, 0x04, 0x00000000 },
{ 0x0019c4, 1, 0x04, 0x00000000 },
@@ -574,19 +578,24 @@ nve4_grctx_init_a097[] = {
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_unk40xx[] = {
+static const struct nvc0_graph_pack
+nve4_grctx_pack_mthd[] = {
+ { nve4_grctx_init_a097_0, 0xa097 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_fe_0[] = {
{ 0x404010, 5, 0x04, 0x00000000 },
{ 0x404024, 1, 0x04, 0x0000e000 },
{ 0x404028, 1, 0x04, 0x00000000 },
- { 0x4040a8, 1, 0x04, 0x00000000 },
- { 0x4040ac, 7, 0x04, 0x00000000 },
+ { 0x4040a8, 8, 0x04, 0x00000000 },
{ 0x4040c8, 1, 0x04, 0xf800008f },
{ 0x4040d0, 6, 0x04, 0x00000000 },
{ 0x4040e8, 1, 0x04, 0x00001000 },
{ 0x4040f8, 1, 0x04, 0x00000000 },
- { 0x404130, 1, 0x04, 0x00000000 },
- { 0x404134, 1, 0x04, 0x00000000 },
+ { 0x404130, 2, 0x04, 0x00000000 },
{ 0x404138, 1, 0x04, 0x20000040 },
{ 0x404150, 1, 0x04, 0x0000002e },
{ 0x404154, 1, 0x04, 0x00000400 },
@@ -597,8 +606,8 @@ nve4_grctx_init_unk40xx[] = {
{}
};
-struct nvc0_graph_init
-nve4_grctx_init_unk46xx[] = {
+const struct nvc0_graph_init
+nve4_grctx_init_memfmt_0[] = {
{ 0x404604, 1, 0x04, 0x00000014 },
{ 0x404608, 1, 0x04, 0x00000000 },
{ 0x40460c, 1, 0x04, 0x00003fff },
@@ -614,11 +623,6 @@ nve4_grctx_init_unk46xx[] = {
{ 0x4046a0, 1, 0x04, 0x007f0080 },
{ 0x4046a4, 8, 0x04, 0x00000000 },
{ 0x4046c8, 3, 0x04, 0x00000000 },
- {}
-};
-
-struct nvc0_graph_init
-nve4_grctx_init_unk47xx[] = {
{ 0x404700, 3, 0x04, 0x00000000 },
{ 0x404718, 7, 0x04, 0x00000000 },
{ 0x404734, 1, 0x04, 0x00000100 },
@@ -628,8 +632,8 @@ nve4_grctx_init_unk47xx[] = {
{}
};
-struct nvc0_graph_init
-nve4_grctx_init_unk58xx[] = {
+const struct nvc0_graph_init
+nve4_grctx_init_ds_0[] = {
{ 0x405800, 1, 0x04, 0x0f8000bf },
{ 0x405830, 1, 0x04, 0x02180648 },
{ 0x405834, 1, 0x04, 0x08000000 },
@@ -641,22 +645,17 @@ nve4_grctx_init_unk58xx[] = {
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_unk5bxx[] = {
+static const struct nvc0_graph_init
+nve4_grctx_init_cwd_0[] = {
{ 0x405b00, 1, 0x04, 0x00000000 },
{ 0x405b10, 1, 0x04, 0x00001000 },
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_unk60xx[] = {
+static const struct nvc0_graph_init
+nve4_grctx_init_pd_0[] = {
{ 0x406020, 1, 0x04, 0x004103c1 },
{ 0x406028, 4, 0x04, 0x00000001 },
- {}
-};
-
-static struct nvc0_graph_init
-nve4_grctx_init_unk64xx[] = {
{ 0x4064a8, 1, 0x04, 0x00000000 },
{ 0x4064ac, 1, 0x04, 0x00003fff },
{ 0x4064b4, 2, 0x04, 0x00000000 },
@@ -668,14 +667,14 @@ nve4_grctx_init_unk64xx[] = {
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_unk70xx[] = {
+static const struct nvc0_graph_init
+nve4_grctx_init_sked_0[] = {
{ 0x407040, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nve4_grctx_init_unk80xx[] = {
+const struct nvc0_graph_init
+nve4_grctx_init_scc_0[] = {
{ 0x408000, 2, 0x04, 0x00000000 },
{ 0x408008, 1, 0x04, 0x00000030 },
{ 0x40800c, 2, 0x04, 0x00000000 },
@@ -685,8 +684,8 @@ nve4_grctx_init_unk80xx[] = {
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_rop[] = {
+static const struct nvc0_graph_init
+nve4_grctx_init_be_0[] = {
{ 0x408800, 1, 0x04, 0x02802a3c },
{ 0x408804, 1, 0x04, 0x00000040 },
{ 0x408808, 1, 0x04, 0x1043e005 },
@@ -698,22 +697,24 @@ nve4_grctx_init_rop[] = {
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_gpc_0[] = {
- { 0x418380, 1, 0x04, 0x00000016 },
- { 0x418400, 1, 0x04, 0x38004e00 },
- { 0x418404, 1, 0x04, 0x71e0ffff },
- { 0x41840c, 1, 0x04, 0x00001008 },
- { 0x418410, 1, 0x04, 0x0fff0fff },
- { 0x418414, 1, 0x04, 0x02200fff },
- { 0x418450, 6, 0x04, 0x00000000 },
- { 0x418468, 1, 0x04, 0x00000001 },
- { 0x41846c, 2, 0x04, 0x00000000 },
- { 0x418600, 1, 0x04, 0x0000001f },
- { 0x418684, 1, 0x04, 0x0000000f },
- { 0x418700, 1, 0x04, 0x00000002 },
- { 0x418704, 1, 0x04, 0x00000080 },
- { 0x418708, 3, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nve4_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nve4_grctx_init_fe_0 },
+ { nvc0_grctx_init_pri_0 },
+ { nve4_grctx_init_memfmt_0 },
+ { nve4_grctx_init_ds_0 },
+ { nve4_grctx_init_cwd_0 },
+ { nve4_grctx_init_pd_0 },
+ { nve4_grctx_init_sked_0 },
+ { nvc0_grctx_init_rstr2d_0 },
+ { nve4_grctx_init_scc_0 },
+ { nve4_grctx_init_be_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x7006860a },
{ 0x418808, 3, 0x04, 0x00000000 },
{ 0x418828, 1, 0x04, 0x00000044 },
@@ -722,35 +723,35 @@ nve4_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x20100018 },
- { 0x41891c, 1, 0x04, 0x00ff00ff },
- { 0x418924, 1, 0x04, 0x00000000 },
- { 0x418928, 1, 0x04, 0x00ffff00 },
- { 0x41892c, 1, 0x04, 0x0000ff00 },
- { 0x418b00, 1, 0x04, 0x00000006 },
- { 0x418b08, 1, 0x04, 0x0a418820 },
- { 0x418b0c, 1, 0x04, 0x062080e6 },
- { 0x418b10, 1, 0x04, 0x020398a4 },
- { 0x418b14, 1, 0x04, 0x0e629062 },
- { 0x418b18, 1, 0x04, 0x0a418820 },
- { 0x418b1c, 1, 0x04, 0x000000e6 },
- { 0x418bb8, 1, 0x04, 0x00000103 },
+ {}
+};
+
+const struct nvc0_graph_init
+nve4_grctx_init_gpm_0[] = {
{ 0x418c08, 1, 0x04, 0x00000001 },
{ 0x418c10, 8, 0x04, 0x00000000 },
{ 0x418c40, 1, 0x04, 0xffffffff },
{ 0x418c6c, 1, 0x04, 0x00000001 },
{ 0x418c80, 1, 0x04, 0x20200004 },
{ 0x418c8c, 1, 0x04, 0x00000001 },
- { 0x419000, 1, 0x04, 0x00000780 },
- { 0x419004, 2, 0x04, 0x00000000 },
- { 0x419014, 1, 0x04, 0x00000004 },
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_tpc[] = {
- { 0x419848, 1, 0x04, 0x00000000 },
- { 0x419864, 1, 0x04, 0x00000129 },
- { 0x419888, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nve4_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvd9_grctx_init_prop_0 },
+ { nvd9_grctx_init_gpc_unk_1 },
+ { nve4_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvd9_grctx_init_crstr_0 },
+ { nve4_grctx_init_gpm_0 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000000f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000021 },
@@ -761,14 +762,29 @@ nve4_grctx_init_tpc[] = {
{ 0x419a20, 1, 0x04, 0x00000800 },
{ 0x419a30, 1, 0x04, 0x00000001 },
{ 0x419ac4, 1, 0x04, 0x0037f440 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_mpc_0[] = {
{ 0x419c00, 1, 0x04, 0x0000000a },
{ 0x419c04, 1, 0x04, 0x80000006 },
{ 0x419c08, 1, 0x04, 0x00000002 },
{ 0x419c20, 1, 0x04, 0x00000000 },
{ 0x419c24, 1, 0x04, 0x00084210 },
{ 0x419c28, 1, 0x04, 0x3efbefbe },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_l1c_0[] = {
{ 0x419ce8, 1, 0x04, 0x00000000 },
{ 0x419cf4, 1, 0x04, 0x00003203 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_sm_0[] = {
{ 0x419e04, 3, 0x04, 0x00000000 },
{ 0x419e10, 1, 0x04, 0x00000402 },
{ 0x419e44, 1, 0x04, 0x0013eff2 },
@@ -782,28 +798,46 @@ nve4_grctx_init_tpc[] = {
{ 0x419f58, 1, 0x04, 0x00000000 },
{ 0x419f70, 1, 0x04, 0x00000000 },
{ 0x419f78, 1, 0x04, 0x0000000b },
- { 0x419f7c, 1, 0x04, 0x0000027a },
+ { 0x419f7c, 1, 0x04, 0x0000027c },
+ {}
+};
+
+static const struct nvc0_graph_pack
+nve4_grctx_pack_tpc[] = {
+ { nvd7_grctx_init_pe_0 },
+ { nve4_grctx_init_tex_0 },
+ { nve4_grctx_init_mpc_0 },
+ { nve4_grctx_init_l1c_0 },
+ { nve4_grctx_init_sm_0 },
{}
};
-static struct nvc0_graph_init
-nve4_grctx_init_unk[] = {
+const struct nvc0_graph_init
+nve4_grctx_init_pes_0[] = {
{ 0x41be24, 1, 0x04, 0x00000006 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_grctx_init_cbm_0[] = {
{ 0x41bec0, 1, 0x04, 0x12180000 },
{ 0x41bec4, 1, 0x04, 0x00037f7f },
{ 0x41bee4, 1, 0x04, 0x06480430 },
- { 0x41bf00, 1, 0x04, 0x0a418820 },
- { 0x41bf04, 1, 0x04, 0x062080e6 },
- { 0x41bf08, 1, 0x04, 0x020398a4 },
- { 0x41bf0c, 1, 0x04, 0x0e629062 },
- { 0x41bf10, 1, 0x04, 0x0a418820 },
- { 0x41bf14, 1, 0x04, 0x000000e6 },
- { 0x41bfd0, 1, 0x04, 0x00900103 },
- { 0x41bfe0, 1, 0x04, 0x00400001 },
- { 0x41bfe4, 1, 0x04, 0x00000000 },
{}
};
+static const struct nvc0_graph_pack
+nve4_grctx_pack_ppc[] = {
+ { nve4_grctx_init_pes_0 },
+ { nve4_grctx_init_cbm_0 },
+ { nvd7_grctx_init_wwdx_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
static void
nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
@@ -925,10 +959,11 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
- for (i = 0; oclass->hub[i]; i++)
- nvc0_graph_mmio(priv, oclass->hub[i]);
- for (i = 0; oclass->gpc[i]; i++)
- nvc0_graph_mmio(priv, oclass->gpc[i]);
+ nvc0_graph_mmio(priv, oclass->hub);
+ nvc0_graph_mmio(priv, oclass->gpc);
+ nvc0_graph_mmio(priv, oclass->zcull);
+ nvc0_graph_mmio(priv, oclass->tpc);
+ nvc0_graph_mmio(priv, oclass->ppc);
nv_wr32(priv, 0x404154, 0x00000000);
@@ -962,41 +997,6 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_mask(priv, 0x41be10, 0x00800000, 0x00800000);
}
-static struct nvc0_graph_init *
-nve4_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nve4_grctx_init_unk40xx,
- nvc0_grctx_init_unk44xx,
- nve4_grctx_init_unk46xx,
- nve4_grctx_init_unk47xx,
- nve4_grctx_init_unk58xx,
- nve4_grctx_init_unk5bxx,
- nve4_grctx_init_unk60xx,
- nve4_grctx_init_unk64xx,
- nve4_grctx_init_unk70xx,
- nvc0_grctx_init_unk78xx,
- nve4_grctx_init_unk80xx,
- nve4_grctx_init_rop,
- NULL
-};
-
-struct nvc0_graph_init *
-nve4_grctx_init_gpc[] = {
- nve4_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nve4_grctx_init_tpc,
- nve4_grctx_init_unk,
- NULL
-};
-
-static struct nvc0_graph_mthd
-nve4_grctx_init_mthd[] = {
- { 0xa097, nve4_grctx_init_a097, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x902d, nvc0_grctx_init_mthd_magic, },
- {}
-};
-
struct nouveau_oclass *
nve4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xe4),
@@ -1008,11 +1008,14 @@ nve4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nve4_grctx_generate_main,
- .mods = nve4_grctx_generate_mods,
- .unkn = nve4_grctx_generate_unkn,
- .hub = nve4_grctx_init_hub,
- .gpc = nve4_grctx_init_gpc,
- .icmd = nve4_grctx_init_icmd,
- .mthd = nve4_grctx_init_mthd,
+ .main = nve4_grctx_generate_main,
+ .mods = nve4_grctx_generate_mods,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = nve4_grctx_pack_hub,
+ .gpc = nve4_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nve4_grctx_pack_tpc,
+ .ppc = nve4_grctx_pack_ppc,
+ .icmd = nve4_grctx_pack_icmd,
+ .mthd = nve4_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
index 44012c3da538..0fab95e49f53 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
@@ -22,10 +22,580 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include "nvc0.h"
+#include "ctxnvc0.h"
-static struct nvc0_graph_init
-nvf0_grctx_init_unk40xx[] = {
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_icmd_0[] = {
+ { 0x001000, 1, 0x01, 0x00000004 },
+ { 0x000039, 3, 0x01, 0x00000000 },
+ { 0x0000a9, 1, 0x01, 0x0000ffff },
+ { 0x000038, 1, 0x01, 0x0fac6881 },
+ { 0x00003d, 1, 0x01, 0x00000001 },
+ { 0x0000e8, 8, 0x01, 0x00000400 },
+ { 0x000078, 8, 0x01, 0x00000300 },
+ { 0x000050, 1, 0x01, 0x00000011 },
+ { 0x000058, 8, 0x01, 0x00000008 },
+ { 0x000208, 8, 0x01, 0x00000001 },
+ { 0x000081, 1, 0x01, 0x00000001 },
+ { 0x000085, 1, 0x01, 0x00000004 },
+ { 0x000088, 1, 0x01, 0x00000400 },
+ { 0x000090, 1, 0x01, 0x00000300 },
+ { 0x000098, 1, 0x01, 0x00001001 },
+ { 0x0000e3, 1, 0x01, 0x00000001 },
+ { 0x0000da, 1, 0x01, 0x00000001 },
+ { 0x0000f8, 1, 0x01, 0x00000003 },
+ { 0x0000fa, 1, 0x01, 0x00000001 },
+ { 0x00009f, 4, 0x01, 0x0000ffff },
+ { 0x0000b1, 1, 0x01, 0x00000001 },
+ { 0x0000ad, 1, 0x01, 0x0000013e },
+ { 0x0000e1, 1, 0x01, 0x00000010 },
+ { 0x000290, 16, 0x01, 0x00000000 },
+ { 0x0003b0, 16, 0x01, 0x00000000 },
+ { 0x0002a0, 16, 0x01, 0x00000000 },
+ { 0x000420, 16, 0x01, 0x00000000 },
+ { 0x0002b0, 16, 0x01, 0x00000000 },
+ { 0x000430, 16, 0x01, 0x00000000 },
+ { 0x0002c0, 16, 0x01, 0x00000000 },
+ { 0x0004d0, 16, 0x01, 0x00000000 },
+ { 0x000720, 16, 0x01, 0x00000000 },
+ { 0x0008c0, 16, 0x01, 0x00000000 },
+ { 0x000890, 16, 0x01, 0x00000000 },
+ { 0x0008e0, 16, 0x01, 0x00000000 },
+ { 0x0008a0, 16, 0x01, 0x00000000 },
+ { 0x0008f0, 16, 0x01, 0x00000000 },
+ { 0x00094c, 1, 0x01, 0x000000ff },
+ { 0x00094d, 1, 0x01, 0xffffffff },
+ { 0x00094e, 1, 0x01, 0x00000002 },
+ { 0x0002ec, 1, 0x01, 0x00000001 },
+ { 0x0002f2, 2, 0x01, 0x00000001 },
+ { 0x0002f5, 1, 0x01, 0x00000001 },
+ { 0x0002f7, 1, 0x01, 0x00000001 },
+ { 0x000303, 1, 0x01, 0x00000001 },
+ { 0x0002e6, 1, 0x01, 0x00000001 },
+ { 0x000466, 1, 0x01, 0x00000052 },
+ { 0x000301, 1, 0x01, 0x3f800000 },
+ { 0x000304, 1, 0x01, 0x30201000 },
+ { 0x000305, 1, 0x01, 0x70605040 },
+ { 0x000306, 1, 0x01, 0xb8a89888 },
+ { 0x000307, 1, 0x01, 0xf8e8d8c8 },
+ { 0x00030a, 1, 0x01, 0x00ffff00 },
+ { 0x00030b, 1, 0x01, 0x0000001a },
+ { 0x00030c, 1, 0x01, 0x00000001 },
+ { 0x000318, 1, 0x01, 0x00000001 },
+ { 0x000340, 1, 0x01, 0x00000000 },
+ { 0x000375, 1, 0x01, 0x00000001 },
+ { 0x00037d, 1, 0x01, 0x00000006 },
+ { 0x0003a0, 1, 0x01, 0x00000002 },
+ { 0x0003aa, 1, 0x01, 0x00000001 },
+ { 0x0003a9, 1, 0x01, 0x00000001 },
+ { 0x000380, 1, 0x01, 0x00000001 },
+ { 0x000383, 1, 0x01, 0x00000011 },
+ { 0x000360, 1, 0x01, 0x00000040 },
+ { 0x000366, 2, 0x01, 0x00000000 },
+ { 0x000368, 1, 0x01, 0x00000fff },
+ { 0x000370, 2, 0x01, 0x00000000 },
+ { 0x000372, 1, 0x01, 0x000fffff },
+ { 0x00037a, 1, 0x01, 0x00000012 },
+ { 0x000619, 1, 0x01, 0x00000003 },
+ { 0x000811, 1, 0x01, 0x00000003 },
+ { 0x000812, 1, 0x01, 0x00000004 },
+ { 0x000813, 1, 0x01, 0x00000006 },
+ { 0x000814, 1, 0x01, 0x00000008 },
+ { 0x000815, 1, 0x01, 0x0000000b },
+ { 0x000800, 6, 0x01, 0x00000001 },
+ { 0x000632, 1, 0x01, 0x00000001 },
+ { 0x000633, 1, 0x01, 0x00000002 },
+ { 0x000634, 1, 0x01, 0x00000003 },
+ { 0x000635, 1, 0x01, 0x00000004 },
+ { 0x000654, 1, 0x01, 0x3f800000 },
+ { 0x000657, 1, 0x01, 0x3f800000 },
+ { 0x000655, 2, 0x01, 0x3f800000 },
+ { 0x0006cd, 1, 0x01, 0x3f800000 },
+ { 0x0007f5, 1, 0x01, 0x3f800000 },
+ { 0x0007dc, 1, 0x01, 0x39291909 },
+ { 0x0007dd, 1, 0x01, 0x79695949 },
+ { 0x0007de, 1, 0x01, 0xb9a99989 },
+ { 0x0007df, 1, 0x01, 0xf9e9d9c9 },
+ { 0x0007e8, 1, 0x01, 0x00003210 },
+ { 0x0007e9, 1, 0x01, 0x00007654 },
+ { 0x0007ea, 1, 0x01, 0x00000098 },
+ { 0x0007ec, 1, 0x01, 0x39291909 },
+ { 0x0007ed, 1, 0x01, 0x79695949 },
+ { 0x0007ee, 1, 0x01, 0xb9a99989 },
+ { 0x0007ef, 1, 0x01, 0xf9e9d9c9 },
+ { 0x0007f0, 1, 0x01, 0x00003210 },
+ { 0x0007f1, 1, 0x01, 0x00007654 },
+ { 0x0007f2, 1, 0x01, 0x00000098 },
+ { 0x0005a5, 1, 0x01, 0x00000001 },
+ { 0x000980, 128, 0x01, 0x00000000 },
+ { 0x000468, 1, 0x01, 0x00000004 },
+ { 0x00046c, 1, 0x01, 0x00000001 },
+ { 0x000470, 96, 0x01, 0x00000000 },
+ { 0x000510, 16, 0x01, 0x3f800000 },
+ { 0x000520, 1, 0x01, 0x000002b6 },
+ { 0x000529, 1, 0x01, 0x00000001 },
+ { 0x000530, 16, 0x01, 0xffff0000 },
+ { 0x000585, 1, 0x01, 0x0000003f },
+ { 0x000576, 1, 0x01, 0x00000003 },
+ { 0x00057b, 1, 0x01, 0x00000059 },
+ { 0x000586, 1, 0x01, 0x00000040 },
+ { 0x000582, 2, 0x01, 0x00000080 },
+ { 0x0005c2, 1, 0x01, 0x00000001 },
+ { 0x000638, 2, 0x01, 0x00000001 },
+ { 0x00063a, 1, 0x01, 0x00000002 },
+ { 0x00063b, 2, 0x01, 0x00000001 },
+ { 0x00063d, 1, 0x01, 0x00000002 },
+ { 0x00063e, 1, 0x01, 0x00000001 },
+ { 0x0008b8, 8, 0x01, 0x00000001 },
+ { 0x000900, 8, 0x01, 0x00000001 },
+ { 0x000908, 8, 0x01, 0x00000002 },
+ { 0x000910, 16, 0x01, 0x00000001 },
+ { 0x000920, 8, 0x01, 0x00000002 },
+ { 0x000928, 8, 0x01, 0x00000001 },
+ { 0x000662, 1, 0x01, 0x00000001 },
+ { 0x000648, 9, 0x01, 0x00000001 },
+ { 0x000658, 1, 0x01, 0x0000000f },
+ { 0x0007ff, 1, 0x01, 0x0000000a },
+ { 0x00066a, 1, 0x01, 0x40000000 },
+ { 0x00066b, 1, 0x01, 0x10000000 },
+ { 0x00066c, 2, 0x01, 0xffff0000 },
+ { 0x0007af, 2, 0x01, 0x00000008 },
+ { 0x0007f6, 1, 0x01, 0x00000001 },
+ { 0x00080b, 1, 0x01, 0x00000002 },
+ { 0x0006b2, 1, 0x01, 0x00000055 },
+ { 0x0007ad, 1, 0x01, 0x00000003 },
+ { 0x000937, 1, 0x01, 0x00000001 },
+ { 0x000971, 1, 0x01, 0x00000008 },
+ { 0x000972, 1, 0x01, 0x00000040 },
+ { 0x000973, 1, 0x01, 0x0000012c },
+ { 0x00097c, 1, 0x01, 0x00000040 },
+ { 0x000979, 1, 0x01, 0x00000003 },
+ { 0x000975, 1, 0x01, 0x00000020 },
+ { 0x000976, 1, 0x01, 0x00000001 },
+ { 0x000977, 1, 0x01, 0x00000020 },
+ { 0x000978, 1, 0x01, 0x00000001 },
+ { 0x000957, 1, 0x01, 0x00000003 },
+ { 0x00095e, 1, 0x01, 0x20164010 },
+ { 0x00095f, 1, 0x01, 0x00000020 },
+ { 0x000a0d, 1, 0x01, 0x00000006 },
+ { 0x00097d, 1, 0x01, 0x00000020 },
+ { 0x000683, 1, 0x01, 0x00000006 },
+ { 0x000685, 1, 0x01, 0x003fffff },
+ { 0x000687, 1, 0x01, 0x003fffff },
+ { 0x0006a0, 1, 0x01, 0x00000005 },
+ { 0x000840, 1, 0x01, 0x00400008 },
+ { 0x000841, 1, 0x01, 0x08000080 },
+ { 0x000842, 1, 0x01, 0x00400008 },
+ { 0x000843, 1, 0x01, 0x08000080 },
+ { 0x0006aa, 1, 0x01, 0x00000001 },
+ { 0x0006ab, 1, 0x01, 0x00000002 },
+ { 0x0006ac, 1, 0x01, 0x00000080 },
+ { 0x0006ad, 2, 0x01, 0x00000100 },
+ { 0x0006b1, 1, 0x01, 0x00000011 },
+ { 0x0006bb, 1, 0x01, 0x000000cf },
+ { 0x0006ce, 1, 0x01, 0x2a712488 },
+ { 0x000739, 1, 0x01, 0x4085c000 },
+ { 0x00073a, 1, 0x01, 0x00000080 },
+ { 0x000786, 1, 0x01, 0x80000100 },
+ { 0x00073c, 1, 0x01, 0x00010100 },
+ { 0x00073d, 1, 0x01, 0x02800000 },
+ { 0x000787, 1, 0x01, 0x000000cf },
+ { 0x00078c, 1, 0x01, 0x00000008 },
+ { 0x000792, 1, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
+ { 0x000797, 1, 0x01, 0x000000cf },
+ { 0x000836, 1, 0x01, 0x00000001 },
+ { 0x00079a, 1, 0x01, 0x00000002 },
+ { 0x000833, 1, 0x01, 0x04444480 },
+ { 0x0007a1, 1, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
+ { 0x000831, 1, 0x01, 0x00000004 },
+ { 0x000b07, 1, 0x01, 0x00000002 },
+ { 0x000b08, 2, 0x01, 0x00000100 },
+ { 0x000b0a, 1, 0x01, 0x00000001 },
+ { 0x000a04, 1, 0x01, 0x000000ff },
+ { 0x000a0b, 1, 0x01, 0x00000040 },
+ { 0x00097f, 1, 0x01, 0x00000100 },
+ { 0x000a02, 1, 0x01, 0x00000001 },
+ { 0x000809, 1, 0x01, 0x00000007 },
+ { 0x00c221, 1, 0x01, 0x00000040 },
+ { 0x00c1b0, 8, 0x01, 0x0000000f },
+ { 0x00c1b8, 1, 0x01, 0x0fac6881 },
+ { 0x00c1b9, 1, 0x01, 0x00fac688 },
+ { 0x00c401, 1, 0x01, 0x00000001 },
+ { 0x00c402, 1, 0x01, 0x00010001 },
+ { 0x00c403, 2, 0x01, 0x00000001 },
+ { 0x00c40e, 1, 0x01, 0x00000020 },
+ { 0x00c500, 1, 0x01, 0x00000003 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ { 0x001000, 1, 0x01, 0x00000002 },
+ { 0x0006aa, 1, 0x01, 0x00000001 },
+ { 0x0006ad, 2, 0x01, 0x00000100 },
+ { 0x0006b1, 1, 0x01, 0x00000011 },
+ { 0x00078c, 1, 0x01, 0x00000008 },
+ { 0x000792, 1, 0x01, 0x00000001 },
+ { 0x000794, 3, 0x01, 0x00000001 },
+ { 0x000797, 1, 0x01, 0x000000cf },
+ { 0x00079a, 1, 0x01, 0x00000002 },
+ { 0x000833, 1, 0x01, 0x04444480 },
+ { 0x0007a1, 1, 0x01, 0x00000001 },
+ { 0x0007a3, 3, 0x01, 0x00000001 },
+ { 0x000831, 1, 0x01, 0x00000004 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ { 0x001000, 1, 0x01, 0x00000008 },
+ { 0x000039, 3, 0x01, 0x00000000 },
+ { 0x000380, 1, 0x01, 0x00000001 },
+ { 0x000366, 2, 0x01, 0x00000000 },
+ { 0x000368, 1, 0x01, 0x00000fff },
+ { 0x000370, 2, 0x01, 0x00000000 },
+ { 0x000372, 1, 0x01, 0x000fffff },
+ { 0x000813, 1, 0x01, 0x00000006 },
+ { 0x000814, 1, 0x01, 0x00000008 },
+ { 0x000957, 1, 0x01, 0x00000003 },
+ { 0x000b07, 1, 0x01, 0x00000002 },
+ { 0x000b08, 2, 0x01, 0x00000100 },
+ { 0x000b0a, 1, 0x01, 0x00000001 },
+ { 0x000a04, 1, 0x01, 0x000000ff },
+ { 0x000a0b, 1, 0x01, 0x00000040 },
+ { 0x00097f, 1, 0x01, 0x00000100 },
+ { 0x000a02, 1, 0x01, 0x00000001 },
+ { 0x000809, 1, 0x01, 0x00000007 },
+ { 0x00c221, 1, 0x01, 0x00000040 },
+ { 0x00c401, 1, 0x01, 0x00000001 },
+ { 0x00c402, 1, 0x01, 0x00010001 },
+ { 0x00c403, 2, 0x01, 0x00000001 },
+ { 0x00c40e, 1, 0x01, 0x00000020 },
+ { 0x00c500, 1, 0x01, 0x00000003 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ { 0x001000, 1, 0x01, 0x00000001 },
+ { 0x000b07, 1, 0x01, 0x00000002 },
+ { 0x000b08, 2, 0x01, 0x00000100 },
+ { 0x000b0a, 1, 0x01, 0x00000001 },
+ { 0x01e100, 1, 0x01, 0x00000001 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+nvf0_grctx_pack_icmd[] = {
+ { nvf0_grctx_init_icmd_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_a197_0[] = {
+ { 0x000800, 8, 0x40, 0x00000000 },
+ { 0x000804, 8, 0x40, 0x00000000 },
+ { 0x000808, 8, 0x40, 0x00000400 },
+ { 0x00080c, 8, 0x40, 0x00000300 },
+ { 0x000810, 1, 0x04, 0x000000cf },
+ { 0x000850, 7, 0x40, 0x00000000 },
+ { 0x000814, 8, 0x40, 0x00000040 },
+ { 0x000818, 8, 0x40, 0x00000001 },
+ { 0x00081c, 8, 0x40, 0x00000000 },
+ { 0x000820, 8, 0x40, 0x00000000 },
+ { 0x001c00, 16, 0x10, 0x00000000 },
+ { 0x001c04, 16, 0x10, 0x00000000 },
+ { 0x001c08, 16, 0x10, 0x00000000 },
+ { 0x001c0c, 16, 0x10, 0x00000000 },
+ { 0x001d00, 16, 0x10, 0x00000000 },
+ { 0x001d04, 16, 0x10, 0x00000000 },
+ { 0x001d08, 16, 0x10, 0x00000000 },
+ { 0x001d0c, 16, 0x10, 0x00000000 },
+ { 0x001f00, 16, 0x08, 0x00000000 },
+ { 0x001f04, 16, 0x08, 0x00000000 },
+ { 0x001f80, 16, 0x08, 0x00000000 },
+ { 0x001f84, 16, 0x08, 0x00000000 },
+ { 0x002000, 1, 0x04, 0x00000000 },
+ { 0x002040, 1, 0x04, 0x00000011 },
+ { 0x002080, 1, 0x04, 0x00000020 },
+ { 0x0020c0, 1, 0x04, 0x00000030 },
+ { 0x002100, 1, 0x04, 0x00000040 },
+ { 0x002140, 1, 0x04, 0x00000051 },
+ { 0x00200c, 6, 0x40, 0x00000001 },
+ { 0x002010, 1, 0x04, 0x00000000 },
+ { 0x002050, 1, 0x04, 0x00000000 },
+ { 0x002090, 1, 0x04, 0x00000001 },
+ { 0x0020d0, 1, 0x04, 0x00000002 },
+ { 0x002110, 1, 0x04, 0x00000003 },
+ { 0x002150, 1, 0x04, 0x00000004 },
+ { 0x000380, 4, 0x20, 0x00000000 },
+ { 0x000384, 4, 0x20, 0x00000000 },
+ { 0x000388, 4, 0x20, 0x00000000 },
+ { 0x00038c, 4, 0x20, 0x00000000 },
+ { 0x000700, 4, 0x10, 0x00000000 },
+ { 0x000704, 4, 0x10, 0x00000000 },
+ { 0x000708, 4, 0x10, 0x00000000 },
+ { 0x002800, 128, 0x04, 0x00000000 },
+ { 0x000a00, 16, 0x20, 0x00000000 },
+ { 0x000a04, 16, 0x20, 0x00000000 },
+ { 0x000a08, 16, 0x20, 0x00000000 },
+ { 0x000a0c, 16, 0x20, 0x00000000 },
+ { 0x000a10, 16, 0x20, 0x00000000 },
+ { 0x000a14, 16, 0x20, 0x00000000 },
+ { 0x000c00, 16, 0x10, 0x00000000 },
+ { 0x000c04, 16, 0x10, 0x00000000 },
+ { 0x000c08, 16, 0x10, 0x00000000 },
+ { 0x000c0c, 16, 0x10, 0x3f800000 },
+ { 0x000d00, 8, 0x08, 0xffff0000 },
+ { 0x000d04, 8, 0x08, 0xffff0000 },
+ { 0x000e00, 16, 0x10, 0x00000000 },
+ { 0x000e04, 16, 0x10, 0xffff0000 },
+ { 0x000e08, 16, 0x10, 0xffff0000 },
+ { 0x000d40, 4, 0x08, 0x00000000 },
+ { 0x000d44, 4, 0x08, 0x00000000 },
+ { 0x001e00, 8, 0x20, 0x00000001 },
+ { 0x001e04, 8, 0x20, 0x00000001 },
+ { 0x001e08, 8, 0x20, 0x00000002 },
+ { 0x001e0c, 8, 0x20, 0x00000001 },
+ { 0x001e10, 8, 0x20, 0x00000001 },
+ { 0x001e14, 8, 0x20, 0x00000002 },
+ { 0x001e18, 8, 0x20, 0x00000001 },
+ { 0x003400, 128, 0x04, 0x00000000 },
+ { 0x00030c, 1, 0x04, 0x00000001 },
+ { 0x001944, 1, 0x04, 0x00000000 },
+ { 0x001514, 1, 0x04, 0x00000000 },
+ { 0x000d68, 1, 0x04, 0x0000ffff },
+ { 0x00121c, 1, 0x04, 0x0fac6881 },
+ { 0x000fac, 1, 0x04, 0x00000001 },
+ { 0x001538, 1, 0x04, 0x00000001 },
+ { 0x000fe0, 2, 0x04, 0x00000000 },
+ { 0x000fe8, 1, 0x04, 0x00000014 },
+ { 0x000fec, 1, 0x04, 0x00000040 },
+ { 0x000ff0, 1, 0x04, 0x00000000 },
+ { 0x00179c, 1, 0x04, 0x00000000 },
+ { 0x001228, 1, 0x04, 0x00000400 },
+ { 0x00122c, 1, 0x04, 0x00000300 },
+ { 0x001230, 1, 0x04, 0x00010001 },
+ { 0x0007f8, 1, 0x04, 0x00000000 },
+ { 0x0015b4, 1, 0x04, 0x00000001 },
+ { 0x0015cc, 1, 0x04, 0x00000000 },
+ { 0x001534, 1, 0x04, 0x00000000 },
+ { 0x000fb0, 1, 0x04, 0x00000000 },
+ { 0x0015d0, 1, 0x04, 0x00000000 },
+ { 0x00153c, 1, 0x04, 0x00000000 },
+ { 0x0016b4, 1, 0x04, 0x00000003 },
+ { 0x000fbc, 4, 0x04, 0x0000ffff },
+ { 0x000df8, 2, 0x04, 0x00000000 },
+ { 0x001948, 1, 0x04, 0x00000000 },
+ { 0x001970, 1, 0x04, 0x00000001 },
+ { 0x00161c, 1, 0x04, 0x000009f0 },
+ { 0x000dcc, 1, 0x04, 0x00000010 },
+ { 0x00163c, 1, 0x04, 0x00000000 },
+ { 0x0015e4, 1, 0x04, 0x00000000 },
+ { 0x001160, 32, 0x04, 0x25e00040 },
+ { 0x001880, 32, 0x04, 0x00000000 },
+ { 0x000f84, 2, 0x04, 0x00000000 },
+ { 0x0017c8, 2, 0x04, 0x00000000 },
+ { 0x0017d0, 1, 0x04, 0x000000ff },
+ { 0x0017d4, 1, 0x04, 0xffffffff },
+ { 0x0017d8, 1, 0x04, 0x00000002 },
+ { 0x0017dc, 1, 0x04, 0x00000000 },
+ { 0x0015f4, 2, 0x04, 0x00000000 },
+ { 0x001434, 2, 0x04, 0x00000000 },
+ { 0x000d74, 1, 0x04, 0x00000000 },
+ { 0x000dec, 1, 0x04, 0x00000001 },
+ { 0x0013a4, 1, 0x04, 0x00000000 },
+ { 0x001318, 1, 0x04, 0x00000001 },
+ { 0x001644, 1, 0x04, 0x00000000 },
+ { 0x000748, 1, 0x04, 0x00000000 },
+ { 0x000de8, 1, 0x04, 0x00000000 },
+ { 0x001648, 1, 0x04, 0x00000000 },
+ { 0x0012a4, 1, 0x04, 0x00000000 },
+ { 0x001120, 4, 0x04, 0x00000000 },
+ { 0x001118, 1, 0x04, 0x00000000 },
+ { 0x00164c, 1, 0x04, 0x00000000 },
+ { 0x001658, 1, 0x04, 0x00000000 },
+ { 0x001910, 1, 0x04, 0x00000290 },
+ { 0x001518, 1, 0x04, 0x00000000 },
+ { 0x00165c, 1, 0x04, 0x00000001 },
+ { 0x001520, 1, 0x04, 0x00000000 },
+ { 0x001604, 1, 0x04, 0x00000000 },
+ { 0x001570, 1, 0x04, 0x00000000 },
+ { 0x0013b0, 2, 0x04, 0x3f800000 },
+ { 0x00020c, 1, 0x04, 0x00000000 },
+ { 0x001670, 1, 0x04, 0x30201000 },
+ { 0x001674, 1, 0x04, 0x70605040 },
+ { 0x001678, 1, 0x04, 0xb8a89888 },
+ { 0x00167c, 1, 0x04, 0xf8e8d8c8 },
+ { 0x00166c, 1, 0x04, 0x00000000 },
+ { 0x001680, 1, 0x04, 0x00ffff00 },
+ { 0x0012d0, 1, 0x04, 0x00000003 },
+ { 0x0012d4, 1, 0x04, 0x00000002 },
+ { 0x001684, 2, 0x04, 0x00000000 },
+ { 0x000dac, 2, 0x04, 0x00001b02 },
+ { 0x000db4, 1, 0x04, 0x00000000 },
+ { 0x00168c, 1, 0x04, 0x00000000 },
+ { 0x0015bc, 1, 0x04, 0x00000000 },
+ { 0x00156c, 1, 0x04, 0x00000000 },
+ { 0x00187c, 1, 0x04, 0x00000000 },
+ { 0x001110, 1, 0x04, 0x00000001 },
+ { 0x000dc0, 3, 0x04, 0x00000000 },
+ { 0x001234, 1, 0x04, 0x00000000 },
+ { 0x001690, 1, 0x04, 0x00000000 },
+ { 0x0012ac, 1, 0x04, 0x00000001 },
+ { 0x0002c4, 1, 0x04, 0x00000000 },
+ { 0x000790, 5, 0x04, 0x00000000 },
+ { 0x00077c, 1, 0x04, 0x00000000 },
+ { 0x001000, 1, 0x04, 0x00000010 },
+ { 0x0010fc, 1, 0x04, 0x00000000 },
+ { 0x001290, 1, 0x04, 0x00000000 },
+ { 0x000218, 1, 0x04, 0x00000010 },
+ { 0x0012d8, 1, 0x04, 0x00000000 },
+ { 0x0012dc, 1, 0x04, 0x00000010 },
+ { 0x000d94, 1, 0x04, 0x00000001 },
+ { 0x00155c, 2, 0x04, 0x00000000 },
+ { 0x001564, 1, 0x04, 0x00000fff },
+ { 0x001574, 2, 0x04, 0x00000000 },
+ { 0x00157c, 1, 0x04, 0x000fffff },
+ { 0x001354, 1, 0x04, 0x00000000 },
+ { 0x001610, 1, 0x04, 0x00000012 },
+ { 0x001608, 2, 0x04, 0x00000000 },
+ { 0x00260c, 1, 0x04, 0x00000000 },
+ { 0x0007ac, 1, 0x04, 0x00000000 },
+ { 0x00162c, 1, 0x04, 0x00000003 },
+ { 0x000210, 1, 0x04, 0x00000000 },
+ { 0x000320, 1, 0x04, 0x00000000 },
+ { 0x000324, 6, 0x04, 0x3f800000 },
+ { 0x000750, 1, 0x04, 0x00000000 },
+ { 0x000760, 1, 0x04, 0x39291909 },
+ { 0x000764, 1, 0x04, 0x79695949 },
+ { 0x000768, 1, 0x04, 0xb9a99989 },
+ { 0x00076c, 1, 0x04, 0xf9e9d9c9 },
+ { 0x000770, 1, 0x04, 0x30201000 },
+ { 0x000774, 1, 0x04, 0x70605040 },
+ { 0x000778, 1, 0x04, 0x00009080 },
+ { 0x000780, 1, 0x04, 0x39291909 },
+ { 0x000784, 1, 0x04, 0x79695949 },
+ { 0x000788, 1, 0x04, 0xb9a99989 },
+ { 0x00078c, 1, 0x04, 0xf9e9d9c9 },
+ { 0x0007d0, 1, 0x04, 0x30201000 },
+ { 0x0007d4, 1, 0x04, 0x70605040 },
+ { 0x0007d8, 1, 0x04, 0x00009080 },
+ { 0x00037c, 1, 0x04, 0x00000001 },
+ { 0x000740, 2, 0x04, 0x00000000 },
+ { 0x002600, 1, 0x04, 0x00000000 },
+ { 0x001918, 1, 0x04, 0x00000000 },
+ { 0x00191c, 1, 0x04, 0x00000900 },
+ { 0x001920, 1, 0x04, 0x00000405 },
+ { 0x001308, 1, 0x04, 0x00000001 },
+ { 0x001924, 1, 0x04, 0x00000000 },
+ { 0x0013ac, 1, 0x04, 0x00000000 },
+ { 0x00192c, 1, 0x04, 0x00000001 },
+ { 0x00193c, 1, 0x04, 0x00002c1c },
+ { 0x000d7c, 1, 0x04, 0x00000000 },
+ { 0x000f8c, 1, 0x04, 0x00000000 },
+ { 0x0002c0, 1, 0x04, 0x00000001 },
+ { 0x001510, 1, 0x04, 0x00000000 },
+ { 0x001940, 1, 0x04, 0x00000000 },
+ { 0x000ff4, 2, 0x04, 0x00000000 },
+ { 0x00194c, 2, 0x04, 0x00000000 },
+ { 0x001968, 1, 0x04, 0x00000000 },
+ { 0x001590, 1, 0x04, 0x0000003f },
+ { 0x0007e8, 4, 0x04, 0x00000000 },
+ { 0x00196c, 1, 0x04, 0x00000011 },
+ { 0x0002e4, 1, 0x04, 0x0000b001 },
+ { 0x00036c, 2, 0x04, 0x00000000 },
+ { 0x00197c, 1, 0x04, 0x00000000 },
+ { 0x000fcc, 2, 0x04, 0x00000000 },
+ { 0x0002d8, 1, 0x04, 0x00000040 },
+ { 0x001980, 1, 0x04, 0x00000080 },
+ { 0x001504, 1, 0x04, 0x00000080 },
+ { 0x001984, 1, 0x04, 0x00000000 },
+ { 0x000300, 1, 0x04, 0x00000001 },
+ { 0x0013a8, 1, 0x04, 0x00000000 },
+ { 0x0012ec, 1, 0x04, 0x00000000 },
+ { 0x001310, 1, 0x04, 0x00000000 },
+ { 0x001314, 1, 0x04, 0x00000001 },
+ { 0x001380, 1, 0x04, 0x00000000 },
+ { 0x001384, 4, 0x04, 0x00000001 },
+ { 0x001394, 1, 0x04, 0x00000000 },
+ { 0x00139c, 1, 0x04, 0x00000000 },
+ { 0x001398, 1, 0x04, 0x00000000 },
+ { 0x001594, 1, 0x04, 0x00000000 },
+ { 0x001598, 4, 0x04, 0x00000001 },
+ { 0x000f54, 3, 0x04, 0x00000000 },
+ { 0x0019bc, 1, 0x04, 0x00000000 },
+ { 0x000f9c, 2, 0x04, 0x00000000 },
+ { 0x0012cc, 1, 0x04, 0x00000000 },
+ { 0x0012e8, 1, 0x04, 0x00000000 },
+ { 0x00130c, 1, 0x04, 0x00000001 },
+ { 0x001360, 8, 0x04, 0x00000000 },
+ { 0x00133c, 2, 0x04, 0x00000001 },
+ { 0x001344, 1, 0x04, 0x00000002 },
+ { 0x001348, 2, 0x04, 0x00000001 },
+ { 0x001350, 1, 0x04, 0x00000002 },
+ { 0x001358, 1, 0x04, 0x00000001 },
+ { 0x0012e4, 1, 0x04, 0x00000000 },
+ { 0x00131c, 4, 0x04, 0x00000000 },
+ { 0x0019c0, 1, 0x04, 0x00000000 },
+ { 0x001140, 1, 0x04, 0x00000000 },
+ { 0x0019c4, 1, 0x04, 0x00000000 },
+ { 0x0019c8, 1, 0x04, 0x00001500 },
+ { 0x00135c, 1, 0x04, 0x00000000 },
+ { 0x000f90, 1, 0x04, 0x00000000 },
+ { 0x0019e0, 8, 0x04, 0x00000001 },
+ { 0x0019cc, 1, 0x04, 0x00000001 },
+ { 0x0015b8, 1, 0x04, 0x00000000 },
+ { 0x001a00, 1, 0x04, 0x00001111 },
+ { 0x001a04, 7, 0x04, 0x00000000 },
+ { 0x000d6c, 2, 0x04, 0xffff0000 },
+ { 0x0010f8, 1, 0x04, 0x00001010 },
+ { 0x000d80, 5, 0x04, 0x00000000 },
+ { 0x000da0, 1, 0x04, 0x00000000 },
+ { 0x0007a4, 2, 0x04, 0x00000000 },
+ { 0x001508, 1, 0x04, 0x80000000 },
+ { 0x00150c, 1, 0x04, 0x40000000 },
+ { 0x001668, 1, 0x04, 0x00000000 },
+ { 0x000318, 2, 0x04, 0x00000008 },
+ { 0x000d9c, 1, 0x04, 0x00000001 },
+ { 0x000ddc, 1, 0x04, 0x00000002 },
+ { 0x000374, 1, 0x04, 0x00000000 },
+ { 0x000378, 1, 0x04, 0x00000020 },
+ { 0x0007dc, 1, 0x04, 0x00000000 },
+ { 0x00074c, 1, 0x04, 0x00000055 },
+ { 0x001420, 1, 0x04, 0x00000003 },
+ { 0x0017bc, 2, 0x04, 0x00000000 },
+ { 0x0017c4, 1, 0x04, 0x00000001 },
+ { 0x001008, 1, 0x04, 0x00000008 },
+ { 0x00100c, 1, 0x04, 0x00000040 },
+ { 0x001010, 1, 0x04, 0x0000012c },
+ { 0x000d60, 1, 0x04, 0x00000040 },
+ { 0x00075c, 1, 0x04, 0x00000003 },
+ { 0x001018, 1, 0x04, 0x00000020 },
+ { 0x00101c, 1, 0x04, 0x00000001 },
+ { 0x001020, 1, 0x04, 0x00000020 },
+ { 0x001024, 1, 0x04, 0x00000001 },
+ { 0x001444, 3, 0x04, 0x00000000 },
+ { 0x000360, 1, 0x04, 0x20164010 },
+ { 0x000364, 1, 0x04, 0x00000020 },
+ { 0x000368, 1, 0x04, 0x00000000 },
+ { 0x000de4, 1, 0x04, 0x00000000 },
+ { 0x000204, 1, 0x04, 0x00000006 },
+ { 0x000208, 1, 0x04, 0x00000000 },
+ { 0x0002cc, 2, 0x04, 0x003fffff },
+ { 0x001220, 1, 0x04, 0x00000005 },
+ { 0x000fdc, 1, 0x04, 0x00000000 },
+ { 0x000f98, 1, 0x04, 0x00400008 },
+ { 0x001284, 1, 0x04, 0x08000080 },
+ { 0x001450, 1, 0x04, 0x00400008 },
+ { 0x001454, 1, 0x04, 0x08000080 },
+ { 0x000214, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_pack
+nvf0_grctx_pack_mthd[] = {
+ { nvf0_grctx_init_a197_0, 0xa197 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_fe_0[] = {
{ 0x404004, 8, 0x04, 0x00000000 },
{ 0x404024, 1, 0x04, 0x0000e000 },
{ 0x404028, 8, 0x04, 0x00000000 },
@@ -50,8 +620,8 @@ nvf0_grctx_init_unk40xx[] = {
{}
};
-struct nvc0_graph_init
-nvf0_grctx_init_unk44xx[] = {
+const struct nvc0_graph_init
+nvf0_grctx_init_pri_0[] = {
{ 0x404404, 12, 0x04, 0x00000000 },
{ 0x404438, 1, 0x04, 0x00000000 },
{ 0x404460, 2, 0x04, 0x00000000 },
@@ -62,23 +632,18 @@ nvf0_grctx_init_unk44xx[] = {
{}
};
-struct nvc0_graph_init
-nvf0_grctx_init_unk5bxx[] = {
+const struct nvc0_graph_init
+nvf0_grctx_init_cwd_0[] = {
{ 0x405b00, 1, 0x04, 0x00000000 },
{ 0x405b10, 1, 0x04, 0x00001000 },
{ 0x405b20, 1, 0x04, 0x04000000 },
{}
};
-struct nvc0_graph_init
-nvf0_grctx_init_unk60xx[] = {
+static const struct nvc0_graph_init
+nvf0_grctx_init_pd_0[] = {
{ 0x406020, 1, 0x04, 0x034103c1 },
{ 0x406028, 4, 0x04, 0x00000001 },
- {}
-};
-
-static struct nvc0_graph_init
-nvf0_grctx_init_unk64xx[] = {
{ 0x4064a8, 1, 0x04, 0x00000000 },
{ 0x4064ac, 1, 0x04, 0x00003fff },
{ 0x4064b0, 3, 0x04, 0x00000000 },
@@ -90,8 +655,8 @@ nvf0_grctx_init_unk64xx[] = {
{}
};
-static struct nvc0_graph_init
-nvf0_grctx_init_unk88xx[] = {
+static const struct nvc0_graph_init
+nvf0_grctx_init_be_0[] = {
{ 0x408800, 1, 0x04, 0x12802a3c },
{ 0x408804, 1, 0x04, 0x00000040 },
{ 0x408808, 1, 0x04, 0x1003e005 },
@@ -103,22 +668,23 @@ nvf0_grctx_init_unk88xx[] = {
{}
};
-static struct nvc0_graph_init
-nvf0_grctx_init_gpc_0[] = {
- { 0x418380, 1, 0x04, 0x00000016 },
- { 0x418400, 1, 0x04, 0x38004e00 },
- { 0x418404, 1, 0x04, 0x71e0ffff },
- { 0x41840c, 1, 0x04, 0x00001008 },
- { 0x418410, 1, 0x04, 0x0fff0fff },
- { 0x418414, 1, 0x04, 0x02200fff },
- { 0x418450, 6, 0x04, 0x00000000 },
- { 0x418468, 1, 0x04, 0x00000001 },
- { 0x41846c, 2, 0x04, 0x00000000 },
- { 0x418600, 1, 0x04, 0x0000001f },
- { 0x418684, 1, 0x04, 0x0000000f },
- { 0x418700, 1, 0x04, 0x00000002 },
- { 0x418704, 1, 0x04, 0x00000080 },
- { 0x418708, 3, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nvf0_grctx_pack_hub[] = {
+ { nvc0_grctx_init_main_0 },
+ { nvf0_grctx_init_fe_0 },
+ { nvf0_grctx_init_pri_0 },
+ { nve4_grctx_init_memfmt_0 },
+ { nve4_grctx_init_ds_0 },
+ { nvf0_grctx_init_cwd_0 },
+ { nvf0_grctx_init_pd_0 },
+ { nvc0_grctx_init_rstr2d_0 },
+ { nve4_grctx_init_scc_0 },
+ { nvf0_grctx_init_be_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_setup_0[] = {
{ 0x418800, 1, 0x04, 0x7006860a },
{ 0x418808, 1, 0x04, 0x00000000 },
{ 0x41880c, 1, 0x04, 0x00000030 },
@@ -129,36 +695,31 @@ nvf0_grctx_init_gpc_0[] = {
{ 0x4188e0, 1, 0x04, 0x01000000 },
{ 0x4188e8, 5, 0x04, 0x00000000 },
{ 0x4188fc, 1, 0x04, 0x20100018 },
- { 0x41891c, 1, 0x04, 0x00ff00ff },
- { 0x418924, 1, 0x04, 0x00000000 },
- { 0x418928, 1, 0x04, 0x00ffff00 },
- { 0x41892c, 1, 0x04, 0x0000ff00 },
- { 0x418b00, 1, 0x04, 0x00000006 },
- { 0x418b08, 1, 0x04, 0x0a418820 },
- { 0x418b0c, 1, 0x04, 0x062080e6 },
- { 0x418b10, 1, 0x04, 0x020398a4 },
- { 0x418b14, 1, 0x04, 0x0e629062 },
- { 0x418b18, 1, 0x04, 0x0a418820 },
- { 0x418b1c, 1, 0x04, 0x000000e6 },
- { 0x418bb8, 1, 0x04, 0x00000103 },
- { 0x418c08, 1, 0x04, 0x00000001 },
- { 0x418c10, 8, 0x04, 0x00000000 },
- { 0x418c40, 1, 0x04, 0xffffffff },
- { 0x418c6c, 1, 0x04, 0x00000001 },
- { 0x418c80, 1, 0x04, 0x20200004 },
- { 0x418c8c, 1, 0x04, 0x00000001 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvf0_grctx_init_gpc_unk_2[] = {
{ 0x418d24, 1, 0x04, 0x00000000 },
- { 0x419000, 1, 0x04, 0x00000780 },
- { 0x419004, 2, 0x04, 0x00000000 },
- { 0x419014, 1, 0x04, 0x00000004 },
{}
};
-static struct nvc0_graph_init
-nvf0_grctx_init_tpc[] = {
- { 0x419848, 1, 0x04, 0x00000000 },
- { 0x419864, 1, 0x04, 0x00000129 },
- { 0x419888, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_pack
+nvf0_grctx_pack_gpc[] = {
+ { nvc0_grctx_init_gpc_unk_0 },
+ { nvd9_grctx_init_prop_0 },
+ { nvd9_grctx_init_gpc_unk_1 },
+ { nvf0_grctx_init_setup_0 },
+ { nvc0_grctx_init_zcull_0 },
+ { nvd9_grctx_init_crstr_0 },
+ { nve4_grctx_init_gpm_0 },
+ { nvf0_grctx_init_gpc_unk_2 },
+ { nvc0_grctx_init_gcc_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000000f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
{ 0x419a08, 1, 0x04, 0x00000021 },
@@ -169,14 +730,29 @@ nvf0_grctx_init_tpc[] = {
{ 0x419a20, 1, 0x04, 0x00020800 },
{ 0x419a30, 1, 0x04, 0x00000001 },
{ 0x419ac4, 1, 0x04, 0x0037f440 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvf0_grctx_init_mpc_0[] = {
{ 0x419c00, 1, 0x04, 0x0000001a },
{ 0x419c04, 1, 0x04, 0x80000006 },
{ 0x419c08, 1, 0x04, 0x00000002 },
{ 0x419c20, 1, 0x04, 0x00000000 },
{ 0x419c24, 1, 0x04, 0x00084210 },
{ 0x419c28, 1, 0x04, 0x3efbefbe },
+ {}
+};
+
+const struct nvc0_graph_init
+nvf0_grctx_init_l1c_0[] = {
{ 0x419ce8, 1, 0x04, 0x00000000 },
{ 0x419cf4, 1, 0x04, 0x00000203 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_sm_0[] = {
{ 0x419e04, 1, 0x04, 0x00000000 },
{ 0x419e08, 1, 0x04, 0x0000001d },
{ 0x419e0c, 1, 0x04, 0x00000000 },
@@ -189,8 +765,8 @@ nvf0_grctx_init_tpc[] = {
{ 0x419e5c, 3, 0x04, 0x00000000 },
{ 0x419e68, 1, 0x04, 0x00000002 },
{ 0x419e6c, 12, 0x04, 0x00000000 },
- { 0x419eac, 1, 0x04, 0x00001fcf },
- { 0x419eb0, 1, 0x04, 0x0db00da0 },
+ { 0x419eac, 1, 0x04, 0x00001f8f },
+ { 0x419eb0, 1, 0x04, 0x0db00d2f },
{ 0x419eb8, 1, 0x04, 0x00000000 },
{ 0x419ec8, 1, 0x04, 0x0001304f },
{ 0x419f30, 4, 0x04, 0x00000000 },
@@ -203,24 +779,36 @@ nvf0_grctx_init_tpc[] = {
{}
};
-static struct nvc0_graph_init
-nvf0_grctx_init_unk[] = {
- { 0x41be24, 1, 0x04, 0x00000006 },
+static const struct nvc0_graph_pack
+nvf0_grctx_pack_tpc[] = {
+ { nvd7_grctx_init_pe_0 },
+ { nvf0_grctx_init_tex_0 },
+ { nvf0_grctx_init_mpc_0 },
+ { nvf0_grctx_init_l1c_0 },
+ { nvf0_grctx_init_sm_0 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_grctx_init_cbm_0[] = {
{ 0x41bec0, 1, 0x04, 0x10000000 },
{ 0x41bec4, 1, 0x04, 0x00037f7f },
{ 0x41bee4, 1, 0x04, 0x00000000 },
- { 0x41bf00, 1, 0x04, 0x0a418820 },
- { 0x41bf04, 1, 0x04, 0x062080e6 },
- { 0x41bf08, 1, 0x04, 0x020398a4 },
- { 0x41bf0c, 1, 0x04, 0x0e629062 },
- { 0x41bf10, 1, 0x04, 0x0a418820 },
- { 0x41bf14, 1, 0x04, 0x000000e6 },
- { 0x41bfd0, 1, 0x04, 0x00900103 },
- { 0x41bfe0, 1, 0x04, 0x00400001 },
- { 0x41bfe4, 1, 0x04, 0x00000000 },
{}
};
+static const struct nvc0_graph_pack
+nvf0_grctx_pack_ppc[] = {
+ { nve4_grctx_init_pes_0 },
+ { nvf0_grctx_init_cbm_0 },
+ { nvd7_grctx_init_wwdx_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
static void
nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
@@ -273,39 +861,6 @@ nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
mmio_list(0x17e920, 0x00090a05, 0, 0);
}
-static struct nvc0_graph_init *
-nvf0_grctx_init_hub[] = {
- nvc0_grctx_init_base,
- nvf0_grctx_init_unk40xx,
- nvf0_grctx_init_unk44xx,
- nve4_grctx_init_unk46xx,
- nve4_grctx_init_unk47xx,
- nve4_grctx_init_unk58xx,
- nvf0_grctx_init_unk5bxx,
- nvf0_grctx_init_unk60xx,
- nvf0_grctx_init_unk64xx,
- nve4_grctx_init_unk80xx,
- nvf0_grctx_init_unk88xx,
- NULL
-};
-
-struct nvc0_graph_init *
-nvf0_grctx_init_gpc[] = {
- nvf0_grctx_init_gpc_0,
- nvc0_grctx_init_gpc_1,
- nvf0_grctx_init_tpc,
- nvf0_grctx_init_unk,
- NULL
-};
-
-static struct nvc0_graph_mthd
-nvf0_grctx_init_mthd[] = {
- { 0xa197, nvc1_grctx_init_9097, },
- { 0x902d, nvc0_grctx_init_902d, },
- { 0x902d, nvc0_grctx_init_mthd_magic, },
- {}
-};
-
struct nouveau_oclass *
nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xf0),
@@ -317,11 +872,14 @@ nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.rd32 = _nouveau_graph_context_rd32,
.wr32 = _nouveau_graph_context_wr32,
},
- .main = nve4_grctx_generate_main,
- .mods = nvf0_grctx_generate_mods,
- .unkn = nve4_grctx_generate_unkn,
- .hub = nvf0_grctx_init_hub,
- .gpc = nvf0_grctx_init_gpc,
- .icmd = nvc0_grctx_init_icmd,
- .mthd = nvf0_grctx_init_mthd,
+ .main = nve4_grctx_generate_main,
+ .mods = nvf0_grctx_generate_mods,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = nvf0_grctx_pack_hub,
+ .gpc = nvf0_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nvf0_grctx_pack_tpc,
+ .ppc = nvf0_grctx_pack_ppc,
+ .icmd = nvf0_grctx_pack_icmd,
+ .mthd = nvf0_grctx_pack_mthd,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc
index e148961b8075..e37d8106ae1a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc
@@ -228,7 +228,7 @@ mmctx_xfer:
and $r11 0x1f
cmpu b32 $r11 0x10
bra ne #mmctx_fini_wait
- mov $r10 2 // DONE_MMCTX
+ mov $r10 5 // DONE_MMCTX
call(wait_donez)
bra #mmctx_done
mmctx_stop:
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc
index 96cbcea3b2c9..2f7345f7fe07 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc
@@ -78,7 +78,12 @@ error:
//
init:
clear b32 $r0
- mov $sp $r0
+
+ // setup stack
+ nv_iord($r1, NV_PGRAPH_GPCX_GPCCS_CAPS, 0)
+ extr $r1 $r1 9:17
+ shl b32 $r1 8
+ mov $sp $r1
// enable fifo access
mov $r2 NV_PGRAPH_GPCX_GPCCS_ACCESS_FIFO
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5 b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5
new file mode 100644
index 000000000000..e730603891d7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000002
+
+#define CHIPSET GK208
+#include "macros.fuc"
+
+.section #gm107_grgpc_data
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "gpc.fuc"
+#undef INCLUDE_DATA
+
+.section #gm107_grgpc_code
+#define INCLUDE_CODE
+bra #init
+#include "com.fuc"
+#include "gpc.fuc"
+.align 256
+#undef INCLUDE_CODE
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5.h
new file mode 100644
index 000000000000..6d53b67dd3c4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcgm107.fuc5.h
@@ -0,0 +1,473 @@
+uint32_t gm107_grgpc_data[] = {
+/* 0x0000: gpc_mmio_list_head */
+ 0x0000006c,
+/* 0x0004: gpc_mmio_list_tail */
+/* 0x0004: tpc_mmio_list_head */
+ 0x0000006c,
+/* 0x0008: tpc_mmio_list_tail */
+/* 0x0008: unk_mmio_list_head */
+ 0x0000006c,
+/* 0x000c: unk_mmio_list_tail */
+ 0x0000006c,
+/* 0x0010: gpc_id */
+ 0x00000000,
+/* 0x0014: tpc_count */
+ 0x00000000,
+/* 0x0018: tpc_mask */
+ 0x00000000,
+/* 0x001c: unk_count */
+ 0x00000000,
+/* 0x0020: unk_mask */
+ 0x00000000,
+/* 0x0024: cmd_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
+
+uint32_t gm107_grgpc_code[] = {
+ 0x03140ef5,
+/* 0x0004: queue_put */
+ 0x9800d898,
+ 0x86f001d9,
+ 0xf489a408,
+ 0x020f0b1b,
+ 0x0002f87e,
+/* 0x001a: queue_put_next */
+ 0x98c400f8,
+ 0x0384b607,
+ 0xb6008dbb,
+ 0x8eb50880,
+ 0x018fb500,
+ 0xf00190b6,
+ 0xd9b50f94,
+/* 0x0037: queue_get */
+ 0xf400f801,
+ 0xd8980131,
+ 0x01d99800,
+ 0x0bf489a4,
+ 0x0789c421,
+ 0xbb0394b6,
+ 0x90b6009d,
+ 0x009e9808,
+ 0xb6019f98,
+ 0x84f00180,
+ 0x00d8b50f,
+/* 0x0063: queue_get_done */
+ 0xf80132f4,
+/* 0x0065: nv_rd32 */
+ 0xf0ecb200,
+ 0x00801fc9,
+ 0x0cf601ca,
+/* 0x0073: nv_rd32_wait */
+ 0x8c04bd00,
+ 0xcf01ca00,
+ 0xccc800cc,
+ 0xf61bf41f,
+ 0xec7e060a,
+ 0x008f0000,
+ 0xffcf01cb,
+/* 0x008f: nv_wr32 */
+ 0x8000f800,
+ 0xf601cc00,
+ 0x04bd000f,
+ 0xc9f0ecb2,
+ 0x1ec9f01f,
+ 0x01ca0080,
+ 0xbd000cf6,
+/* 0x00a9: nv_wr32_wait */
+ 0xca008c04,
+ 0x00cccf01,
+ 0xf41fccc8,
+ 0x00f8f61b,
+/* 0x00b8: wait_donez */
+ 0x99f094bd,
+ 0x37008000,
+ 0x0009f602,
+ 0x008004bd,
+ 0x0af60206,
+/* 0x00cf: wait_donez_ne */
+ 0x8804bd00,
+ 0xcf010000,
+ 0x8aff0088,
+ 0xf61bf488,
+ 0x99f094bd,
+ 0x17008000,
+ 0x0009f602,
+ 0x00f804bd,
+/* 0x00ec: wait_doneo */
+ 0x99f094bd,
+ 0x37008000,
+ 0x0009f602,
+ 0x008004bd,
+ 0x0af60206,
+/* 0x0103: wait_doneo_e */
+ 0x8804bd00,
+ 0xcf010000,
+ 0x8aff0088,
+ 0xf60bf488,
+ 0x99f094bd,
+ 0x17008000,
+ 0x0009f602,
+ 0x00f804bd,
+/* 0x0120: mmctx_size */
+/* 0x0122: nv_mmctx_size_loop */
+ 0xe89894bd,
+ 0x1a85b600,
+ 0xb60180b6,
+ 0x98bb0284,
+ 0x04e0b600,
+ 0x1bf4efa4,
+ 0xf89fb2ec,
+/* 0x013d: mmctx_xfer */
+ 0xf094bd00,
+ 0x00800199,
+ 0x09f60237,
+ 0xbd04bd00,
+ 0x05bbfd94,
+ 0x800f0bf4,
+ 0xf601c400,
+ 0x04bd000b,
+/* 0x015f: mmctx_base_disabled */
+ 0xfd0099f0,
+ 0x0bf405ee,
+ 0xc6008018,
+ 0x000ef601,
+ 0x008004bd,
+ 0x0ff601c7,
+ 0xf004bd00,
+/* 0x017a: mmctx_multi_disabled */
+ 0xabc80199,
+ 0x10b4b600,
+ 0xc80cb9f0,
+ 0xe4b601ae,
+ 0x05befd11,
+ 0x01c50080,
+ 0xbd000bf6,
+/* 0x0195: mmctx_exec_loop */
+/* 0x0195: mmctx_wait_free */
+ 0xc5008e04,
+ 0x00eecf01,
+ 0xf41fe4f0,
+ 0xce98f60b,
+ 0x05e9fd00,
+ 0x01c80080,
+ 0xbd000ef6,
+ 0x04c0b604,
+ 0x1bf4cda4,
+ 0x02abc8df,
+/* 0x01bf: mmctx_fini_wait */
+ 0x8b1c1bf4,
+ 0xcf01c500,
+ 0xb4f000bb,
+ 0x10b4b01f,
+ 0x0af31bf4,
+ 0x00b87e05,
+ 0x250ef400,
+/* 0x01d8: mmctx_stop */
+ 0xb600abc8,
+ 0xb9f010b4,
+ 0x12b9f00c,
+ 0x01c50080,
+ 0xbd000bf6,
+/* 0x01ed: mmctx_stop_wait */
+ 0xc5008b04,
+ 0x00bbcf01,
+ 0xf412bbc8,
+/* 0x01fa: mmctx_done */
+ 0x94bdf61b,
+ 0x800199f0,
+ 0xf6021700,
+ 0x04bd0009,
+/* 0x020a: strand_wait */
+ 0xa0f900f8,
+ 0xb87e020a,
+ 0xa0fc0000,
+/* 0x0216: strand_pre */
+ 0x0c0900f8,
+ 0x024afc80,
+ 0xbd0009f6,
+ 0x020a7e04,
+/* 0x0227: strand_post */
+ 0x0900f800,
+ 0x4afc800d,
+ 0x0009f602,
+ 0x0a7e04bd,
+ 0x00f80002,
+/* 0x0238: strand_set */
+ 0xfc800f0c,
+ 0x0cf6024f,
+ 0x0c04bd00,
+ 0x4afc800b,
+ 0x000cf602,
+ 0xfc8004bd,
+ 0x0ef6024f,
+ 0x0c04bd00,
+ 0x4afc800a,
+ 0x000cf602,
+ 0x0a7e04bd,
+ 0x00f80002,
+/* 0x0268: strand_ctx_init */
+ 0x99f094bd,
+ 0x37008003,
+ 0x0009f602,
+ 0x167e04bd,
+ 0x030e0002,
+ 0x0002387e,
+ 0xfc80c4bd,
+ 0x0cf60247,
+ 0x0c04bd00,
+ 0x4afc8001,
+ 0x000cf602,
+ 0x0a7e04bd,
+ 0x0c920002,
+ 0x46fc8001,
+ 0x000cf602,
+ 0x020c04bd,
+ 0x024afc80,
+ 0xbd000cf6,
+ 0x020a7e04,
+ 0x02277e00,
+ 0x42008800,
+ 0x20008902,
+ 0x0099cf02,
+/* 0x02c7: ctx_init_strand_loop */
+ 0xf608fe95,
+ 0x8ef6008e,
+ 0x808acf40,
+ 0xb606a5b6,
+ 0xeabb01a0,
+ 0x0480b600,
+ 0xf40192b6,
+ 0xe4b6e81b,
+ 0xf2efbc08,
+ 0x99f094bd,
+ 0x17008003,
+ 0x0009f602,
+ 0x00f804bd,
+/* 0x02f8: error */
+ 0xffb2e0f9,
+ 0x4098148e,
+ 0x00008f7e,
+ 0xffb2010f,
+ 0x409c1c8e,
+ 0x00008f7e,
+ 0x00f8e0fc,
+/* 0x0314: init */
+ 0x004104bd,
+ 0x0011cf42,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x02020014,
+ 0xf6120040,
+ 0x04bd0002,
+ 0xfe047241,
+ 0x00400010,
+ 0x0000f607,
+ 0x040204bd,
+ 0xf6040040,
+ 0x04bd0002,
+ 0x821031f4,
+ 0xcf018200,
+ 0x01030022,
+ 0xbb1f24f0,
+ 0x32b60432,
+ 0x0502b501,
+ 0x820603b5,
+ 0xcf018600,
+ 0x02b50022,
+ 0x0c308e04,
+ 0xbd24bd50,
+/* 0x0377: init_unk_loop */
+ 0x7e44bd34,
+ 0xb0000065,
+ 0x0bf400f6,
+ 0xbb010f0e,
+ 0x4ffd04f2,
+ 0x0130b605,
+/* 0x038c: init_unk_next */
+ 0xb60120b6,
+ 0x26b004e0,
+ 0xe21bf402,
+/* 0x0398: init_unk_done */
+ 0xb50703b5,
+ 0x00820804,
+ 0x22cf0201,
+ 0x9534bd00,
+ 0x00800825,
+ 0x05f601c0,
+ 0x8004bd00,
+ 0xf601c100,
+ 0x04bd0005,
+ 0x98000e98,
+ 0x207e010f,
+ 0x2fbb0001,
+ 0x003fbb00,
+ 0x98010e98,
+ 0x207e020f,
+ 0x0e980001,
+ 0x00effd05,
+ 0xbb002ebb,
+ 0x0e98003e,
+ 0x030f9802,
+ 0x0001207e,
+ 0xfd070e98,
+ 0x2ebb00ef,
+ 0x003ebb00,
+ 0x800235b6,
+ 0xf601d300,
+ 0x04bd0003,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb20834,
+ 0x0002687e,
+ 0x80003fbb,
+ 0xf6020100,
+ 0x04bd0003,
+ 0x29f024bd,
+ 0x3000801f,
+ 0x0002f602,
+/* 0x0436: main */
+ 0x31f404bd,
+ 0x0028f400,
+ 0x377e240d,
+ 0x01f40000,
+ 0x04e4b0f4,
+ 0xfe1d18f4,
+ 0x06020181,
+ 0x12fd20bd,
+ 0x01e4b604,
+ 0xfe051efd,
+ 0x097e0018,
+ 0x0ef40005,
+/* 0x0465: main_not_ctx_xfer */
+ 0x10ef94d4,
+ 0x7e01f5f0,
+ 0xf40002f8,
+/* 0x0472: ih */
+ 0x80f9c70e,
+ 0xf90188fe,
+ 0xf990f980,
+ 0xf9b0f9a0,
+ 0xf9e0f9d0,
+ 0x4a04bdf0,
+ 0xaacf0200,
+ 0x04abc400,
+ 0x0d1f0bf4,
+ 0x1a004e24,
+ 0x4f00eecf,
+ 0xffcf1900,
+ 0x00047e00,
+ 0x40010e00,
+ 0x0ef61d00,
+/* 0x04af: ih_no_fifo */
+ 0x4004bd00,
+ 0x0af60100,
+ 0xfc04bd00,
+ 0xfce0fcf0,
+ 0xfcb0fcd0,
+ 0xfc90fca0,
+ 0x0088fe80,
+ 0x32f480fc,
+/* 0x04cf: hub_barrier_done */
+ 0x0f01f800,
+ 0x040e9801,
+ 0xb204febb,
+ 0x94188eff,
+ 0x008f7e40,
+/* 0x04e3: ctx_redswitch */
+ 0x0f00f800,
+ 0x85008020,
+ 0x000ff601,
+ 0x080e04bd,
+/* 0x04f0: ctx_redswitch_delay */
+ 0xf401e2b6,
+ 0xf5f1fd1b,
+ 0xf5f10800,
+ 0x00800200,
+ 0x0ff60185,
+ 0xf804bd00,
+/* 0x0509: ctx_xfer */
+ 0x81008000,
+ 0x000ff602,
+ 0x11f404bd,
+ 0x04e37e07,
+/* 0x0519: ctx_xfer_not_load */
+ 0x02167e00,
+ 0x8024bd00,
+ 0xf60247fc,
+ 0x04bd0002,
+ 0xb6012cf0,
+ 0xfc800320,
+ 0x02f6024a,
+ 0xf004bd00,
+ 0xa5f001ac,
+ 0x00008b02,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x010d9800,
+ 0x3d7e000e,
+ 0xacf00001,
+ 0x40008b01,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x020d9801,
+ 0x4e060f98,
+ 0x3d7e0800,
+ 0xacf00001,
+ 0x04a5f001,
+ 0x5030008b,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x020c9800,
+ 0x98030d98,
+ 0x004e080f,
+ 0x013d7e02,
+ 0x020a7e00,
+ 0x0601f400,
+/* 0x05a3: ctx_xfer_post */
+ 0x7e0712f4,
+/* 0x05a7: ctx_xfer_done */
+ 0x7e000227,
+ 0xf80004cf,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnv108.fuc5.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnv108.fuc5.h
index 27dc1280dc10..31922707794f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnv108.fuc5.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnv108.fuc5.h
@@ -177,7 +177,7 @@ uint32_t nv108_grgpc_code[] = {
0xb4f000bb,
0x10b4b01f,
0x0af31bf4,
- 0x00b87e02,
+ 0x00b87e05,
0x250ef400,
/* 0x01d8: mmctx_stop */
0xb600abc8,
@@ -269,186 +269,186 @@ uint32_t nv108_grgpc_code[] = {
0x00008f7e,
0x00f8e0fc,
/* 0x0314: init */
- 0x04fe04bd,
- 0x40020200,
- 0x02f61200,
- 0x4104bd00,
- 0x10fe0465,
- 0x07004000,
- 0xbd0000f6,
- 0x40040204,
- 0x02f60400,
- 0xf404bd00,
- 0x00821031,
- 0x22cf0182,
- 0xf0010300,
- 0x32bb1f24,
- 0x0132b604,
- 0xb50502b5,
- 0x00820603,
- 0x22cf0186,
- 0x0402b500,
- 0x500c308e,
- 0x34bd24bd,
-/* 0x036a: init_unk_loop */
- 0x657e44bd,
- 0xf6b00000,
- 0x0e0bf400,
- 0xf2bb010f,
- 0x054ffd04,
-/* 0x037f: init_unk_next */
- 0xb60130b6,
- 0xe0b60120,
- 0x0126b004,
-/* 0x038b: init_unk_done */
- 0xb5e21bf4,
- 0x04b50703,
- 0x01008208,
- 0x0022cf02,
- 0x259534bd,
- 0xc0008008,
- 0x0005f601,
- 0x008004bd,
- 0x05f601c1,
- 0x9804bd00,
- 0x0f98000e,
- 0x01207e01,
- 0x002fbb00,
- 0x98003fbb,
- 0x0f98010e,
- 0x01207e02,
- 0x050e9800,
- 0xbb00effd,
- 0x3ebb002e,
- 0x020e9800,
- 0x7e030f98,
- 0x98000120,
- 0xeffd070e,
- 0x002ebb00,
- 0xb6003ebb,
- 0x00800235,
- 0x03f601d3,
- 0xb604bd00,
- 0x35b60825,
- 0x0120b606,
- 0xb60130b6,
- 0x34b60824,
- 0x7e2fb208,
- 0xbb000268,
- 0x0080003f,
- 0x03f60201,
- 0xbd04bd00,
- 0x1f29f024,
- 0x02300080,
- 0xbd0002f6,
-/* 0x0429: main */
- 0x0031f404,
- 0x0d0028f4,
- 0x00377e24,
- 0xf401f400,
- 0xf404e4b0,
- 0x81fe1d18,
- 0xbd060201,
- 0x0412fd20,
- 0xfd01e4b6,
- 0x18fe051e,
- 0x04fc7e00,
- 0xd40ef400,
-/* 0x0458: main_not_ctx_xfer */
- 0xf010ef94,
- 0xf87e01f5,
- 0x0ef40002,
-/* 0x0465: ih */
- 0xfe80f9c7,
- 0x80f90188,
- 0xa0f990f9,
- 0xd0f9b0f9,
- 0xf0f9e0f9,
- 0x004a04bd,
- 0x00aacf02,
- 0xf404abc4,
- 0x240d1f0b,
- 0xcf1a004e,
- 0x004f00ee,
- 0x00ffcf19,
- 0x0000047e,
- 0x0040010e,
- 0x000ef61d,
-/* 0x04a2: ih_no_fifo */
- 0x004004bd,
- 0x000af601,
- 0xf0fc04bd,
- 0xd0fce0fc,
- 0xa0fcb0fc,
- 0x80fc90fc,
- 0xfc0088fe,
- 0x0032f480,
-/* 0x04c2: hub_barrier_done */
- 0x010f01f8,
- 0xbb040e98,
- 0xffb204fe,
- 0x4094188e,
- 0x00008f7e,
-/* 0x04d6: ctx_redswitch */
- 0x200f00f8,
- 0x01850080,
- 0xbd000ff6,
-/* 0x04e3: ctx_redswitch_delay */
- 0xb6080e04,
- 0x1bf401e2,
- 0x00f5f1fd,
- 0x00f5f108,
- 0x85008002,
+ 0x004104bd,
+ 0x0011cf42,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x02020014,
+ 0xf6120040,
+ 0x04bd0002,
+ 0xfe047241,
+ 0x00400010,
+ 0x0000f607,
+ 0x040204bd,
+ 0xf6040040,
+ 0x04bd0002,
+ 0x821031f4,
+ 0xcf018200,
+ 0x01030022,
+ 0xbb1f24f0,
+ 0x32b60432,
+ 0x0502b501,
+ 0x820603b5,
+ 0xcf018600,
+ 0x02b50022,
+ 0x0c308e04,
+ 0xbd24bd50,
+/* 0x0377: init_unk_loop */
+ 0x7e44bd34,
+ 0xb0000065,
+ 0x0bf400f6,
+ 0xbb010f0e,
+ 0x4ffd04f2,
+ 0x0130b605,
+/* 0x038c: init_unk_next */
+ 0xb60120b6,
+ 0x26b004e0,
+ 0xe21bf401,
+/* 0x0398: init_unk_done */
+ 0xb50703b5,
+ 0x00820804,
+ 0x22cf0201,
+ 0x9534bd00,
+ 0x00800825,
+ 0x05f601c0,
+ 0x8004bd00,
+ 0xf601c100,
+ 0x04bd0005,
+ 0x98000e98,
+ 0x207e010f,
+ 0x2fbb0001,
+ 0x003fbb00,
+ 0x98010e98,
+ 0x207e020f,
+ 0x0e980001,
+ 0x00effd05,
+ 0xbb002ebb,
+ 0x0e98003e,
+ 0x030f9802,
+ 0x0001207e,
+ 0xfd070e98,
+ 0x2ebb00ef,
+ 0x003ebb00,
+ 0x800235b6,
+ 0xf601d300,
+ 0x04bd0003,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb20834,
+ 0x0002687e,
+ 0x80003fbb,
+ 0xf6020100,
+ 0x04bd0003,
+ 0x29f024bd,
+ 0x3000801f,
+ 0x0002f602,
+/* 0x0436: main */
+ 0x31f404bd,
+ 0x0028f400,
+ 0x377e240d,
+ 0x01f40000,
+ 0x04e4b0f4,
+ 0xfe1d18f4,
+ 0x06020181,
+ 0x12fd20bd,
+ 0x01e4b604,
+ 0xfe051efd,
+ 0x097e0018,
+ 0x0ef40005,
+/* 0x0465: main_not_ctx_xfer */
+ 0x10ef94d4,
+ 0x7e01f5f0,
+ 0xf40002f8,
+/* 0x0472: ih */
+ 0x80f9c70e,
+ 0xf90188fe,
+ 0xf990f980,
+ 0xf9b0f9a0,
+ 0xf9e0f9d0,
+ 0x4a04bdf0,
+ 0xaacf0200,
+ 0x04abc400,
+ 0x0d1f0bf4,
+ 0x1a004e24,
+ 0x4f00eecf,
+ 0xffcf1900,
+ 0x00047e00,
+ 0x40010e00,
+ 0x0ef61d00,
+/* 0x04af: ih_no_fifo */
+ 0x4004bd00,
+ 0x0af60100,
+ 0xfc04bd00,
+ 0xfce0fcf0,
+ 0xfcb0fcd0,
+ 0xfc90fca0,
+ 0x0088fe80,
+ 0x32f480fc,
+/* 0x04cf: hub_barrier_done */
+ 0x0f01f800,
+ 0x040e9801,
+ 0xb204febb,
+ 0x94188eff,
+ 0x008f7e40,
+/* 0x04e3: ctx_redswitch */
+ 0x0f00f800,
+ 0x85008020,
0x000ff601,
- 0x00f804bd,
-/* 0x04fc: ctx_xfer */
- 0x02810080,
- 0xbd000ff6,
- 0x0711f404,
- 0x0004d67e,
-/* 0x050c: ctx_xfer_not_load */
- 0x0002167e,
- 0xfc8024bd,
- 0x02f60247,
+ 0x080e04bd,
+/* 0x04f0: ctx_redswitch_delay */
+ 0xf401e2b6,
+ 0xf5f1fd1b,
+ 0xf5f10800,
+ 0x00800200,
+ 0x0ff60185,
+ 0xf804bd00,
+/* 0x0509: ctx_xfer */
+ 0x81008000,
+ 0x000ff602,
+ 0x11f404bd,
+ 0x04e37e07,
+/* 0x0519: ctx_xfer_not_load */
+ 0x02167e00,
+ 0x8024bd00,
+ 0xf60247fc,
+ 0x04bd0002,
+ 0xb6012cf0,
+ 0xfc800320,
+ 0x02f6024a,
0xf004bd00,
- 0x20b6012c,
- 0x4afc8003,
- 0x0002f602,
- 0xacf004bd,
- 0x02a5f001,
- 0x5000008b,
+ 0xa5f001ac,
+ 0x00008b02,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x010d9800,
+ 0x3d7e000e,
+ 0xacf00001,
+ 0x40008b01,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x020d9801,
+ 0x4e060f98,
+ 0x3d7e0800,
+ 0xacf00001,
+ 0x04a5f001,
+ 0x5030008b,
0xb6040c98,
0xbcbb0fc4,
- 0x000c9800,
- 0x0e010d98,
- 0x013d7e00,
- 0x01acf000,
- 0x5040008b,
- 0xb6040c98,
- 0xbcbb0fc4,
- 0x010c9800,
- 0x98020d98,
- 0x004e060f,
- 0x013d7e08,
- 0x01acf000,
- 0x8b04a5f0,
- 0x98503000,
- 0xc4b6040c,
- 0x00bcbb0f,
- 0x98020c98,
- 0x0f98030d,
- 0x02004e08,
- 0x00013d7e,
- 0x00020a7e,
- 0xf40601f4,
-/* 0x0596: ctx_xfer_post */
- 0x277e0712,
-/* 0x059a: ctx_xfer_done */
- 0xc27e0002,
- 0x00f80004,
- 0x00000000,
- 0x00000000,
- 0x00000000,
+ 0x020c9800,
+ 0x98030d98,
+ 0x004e080f,
+ 0x013d7e02,
+ 0x020a7e00,
+ 0x0601f400,
+/* 0x05a3: ctx_xfer_post */
+ 0x7e0712f4,
+/* 0x05a7: ctx_xfer_done */
+ 0x7e000227,
+ 0xf80004cf,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h
index 0e7b01efae8d..325cc7b7b2fb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h
@@ -192,7 +192,7 @@ uint32_t nvc0_grgpc_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
@@ -300,182 +300,182 @@ uint32_t nvc0_grgpc_code[] = {
0x21f440e3,
0xf8e0fc9d,
/* 0x03a1: init */
- 0xfe04bd00,
- 0x27f00004,
- 0x0007f102,
- 0x0003f012,
- 0xbd0002d0,
- 0xd517f104,
- 0x0010fe04,
- 0x070007f1,
+ 0xf104bd00,
+ 0xf0420017,
+ 0x11cf0013,
+ 0x0911e700,
+ 0x0814b601,
+ 0xf00014fe,
+ 0x07f10227,
+ 0x03f01200,
+ 0x0002d000,
+ 0x17f104bd,
+ 0x10fe04e6,
+ 0x0007f100,
+ 0x0003f007,
+ 0xbd0000d0,
+ 0x0427f004,
+ 0x040007f1,
0xd00003f0,
- 0x04bd0000,
- 0xf10427f0,
- 0xf0040007,
- 0x02d00003,
- 0xf404bd00,
- 0x27f11031,
- 0x23f08200,
- 0x0022cf01,
- 0xf00137f0,
- 0x32bb1f24,
- 0x0132b604,
- 0x80050280,
- 0x27f10603,
- 0x23f08600,
- 0x0022cf01,
- 0xf1040280,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
- 0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0xf10235b6,
- 0xf0d30007,
- 0x03d00103,
- 0xb604bd00,
- 0x35b60825,
- 0x0120b606,
- 0xb60130b6,
- 0x34b60824,
- 0x022fb908,
- 0x02d321f5,
- 0xf1003fbb,
- 0xf0010007,
- 0x03d00203,
- 0xbd04bd00,
- 0x1f29f024,
- 0x080007f1,
- 0xd00203f0,
0x04bd0002,
-/* 0x0498: main */
- 0xf40031f4,
- 0xd7f00028,
- 0x3921f41c,
- 0xb0f401f4,
- 0x18f404e4,
- 0x0181fe1e,
- 0xbd0627f0,
- 0x0412fd20,
- 0xfd01e4b6,
- 0x18fe051e,
- 0x8d21f500,
- 0xd30ef405,
-/* 0x04c8: main_not_ctx_xfer */
- 0xf010ef94,
- 0x21f501f5,
- 0x0ef4037e,
-/* 0x04d5: ih */
- 0xfe80f9c6,
- 0x80f90188,
- 0xa0f990f9,
- 0xd0f9b0f9,
- 0xf0f9e0f9,
- 0xa7f104bd,
- 0xa3f00200,
- 0x00aacf00,
- 0xf404abc4,
- 0xd7f02c0b,
- 0x00e7f11c,
- 0x00e3f01a,
- 0xf100eecf,
- 0xf01900f7,
- 0xffcf00f3,
- 0x0421f400,
- 0xf101e7f0,
- 0xf01d0007,
- 0x0ed00003,
-/* 0x0523: ih_no_fifo */
+ 0xf11031f4,
+ 0xf0820027,
+ 0x22cf0123,
+ 0x0137f000,
+ 0xbb1f24f0,
+ 0x32b60432,
+ 0x05028001,
+ 0xf1060380,
+ 0xf0860027,
+ 0x22cf0123,
+ 0x04028000,
+ 0x010027f1,
+ 0xcf0223f0,
+ 0x34bd0022,
+ 0xf1082595,
+ 0xf0c00007,
+ 0x05d00103,
0xf104bd00,
- 0xf0010007,
- 0x0ad00003,
- 0xfc04bd00,
- 0xfce0fcf0,
- 0xfcb0fcd0,
- 0xfc90fca0,
- 0x0088fe80,
- 0x32f480fc,
-/* 0x0547: hub_barrier_done */
- 0xf001f800,
- 0x0e9801f7,
- 0x04febb04,
- 0xf102ffb9,
- 0xf09418e7,
- 0x21f440e3,
-/* 0x055f: ctx_redswitch */
- 0xf000f89d,
- 0x07f120f7,
- 0x03f08500,
- 0x000fd001,
- 0xe7f004bd,
-/* 0x0571: ctx_redswitch_delay */
- 0x01e2b608,
- 0xf1fd1bf4,
- 0xf10800f5,
- 0xf10200f5,
+ 0xf0c10007,
+ 0x05d00103,
+ 0x9804bd00,
+ 0x0f98000e,
+ 0x5021f501,
+ 0x002fbb01,
+ 0x98003fbb,
+ 0x0f98010e,
+ 0x5021f502,
+ 0x050e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x0235b600,
+ 0xd30007f1,
+ 0xd00103f0,
+ 0x04bd0003,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb90834,
+ 0xd321f502,
+ 0x003fbb02,
+ 0x010007f1,
+ 0xd00203f0,
+ 0x04bd0003,
+ 0x29f024bd,
+ 0x0007f11f,
+ 0x0203f008,
+ 0xbd0002d0,
+/* 0x04a9: main */
+ 0x0031f404,
+ 0xf00028f4,
+ 0x21f41cd7,
+ 0xf401f439,
+ 0xf404e4b0,
+ 0x81fe1e18,
+ 0x0627f001,
+ 0x12fd20bd,
+ 0x01e4b604,
+ 0xfe051efd,
+ 0x21f50018,
+ 0x0ef4059e,
+/* 0x04d9: main_not_ctx_xfer */
+ 0x10ef94d3,
+ 0xf501f5f0,
+ 0xf4037e21,
+/* 0x04e6: ih */
+ 0x80f9c60e,
+ 0xf90188fe,
+ 0xf990f980,
+ 0xf9b0f9a0,
+ 0xf9e0f9d0,
+ 0xf104bdf0,
+ 0xf00200a7,
+ 0xaacf00a3,
+ 0x04abc400,
+ 0xf02c0bf4,
+ 0xe7f11cd7,
+ 0xe3f01a00,
+ 0x00eecf00,
+ 0x1900f7f1,
+ 0xcf00f3f0,
+ 0x21f400ff,
+ 0x01e7f004,
+ 0x1d0007f1,
+ 0xd00003f0,
+ 0x04bd000e,
+/* 0x0534: ih_no_fifo */
+ 0x010007f1,
+ 0xd00003f0,
+ 0x04bd000a,
+ 0xe0fcf0fc,
+ 0xb0fcd0fc,
+ 0x90fca0fc,
+ 0x88fe80fc,
+ 0xf480fc00,
+ 0x01f80032,
+/* 0x0558: hub_barrier_done */
+ 0x9801f7f0,
+ 0xfebb040e,
+ 0x02ffb904,
+ 0x9418e7f1,
+ 0xf440e3f0,
+ 0x00f89d21,
+/* 0x0570: ctx_redswitch */
+ 0xf120f7f0,
0xf0850007,
0x0fd00103,
- 0xf804bd00,
-/* 0x058d: ctx_xfer */
- 0x0007f100,
- 0x0203f081,
- 0xbd000fd0,
- 0x0711f404,
- 0x055f21f5,
-/* 0x05a0: ctx_xfer_not_load */
- 0x026a21f5,
- 0x07f124bd,
- 0x03f047fc,
- 0x0002d002,
- 0x2cf004bd,
- 0x0320b601,
- 0x4afc07f1,
- 0xd00203f0,
- 0x04bd0002,
+ 0xf004bd00,
+/* 0x0582: ctx_redswitch_delay */
+ 0xe2b608e7,
+ 0xfd1bf401,
+ 0x0800f5f1,
+ 0x0200f5f1,
+ 0x850007f1,
+ 0xd00103f0,
+ 0x04bd000f,
+/* 0x059e: ctx_xfer */
+ 0x07f100f8,
+ 0x03f08100,
+ 0x000fd002,
+ 0x11f404bd,
+ 0x7021f507,
+/* 0x05b1: ctx_xfer_not_load */
+ 0x6a21f505,
+ 0xf124bd02,
+ 0xf047fc07,
+ 0x02d00203,
+ 0xf004bd00,
+ 0x20b6012c,
+ 0xfc07f103,
+ 0x0203f04a,
+ 0xbd0002d0,
+ 0x01acf004,
+ 0xf102a5f0,
+ 0xf00000b7,
+ 0x0c9850b3,
+ 0x0fc4b604,
+ 0x9800bcbb,
+ 0x0d98000c,
+ 0x00e7f001,
+ 0x016f21f5,
0xf001acf0,
- 0xb7f102a5,
- 0xb3f00000,
+ 0xb7f104a5,
+ 0xb3f04000,
0x040c9850,
0xbb0fc4b6,
0x0c9800bc,
- 0x010d9800,
- 0xf500e7f0,
- 0xf0016f21,
- 0xa5f001ac,
- 0x00b7f104,
- 0x50b3f040,
- 0xb6040c98,
- 0xbcbb0fc4,
- 0x010c9800,
- 0x98020d98,
- 0xe7f1060f,
- 0x21f50800,
- 0x21f5016f,
- 0x01f4025e,
- 0x0712f406,
-/* 0x0618: ctx_xfer_post */
- 0x027f21f5,
-/* 0x061c: ctx_xfer_done */
- 0x054721f5,
- 0x000000f8,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
+ 0x020d9801,
+ 0xf1060f98,
+ 0xf50800e7,
+ 0xf5016f21,
+ 0xf4025e21,
+ 0x12f40601,
+/* 0x0629: ctx_xfer_post */
+ 0x7f21f507,
+/* 0x062d: ctx_xfer_done */
+ 0x5821f502,
+ 0x0000f805,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h
index 84dd32db28a0..d1504a4059c6 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h
@@ -196,7 +196,7 @@ uint32_t nvd7_grgpc_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
@@ -304,212 +304,212 @@ uint32_t nvd7_grgpc_code[] = {
0x21f440e3,
0xf8e0fc9d,
/* 0x03a1: init */
- 0xfe04bd00,
- 0x27f00004,
- 0x0007f102,
- 0x0003f012,
- 0xbd0002d0,
- 0x1f17f104,
- 0x0010fe05,
- 0x070007f1,
+ 0xf104bd00,
+ 0xf0420017,
+ 0x11cf0013,
+ 0x0911e700,
+ 0x0814b601,
+ 0xf00014fe,
+ 0x07f10227,
+ 0x03f01200,
+ 0x0002d000,
+ 0x17f104bd,
+ 0x10fe0530,
+ 0x0007f100,
+ 0x0003f007,
+ 0xbd0000d0,
+ 0x0427f004,
+ 0x040007f1,
0xd00003f0,
- 0x04bd0000,
- 0xf10427f0,
- 0xf0040007,
- 0x02d00003,
+ 0x04bd0002,
+ 0xf11031f4,
+ 0xf0820027,
+ 0x22cf0123,
+ 0x0137f000,
+ 0xbb1f24f0,
+ 0x32b60432,
+ 0x05028001,
+ 0xf1060380,
+ 0xf0860027,
+ 0x22cf0123,
+ 0x04028000,
+ 0x0c30e7f1,
+ 0xbd50e3f0,
+ 0xbd34bd24,
+/* 0x0421: init_unk_loop */
+ 0x6821f444,
+ 0xf400f6b0,
+ 0xf7f00f0b,
+ 0x04f2bb01,
+ 0xb6054ffd,
+/* 0x0436: init_unk_next */
+ 0x20b60130,
+ 0x04e0b601,
+ 0xf40126b0,
+/* 0x0442: init_unk_done */
+ 0x0380e21b,
+ 0x08048007,
+ 0x010027f1,
+ 0xcf0223f0,
+ 0x34bd0022,
+ 0xf1082595,
+ 0xf0c00007,
+ 0x05d00103,
+ 0xf104bd00,
+ 0xf0c10007,
+ 0x05d00103,
+ 0x9804bd00,
+ 0x0f98000e,
+ 0x5021f501,
+ 0x002fbb01,
+ 0x98003fbb,
+ 0x0f98010e,
+ 0x5021f502,
+ 0x050e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x020e9800,
+ 0xf5030f98,
+ 0x98015021,
+ 0xeffd070e,
+ 0x002ebb00,
+ 0xb6003ebb,
+ 0x07f10235,
+ 0x03f0d300,
+ 0x0003d001,
+ 0x25b604bd,
+ 0x0635b608,
+ 0xb60120b6,
+ 0x24b60130,
+ 0x0834b608,
+ 0xf5022fb9,
+ 0xbb02d321,
+ 0x07f1003f,
+ 0x03f00100,
+ 0x0003d002,
+ 0x24bd04bd,
+ 0xf11f29f0,
+ 0xf0080007,
+ 0x02d00203,
+/* 0x04f3: main */
0xf404bd00,
- 0x27f11031,
- 0x23f08200,
- 0x0022cf01,
- 0xf00137f0,
- 0x32bb1f24,
- 0x0132b604,
- 0x80050280,
- 0x27f10603,
- 0x23f08600,
- 0x0022cf01,
- 0xf1040280,
- 0xf00c30e7,
- 0x24bd50e3,
- 0x44bd34bd,
-/* 0x0410: init_unk_loop */
- 0xb06821f4,
- 0x0bf400f6,
- 0x01f7f00f,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x0425: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40126,
-/* 0x0431: init_unk_done */
- 0x070380e2,
- 0xf1080480,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
+ 0x28f40031,
+ 0x24d7f000,
+ 0xf43921f4,
+ 0xe4b0f401,
+ 0x1e18f404,
+ 0xf00181fe,
+ 0x20bd0627,
+ 0xb60412fd,
+ 0x1efd01e4,
+ 0x0018fe05,
+ 0x05e821f5,
+/* 0x0523: main_not_ctx_xfer */
+ 0x94d30ef4,
+ 0xf5f010ef,
+ 0x7e21f501,
+ 0xc60ef403,
+/* 0x0530: ih */
+ 0x88fe80f9,
+ 0xf980f901,
+ 0xf9a0f990,
+ 0xf9d0f9b0,
+ 0xbdf0f9e0,
+ 0x00a7f104,
+ 0x00a3f002,
+ 0xc400aacf,
+ 0x0bf404ab,
+ 0x24d7f02c,
+ 0x1a00e7f1,
+ 0xcf00e3f0,
+ 0xf7f100ee,
+ 0xf3f01900,
+ 0x00ffcf00,
+ 0xf00421f4,
+ 0x07f101e7,
+ 0x03f01d00,
+ 0x000ed000,
+/* 0x057e: ih_no_fifo */
0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x98020e98,
- 0x21f5030f,
- 0x0e980150,
- 0x00effd07,
- 0xbb002ebb,
- 0x35b6003e,
- 0x0007f102,
- 0x0103f0d3,
- 0xbd0003d0,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb90834b6,
- 0x21f5022f,
- 0x3fbb02d3,
- 0x0007f100,
- 0x0203f001,
- 0xbd0003d0,
- 0xf024bd04,
- 0x07f11f29,
- 0x03f00800,
- 0x0002d002,
-/* 0x04e2: main */
- 0x31f404bd,
- 0x0028f400,
- 0xf424d7f0,
- 0x01f43921,
- 0x04e4b0f4,
- 0xfe1e18f4,
- 0x27f00181,
- 0xfd20bd06,
- 0xe4b60412,
- 0x051efd01,
- 0xf50018fe,
- 0xf405d721,
-/* 0x0512: main_not_ctx_xfer */
- 0xef94d30e,
- 0x01f5f010,
- 0x037e21f5,
-/* 0x051f: ih */
- 0xf9c60ef4,
- 0x0188fe80,
- 0x90f980f9,
- 0xb0f9a0f9,
- 0xe0f9d0f9,
- 0x04bdf0f9,
- 0x0200a7f1,
- 0xcf00a3f0,
- 0xabc400aa,
- 0x2c0bf404,
- 0xf124d7f0,
- 0xf01a00e7,
- 0xeecf00e3,
- 0x00f7f100,
- 0x00f3f019,
- 0xf400ffcf,
- 0xe7f00421,
- 0x0007f101,
- 0x0003f01d,
- 0xbd000ed0,
-/* 0x056d: ih_no_fifo */
- 0x0007f104,
- 0x0003f001,
- 0xbd000ad0,
- 0xfcf0fc04,
- 0xfcd0fce0,
- 0xfca0fcb0,
- 0xfe80fc90,
- 0x80fc0088,
- 0xf80032f4,
-/* 0x0591: hub_barrier_done */
- 0x01f7f001,
- 0xbb040e98,
- 0xffb904fe,
- 0x18e7f102,
- 0x40e3f094,
- 0xf89d21f4,
-/* 0x05a9: ctx_redswitch */
- 0x20f7f000,
- 0x850007f1,
- 0xd00103f0,
- 0x04bd000f,
-/* 0x05bb: ctx_redswitch_delay */
- 0xb608e7f0,
- 0x1bf401e2,
- 0x00f5f1fd,
- 0x00f5f108,
- 0x0007f102,
+ 0x03f00100,
+ 0x000ad000,
+ 0xf0fc04bd,
+ 0xd0fce0fc,
+ 0xa0fcb0fc,
+ 0x80fc90fc,
+ 0xfc0088fe,
+ 0x0032f480,
+/* 0x05a2: hub_barrier_done */
+ 0xf7f001f8,
+ 0x040e9801,
+ 0xb904febb,
+ 0xe7f102ff,
+ 0xe3f09418,
+ 0x9d21f440,
+/* 0x05ba: ctx_redswitch */
+ 0xf7f000f8,
+ 0x0007f120,
0x0103f085,
0xbd000fd0,
-/* 0x05d7: ctx_xfer */
- 0xf100f804,
- 0xf0810007,
- 0x0fd00203,
- 0xf404bd00,
- 0x21f50711,
-/* 0x05ea: ctx_xfer_not_load */
- 0x21f505a9,
- 0x24bd026a,
- 0x47fc07f1,
+ 0x08e7f004,
+/* 0x05cc: ctx_redswitch_delay */
+ 0xf401e2b6,
+ 0xf5f1fd1b,
+ 0xf5f10800,
+ 0x07f10200,
+ 0x03f08500,
+ 0x000fd001,
+ 0x00f804bd,
+/* 0x05e8: ctx_xfer */
+ 0x810007f1,
0xd00203f0,
- 0x04bd0002,
- 0xb6012cf0,
- 0x07f10320,
- 0x03f04afc,
- 0x0002d002,
- 0xacf004bd,
- 0x02a5f001,
- 0x0000b7f1,
- 0x9850b3f0,
- 0xc4b6040c,
- 0x00bcbb0f,
- 0x98000c98,
- 0xe7f0010d,
- 0x6f21f500,
- 0x01acf001,
- 0x4000b7f1,
+ 0x04bd000f,
+ 0xf50711f4,
+/* 0x05fb: ctx_xfer_not_load */
+ 0xf505ba21,
+ 0xbd026a21,
+ 0xfc07f124,
+ 0x0203f047,
+ 0xbd0002d0,
+ 0x012cf004,
+ 0xf10320b6,
+ 0xf04afc07,
+ 0x02d00203,
+ 0xf004bd00,
+ 0xa5f001ac,
+ 0x00b7f102,
+ 0x50b3f000,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x000c9800,
+ 0xf0010d98,
+ 0x21f500e7,
+ 0xacf0016f,
+ 0x00b7f101,
+ 0x50b3f040,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x010c9800,
+ 0x98020d98,
+ 0xe7f1060f,
+ 0x21f50800,
+ 0xacf0016f,
+ 0x04a5f001,
+ 0x3000b7f1,
0x9850b3f0,
0xc4b6040c,
0x00bcbb0f,
- 0x98010c98,
- 0x0f98020d,
- 0x00e7f106,
- 0x6f21f508,
- 0x01acf001,
- 0xf104a5f0,
- 0xf03000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98020c,
- 0x080f9803,
- 0x0200e7f1,
- 0x016f21f5,
- 0x025e21f5,
- 0xf40601f4,
-/* 0x0686: ctx_xfer_post */
- 0x21f50712,
-/* 0x068a: ctx_xfer_done */
- 0x21f5027f,
- 0x00f80591,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
+ 0x98020c98,
+ 0x0f98030d,
+ 0x00e7f108,
+ 0x6f21f502,
+ 0x5e21f501,
+ 0x0601f402,
+/* 0x0697: ctx_xfer_post */
+ 0xf50712f4,
+/* 0x069b: ctx_xfer_done */
+ 0xf5027f21,
+ 0xf805a221,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h
index b6da800ee9c2..855b220378f9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h
@@ -196,7 +196,7 @@ uint32_t nve0_grgpc_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
@@ -304,212 +304,212 @@ uint32_t nve0_grgpc_code[] = {
0x21f440e3,
0xf8e0fc9d,
/* 0x03a1: init */
- 0xfe04bd00,
- 0x27f00004,
- 0x0007f102,
- 0x0003f012,
- 0xbd0002d0,
- 0x1f17f104,
- 0x0010fe05,
- 0x070007f1,
+ 0xf104bd00,
+ 0xf0420017,
+ 0x11cf0013,
+ 0x0911e700,
+ 0x0814b601,
+ 0xf00014fe,
+ 0x07f10227,
+ 0x03f01200,
+ 0x0002d000,
+ 0x17f104bd,
+ 0x10fe0530,
+ 0x0007f100,
+ 0x0003f007,
+ 0xbd0000d0,
+ 0x0427f004,
+ 0x040007f1,
0xd00003f0,
- 0x04bd0000,
- 0xf10427f0,
- 0xf0040007,
- 0x02d00003,
+ 0x04bd0002,
+ 0xf11031f4,
+ 0xf0820027,
+ 0x22cf0123,
+ 0x0137f000,
+ 0xbb1f24f0,
+ 0x32b60432,
+ 0x05028001,
+ 0xf1060380,
+ 0xf0860027,
+ 0x22cf0123,
+ 0x04028000,
+ 0x0c30e7f1,
+ 0xbd50e3f0,
+ 0xbd34bd24,
+/* 0x0421: init_unk_loop */
+ 0x6821f444,
+ 0xf400f6b0,
+ 0xf7f00f0b,
+ 0x04f2bb01,
+ 0xb6054ffd,
+/* 0x0436: init_unk_next */
+ 0x20b60130,
+ 0x04e0b601,
+ 0xf40126b0,
+/* 0x0442: init_unk_done */
+ 0x0380e21b,
+ 0x08048007,
+ 0x010027f1,
+ 0xcf0223f0,
+ 0x34bd0022,
+ 0xf1082595,
+ 0xf0c00007,
+ 0x05d00103,
+ 0xf104bd00,
+ 0xf0c10007,
+ 0x05d00103,
+ 0x9804bd00,
+ 0x0f98000e,
+ 0x5021f501,
+ 0x002fbb01,
+ 0x98003fbb,
+ 0x0f98010e,
+ 0x5021f502,
+ 0x050e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x020e9800,
+ 0xf5030f98,
+ 0x98015021,
+ 0xeffd070e,
+ 0x002ebb00,
+ 0xb6003ebb,
+ 0x07f10235,
+ 0x03f0d300,
+ 0x0003d001,
+ 0x25b604bd,
+ 0x0635b608,
+ 0xb60120b6,
+ 0x24b60130,
+ 0x0834b608,
+ 0xf5022fb9,
+ 0xbb02d321,
+ 0x07f1003f,
+ 0x03f00100,
+ 0x0003d002,
+ 0x24bd04bd,
+ 0xf11f29f0,
+ 0xf0080007,
+ 0x02d00203,
+/* 0x04f3: main */
0xf404bd00,
- 0x27f11031,
- 0x23f08200,
- 0x0022cf01,
- 0xf00137f0,
- 0x32bb1f24,
- 0x0132b604,
- 0x80050280,
- 0x27f10603,
- 0x23f08600,
- 0x0022cf01,
- 0xf1040280,
- 0xf00c30e7,
- 0x24bd50e3,
- 0x44bd34bd,
-/* 0x0410: init_unk_loop */
- 0xb06821f4,
- 0x0bf400f6,
- 0x01f7f00f,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x0425: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40126,
-/* 0x0431: init_unk_done */
- 0x070380e2,
- 0xf1080480,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
+ 0x28f40031,
+ 0x24d7f000,
+ 0xf43921f4,
+ 0xe4b0f401,
+ 0x1e18f404,
+ 0xf00181fe,
+ 0x20bd0627,
+ 0xb60412fd,
+ 0x1efd01e4,
+ 0x0018fe05,
+ 0x05e821f5,
+/* 0x0523: main_not_ctx_xfer */
+ 0x94d30ef4,
+ 0xf5f010ef,
+ 0x7e21f501,
+ 0xc60ef403,
+/* 0x0530: ih */
+ 0x88fe80f9,
+ 0xf980f901,
+ 0xf9a0f990,
+ 0xf9d0f9b0,
+ 0xbdf0f9e0,
+ 0x00a7f104,
+ 0x00a3f002,
+ 0xc400aacf,
+ 0x0bf404ab,
+ 0x24d7f02c,
+ 0x1a00e7f1,
+ 0xcf00e3f0,
+ 0xf7f100ee,
+ 0xf3f01900,
+ 0x00ffcf00,
+ 0xf00421f4,
+ 0x07f101e7,
+ 0x03f01d00,
+ 0x000ed000,
+/* 0x057e: ih_no_fifo */
0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x98020e98,
- 0x21f5030f,
- 0x0e980150,
- 0x00effd07,
- 0xbb002ebb,
- 0x35b6003e,
- 0x0007f102,
- 0x0103f0d3,
- 0xbd0003d0,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb90834b6,
- 0x21f5022f,
- 0x3fbb02d3,
- 0x0007f100,
- 0x0203f001,
- 0xbd0003d0,
- 0xf024bd04,
- 0x07f11f29,
- 0x03f00800,
- 0x0002d002,
-/* 0x04e2: main */
- 0x31f404bd,
- 0x0028f400,
- 0xf424d7f0,
- 0x01f43921,
- 0x04e4b0f4,
- 0xfe1e18f4,
- 0x27f00181,
- 0xfd20bd06,
- 0xe4b60412,
- 0x051efd01,
- 0xf50018fe,
- 0xf405d721,
-/* 0x0512: main_not_ctx_xfer */
- 0xef94d30e,
- 0x01f5f010,
- 0x037e21f5,
-/* 0x051f: ih */
- 0xf9c60ef4,
- 0x0188fe80,
- 0x90f980f9,
- 0xb0f9a0f9,
- 0xe0f9d0f9,
- 0x04bdf0f9,
- 0x0200a7f1,
- 0xcf00a3f0,
- 0xabc400aa,
- 0x2c0bf404,
- 0xf124d7f0,
- 0xf01a00e7,
- 0xeecf00e3,
- 0x00f7f100,
- 0x00f3f019,
- 0xf400ffcf,
- 0xe7f00421,
- 0x0007f101,
- 0x0003f01d,
- 0xbd000ed0,
-/* 0x056d: ih_no_fifo */
- 0x0007f104,
- 0x0003f001,
- 0xbd000ad0,
- 0xfcf0fc04,
- 0xfcd0fce0,
- 0xfca0fcb0,
- 0xfe80fc90,
- 0x80fc0088,
- 0xf80032f4,
-/* 0x0591: hub_barrier_done */
- 0x01f7f001,
- 0xbb040e98,
- 0xffb904fe,
- 0x18e7f102,
- 0x40e3f094,
- 0xf89d21f4,
-/* 0x05a9: ctx_redswitch */
- 0x20f7f000,
- 0x850007f1,
- 0xd00103f0,
- 0x04bd000f,
-/* 0x05bb: ctx_redswitch_delay */
- 0xb608e7f0,
- 0x1bf401e2,
- 0x00f5f1fd,
- 0x00f5f108,
- 0x0007f102,
+ 0x03f00100,
+ 0x000ad000,
+ 0xf0fc04bd,
+ 0xd0fce0fc,
+ 0xa0fcb0fc,
+ 0x80fc90fc,
+ 0xfc0088fe,
+ 0x0032f480,
+/* 0x05a2: hub_barrier_done */
+ 0xf7f001f8,
+ 0x040e9801,
+ 0xb904febb,
+ 0xe7f102ff,
+ 0xe3f09418,
+ 0x9d21f440,
+/* 0x05ba: ctx_redswitch */
+ 0xf7f000f8,
+ 0x0007f120,
0x0103f085,
0xbd000fd0,
-/* 0x05d7: ctx_xfer */
- 0xf100f804,
- 0xf0810007,
- 0x0fd00203,
- 0xf404bd00,
- 0x21f50711,
-/* 0x05ea: ctx_xfer_not_load */
- 0x21f505a9,
- 0x24bd026a,
- 0x47fc07f1,
+ 0x08e7f004,
+/* 0x05cc: ctx_redswitch_delay */
+ 0xf401e2b6,
+ 0xf5f1fd1b,
+ 0xf5f10800,
+ 0x07f10200,
+ 0x03f08500,
+ 0x000fd001,
+ 0x00f804bd,
+/* 0x05e8: ctx_xfer */
+ 0x810007f1,
0xd00203f0,
- 0x04bd0002,
- 0xb6012cf0,
- 0x07f10320,
- 0x03f04afc,
- 0x0002d002,
- 0xacf004bd,
- 0x02a5f001,
- 0x0000b7f1,
- 0x9850b3f0,
- 0xc4b6040c,
- 0x00bcbb0f,
- 0x98000c98,
- 0xe7f0010d,
- 0x6f21f500,
- 0x01acf001,
- 0x4000b7f1,
+ 0x04bd000f,
+ 0xf50711f4,
+/* 0x05fb: ctx_xfer_not_load */
+ 0xf505ba21,
+ 0xbd026a21,
+ 0xfc07f124,
+ 0x0203f047,
+ 0xbd0002d0,
+ 0x012cf004,
+ 0xf10320b6,
+ 0xf04afc07,
+ 0x02d00203,
+ 0xf004bd00,
+ 0xa5f001ac,
+ 0x00b7f102,
+ 0x50b3f000,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x000c9800,
+ 0xf0010d98,
+ 0x21f500e7,
+ 0xacf0016f,
+ 0x00b7f101,
+ 0x50b3f040,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x010c9800,
+ 0x98020d98,
+ 0xe7f1060f,
+ 0x21f50800,
+ 0xacf0016f,
+ 0x04a5f001,
+ 0x3000b7f1,
0x9850b3f0,
0xc4b6040c,
0x00bcbb0f,
- 0x98010c98,
- 0x0f98020d,
- 0x00e7f106,
- 0x6f21f508,
- 0x01acf001,
- 0xf104a5f0,
- 0xf03000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98020c,
- 0x080f9803,
- 0x0200e7f1,
- 0x016f21f5,
- 0x025e21f5,
- 0xf40601f4,
-/* 0x0686: ctx_xfer_post */
- 0x21f50712,
-/* 0x068a: ctx_xfer_done */
- 0x21f5027f,
- 0x00f80591,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
+ 0x98020c98,
+ 0x0f98030d,
+ 0x00e7f108,
+ 0x6f21f502,
+ 0x5e21f501,
+ 0x0601f402,
+/* 0x0697: ctx_xfer_post */
+ 0xf50712f4,
+/* 0x069b: ctx_xfer_done */
+ 0xf5027f21,
+ 0xf805a221,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h
index 6316ebaf5d9a..1b803197d28b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h
@@ -196,7 +196,7 @@ uint32_t nvf0_grgpc_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
@@ -304,212 +304,212 @@ uint32_t nvf0_grgpc_code[] = {
0x21f440e3,
0xf8e0fc9d,
/* 0x03a1: init */
- 0xfe04bd00,
- 0x27f00004,
- 0x0007f102,
- 0x0003f012,
- 0xbd0002d0,
- 0x1f17f104,
- 0x0010fe05,
- 0x070007f1,
+ 0xf104bd00,
+ 0xf0420017,
+ 0x11cf0013,
+ 0x0911e700,
+ 0x0814b601,
+ 0xf00014fe,
+ 0x07f10227,
+ 0x03f01200,
+ 0x0002d000,
+ 0x17f104bd,
+ 0x10fe0530,
+ 0x0007f100,
+ 0x0003f007,
+ 0xbd0000d0,
+ 0x0427f004,
+ 0x040007f1,
0xd00003f0,
- 0x04bd0000,
- 0xf10427f0,
- 0xf0040007,
- 0x02d00003,
+ 0x04bd0002,
+ 0xf11031f4,
+ 0xf0820027,
+ 0x22cf0123,
+ 0x0137f000,
+ 0xbb1f24f0,
+ 0x32b60432,
+ 0x05028001,
+ 0xf1060380,
+ 0xf0860027,
+ 0x22cf0123,
+ 0x04028000,
+ 0x0c30e7f1,
+ 0xbd50e3f0,
+ 0xbd34bd24,
+/* 0x0421: init_unk_loop */
+ 0x6821f444,
+ 0xf400f6b0,
+ 0xf7f00f0b,
+ 0x04f2bb01,
+ 0xb6054ffd,
+/* 0x0436: init_unk_next */
+ 0x20b60130,
+ 0x04e0b601,
+ 0xf40226b0,
+/* 0x0442: init_unk_done */
+ 0x0380e21b,
+ 0x08048007,
+ 0x010027f1,
+ 0xcf0223f0,
+ 0x34bd0022,
+ 0xf1082595,
+ 0xf0c00007,
+ 0x05d00103,
+ 0xf104bd00,
+ 0xf0c10007,
+ 0x05d00103,
+ 0x9804bd00,
+ 0x0f98000e,
+ 0x5021f501,
+ 0x002fbb01,
+ 0x98003fbb,
+ 0x0f98010e,
+ 0x5021f502,
+ 0x050e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x020e9800,
+ 0xf5030f98,
+ 0x98015021,
+ 0xeffd070e,
+ 0x002ebb00,
+ 0xb6003ebb,
+ 0x07f10235,
+ 0x03f0d300,
+ 0x0003d001,
+ 0x25b604bd,
+ 0x0635b608,
+ 0xb60120b6,
+ 0x24b60130,
+ 0x0834b608,
+ 0xf5022fb9,
+ 0xbb02d321,
+ 0x07f1003f,
+ 0x03f00100,
+ 0x0003d002,
+ 0x24bd04bd,
+ 0xf11f29f0,
+ 0xf0300007,
+ 0x02d00203,
+/* 0x04f3: main */
0xf404bd00,
- 0x27f11031,
- 0x23f08200,
- 0x0022cf01,
- 0xf00137f0,
- 0x32bb1f24,
- 0x0132b604,
- 0x80050280,
- 0x27f10603,
- 0x23f08600,
- 0x0022cf01,
- 0xf1040280,
- 0xf00c30e7,
- 0x24bd50e3,
- 0x44bd34bd,
-/* 0x0410: init_unk_loop */
- 0xb06821f4,
- 0x0bf400f6,
- 0x01f7f00f,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x0425: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40226,
-/* 0x0431: init_unk_done */
- 0x070380e2,
- 0xf1080480,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
+ 0x28f40031,
+ 0x24d7f000,
+ 0xf43921f4,
+ 0xe4b0f401,
+ 0x1e18f404,
+ 0xf00181fe,
+ 0x20bd0627,
+ 0xb60412fd,
+ 0x1efd01e4,
+ 0x0018fe05,
+ 0x05e821f5,
+/* 0x0523: main_not_ctx_xfer */
+ 0x94d30ef4,
+ 0xf5f010ef,
+ 0x7e21f501,
+ 0xc60ef403,
+/* 0x0530: ih */
+ 0x88fe80f9,
+ 0xf980f901,
+ 0xf9a0f990,
+ 0xf9d0f9b0,
+ 0xbdf0f9e0,
+ 0x00a7f104,
+ 0x00a3f002,
+ 0xc400aacf,
+ 0x0bf404ab,
+ 0x24d7f02c,
+ 0x1a00e7f1,
+ 0xcf00e3f0,
+ 0xf7f100ee,
+ 0xf3f01900,
+ 0x00ffcf00,
+ 0xf00421f4,
+ 0x07f101e7,
+ 0x03f01d00,
+ 0x000ed000,
+/* 0x057e: ih_no_fifo */
0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x98020e98,
- 0x21f5030f,
- 0x0e980150,
- 0x00effd07,
- 0xbb002ebb,
- 0x35b6003e,
- 0x0007f102,
- 0x0103f0d3,
- 0xbd0003d0,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb90834b6,
- 0x21f5022f,
- 0x3fbb02d3,
- 0x0007f100,
- 0x0203f001,
- 0xbd0003d0,
- 0xf024bd04,
- 0x07f11f29,
- 0x03f03000,
- 0x0002d002,
-/* 0x04e2: main */
- 0x31f404bd,
- 0x0028f400,
- 0xf424d7f0,
- 0x01f43921,
- 0x04e4b0f4,
- 0xfe1e18f4,
- 0x27f00181,
- 0xfd20bd06,
- 0xe4b60412,
- 0x051efd01,
- 0xf50018fe,
- 0xf405d721,
-/* 0x0512: main_not_ctx_xfer */
- 0xef94d30e,
- 0x01f5f010,
- 0x037e21f5,
-/* 0x051f: ih */
- 0xf9c60ef4,
- 0x0188fe80,
- 0x90f980f9,
- 0xb0f9a0f9,
- 0xe0f9d0f9,
- 0x04bdf0f9,
- 0x0200a7f1,
- 0xcf00a3f0,
- 0xabc400aa,
- 0x2c0bf404,
- 0xf124d7f0,
- 0xf01a00e7,
- 0xeecf00e3,
- 0x00f7f100,
- 0x00f3f019,
- 0xf400ffcf,
- 0xe7f00421,
- 0x0007f101,
- 0x0003f01d,
- 0xbd000ed0,
-/* 0x056d: ih_no_fifo */
- 0x0007f104,
- 0x0003f001,
- 0xbd000ad0,
- 0xfcf0fc04,
- 0xfcd0fce0,
- 0xfca0fcb0,
- 0xfe80fc90,
- 0x80fc0088,
- 0xf80032f4,
-/* 0x0591: hub_barrier_done */
- 0x01f7f001,
- 0xbb040e98,
- 0xffb904fe,
- 0x18e7f102,
- 0x40e3f094,
- 0xf89d21f4,
-/* 0x05a9: ctx_redswitch */
- 0x20f7f000,
- 0x850007f1,
- 0xd00103f0,
- 0x04bd000f,
-/* 0x05bb: ctx_redswitch_delay */
- 0xb608e7f0,
- 0x1bf401e2,
- 0x00f5f1fd,
- 0x00f5f108,
- 0x0007f102,
+ 0x03f00100,
+ 0x000ad000,
+ 0xf0fc04bd,
+ 0xd0fce0fc,
+ 0xa0fcb0fc,
+ 0x80fc90fc,
+ 0xfc0088fe,
+ 0x0032f480,
+/* 0x05a2: hub_barrier_done */
+ 0xf7f001f8,
+ 0x040e9801,
+ 0xb904febb,
+ 0xe7f102ff,
+ 0xe3f09418,
+ 0x9d21f440,
+/* 0x05ba: ctx_redswitch */
+ 0xf7f000f8,
+ 0x0007f120,
0x0103f085,
0xbd000fd0,
-/* 0x05d7: ctx_xfer */
- 0xf100f804,
- 0xf0810007,
- 0x0fd00203,
- 0xf404bd00,
- 0x21f50711,
-/* 0x05ea: ctx_xfer_not_load */
- 0x21f505a9,
- 0x24bd026a,
- 0x47fc07f1,
+ 0x08e7f004,
+/* 0x05cc: ctx_redswitch_delay */
+ 0xf401e2b6,
+ 0xf5f1fd1b,
+ 0xf5f10800,
+ 0x07f10200,
+ 0x03f08500,
+ 0x000fd001,
+ 0x00f804bd,
+/* 0x05e8: ctx_xfer */
+ 0x810007f1,
0xd00203f0,
- 0x04bd0002,
- 0xb6012cf0,
- 0x07f10320,
- 0x03f04afc,
- 0x0002d002,
- 0xacf004bd,
- 0x02a5f001,
- 0x0000b7f1,
- 0x9850b3f0,
- 0xc4b6040c,
- 0x00bcbb0f,
- 0x98000c98,
- 0xe7f0010d,
- 0x6f21f500,
- 0x01acf001,
- 0x4000b7f1,
+ 0x04bd000f,
+ 0xf50711f4,
+/* 0x05fb: ctx_xfer_not_load */
+ 0xf505ba21,
+ 0xbd026a21,
+ 0xfc07f124,
+ 0x0203f047,
+ 0xbd0002d0,
+ 0x012cf004,
+ 0xf10320b6,
+ 0xf04afc07,
+ 0x02d00203,
+ 0xf004bd00,
+ 0xa5f001ac,
+ 0x00b7f102,
+ 0x50b3f000,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x000c9800,
+ 0xf0010d98,
+ 0x21f500e7,
+ 0xacf0016f,
+ 0x00b7f101,
+ 0x50b3f040,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x010c9800,
+ 0x98020d98,
+ 0xe7f1060f,
+ 0x21f50800,
+ 0xacf0016f,
+ 0x04a5f001,
+ 0x3000b7f1,
0x9850b3f0,
0xc4b6040c,
0x00bcbb0f,
- 0x98010c98,
- 0x0f98020d,
- 0x00e7f106,
- 0x6f21f508,
- 0x01acf001,
- 0xf104a5f0,
- 0xf03000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98020c,
- 0x080f9803,
- 0x0200e7f1,
- 0x016f21f5,
- 0x025e21f5,
- 0xf40601f4,
-/* 0x0686: ctx_xfer_post */
- 0x21f50712,
-/* 0x068a: ctx_xfer_done */
- 0x21f5027f,
- 0x00f80591,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
+ 0x98020c98,
+ 0x0f98030d,
+ 0x00e7f108,
+ 0x6f21f502,
+ 0x5e21f501,
+ 0x0601f402,
+/* 0x0697: ctx_xfer_post */
+ 0xf50712f4,
+/* 0x069b: ctx_xfer_done */
+ 0xf5027f21,
+ 0xf805a221,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5 b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5
new file mode 100644
index 000000000000..27591b3086a5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#define CHIPSET GK208
+#include "macros.fuc"
+
+.section #gm107_grhub_data
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "hub.fuc"
+#undef INCLUDE_DATA
+
+.section #gm107_grhub_code
+#define INCLUDE_CODE
+bra #init
+#include "com.fuc"
+#include "hub.fuc"
+.align 256
+#undef INCLUDE_CODE
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h
new file mode 100644
index 000000000000..214dd16ec566
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubgm107.fuc5.h
@@ -0,0 +1,916 @@
+uint32_t gm107_grhub_data[] = {
+/* 0x0000: hub_mmio_list_head */
+ 0x00000300,
+/* 0x0004: hub_mmio_list_tail */
+ 0x00000304,
+/* 0x0008: gpc_count */
+ 0x00000000,
+/* 0x000c: rop_count */
+ 0x00000000,
+/* 0x0010: cmd_queue */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0058: ctx_current */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0100: chan_data */
+/* 0x0100: chan_mmio_count */
+ 0x00000000,
+/* 0x0104: chan_mmio_address */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0200: xfer_data */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0300: hub_mmio_list_base */
+ 0x0417e91c,
+};
+
+uint32_t gm107_grhub_code[] = {
+ 0x030e0ef5,
+/* 0x0004: queue_put */
+ 0x9800d898,
+ 0x86f001d9,
+ 0xf489a408,
+ 0x020f0b1b,
+ 0x0002f87e,
+/* 0x001a: queue_put_next */
+ 0x98c400f8,
+ 0x0384b607,
+ 0xb6008dbb,
+ 0x8eb50880,
+ 0x018fb500,
+ 0xf00190b6,
+ 0xd9b50f94,
+/* 0x0037: queue_get */
+ 0xf400f801,
+ 0xd8980131,
+ 0x01d99800,
+ 0x0bf489a4,
+ 0x0789c421,
+ 0xbb0394b6,
+ 0x90b6009d,
+ 0x009e9808,
+ 0xb6019f98,
+ 0x84f00180,
+ 0x00d8b50f,
+/* 0x0063: queue_get_done */
+ 0xf80132f4,
+/* 0x0065: nv_rd32 */
+ 0xf0ecb200,
+ 0x00801fc9,
+ 0x0cf601ca,
+/* 0x0073: nv_rd32_wait */
+ 0x8c04bd00,
+ 0xcf01ca00,
+ 0xccc800cc,
+ 0xf61bf41f,
+ 0xec7e060a,
+ 0x008f0000,
+ 0xffcf01cb,
+/* 0x008f: nv_wr32 */
+ 0x8000f800,
+ 0xf601cc00,
+ 0x04bd000f,
+ 0xc9f0ecb2,
+ 0x1ec9f01f,
+ 0x01ca0080,
+ 0xbd000cf6,
+/* 0x00a9: nv_wr32_wait */
+ 0xca008c04,
+ 0x00cccf01,
+ 0xf41fccc8,
+ 0x00f8f61b,
+/* 0x00b8: wait_donez */
+ 0x99f094bd,
+ 0x37008000,
+ 0x0009f602,
+ 0x008004bd,
+ 0x0af60206,
+/* 0x00cf: wait_donez_ne */
+ 0x8804bd00,
+ 0xcf010000,
+ 0x8aff0088,
+ 0xf61bf488,
+ 0x99f094bd,
+ 0x17008000,
+ 0x0009f602,
+ 0x00f804bd,
+/* 0x00ec: wait_doneo */
+ 0x99f094bd,
+ 0x37008000,
+ 0x0009f602,
+ 0x008004bd,
+ 0x0af60206,
+/* 0x0103: wait_doneo_e */
+ 0x8804bd00,
+ 0xcf010000,
+ 0x8aff0088,
+ 0xf60bf488,
+ 0x99f094bd,
+ 0x17008000,
+ 0x0009f602,
+ 0x00f804bd,
+/* 0x0120: mmctx_size */
+/* 0x0122: nv_mmctx_size_loop */
+ 0xe89894bd,
+ 0x1a85b600,
+ 0xb60180b6,
+ 0x98bb0284,
+ 0x04e0b600,
+ 0x1bf4efa4,
+ 0xf89fb2ec,
+/* 0x013d: mmctx_xfer */
+ 0xf094bd00,
+ 0x00800199,
+ 0x09f60237,
+ 0xbd04bd00,
+ 0x05bbfd94,
+ 0x800f0bf4,
+ 0xf601c400,
+ 0x04bd000b,
+/* 0x015f: mmctx_base_disabled */
+ 0xfd0099f0,
+ 0x0bf405ee,
+ 0xc6008018,
+ 0x000ef601,
+ 0x008004bd,
+ 0x0ff601c7,
+ 0xf004bd00,
+/* 0x017a: mmctx_multi_disabled */
+ 0xabc80199,
+ 0x10b4b600,
+ 0xc80cb9f0,
+ 0xe4b601ae,
+ 0x05befd11,
+ 0x01c50080,
+ 0xbd000bf6,
+/* 0x0195: mmctx_exec_loop */
+/* 0x0195: mmctx_wait_free */
+ 0xc5008e04,
+ 0x00eecf01,
+ 0xf41fe4f0,
+ 0xce98f60b,
+ 0x05e9fd00,
+ 0x01c80080,
+ 0xbd000ef6,
+ 0x04c0b604,
+ 0x1bf4cda4,
+ 0x02abc8df,
+/* 0x01bf: mmctx_fini_wait */
+ 0x8b1c1bf4,
+ 0xcf01c500,
+ 0xb4f000bb,
+ 0x10b4b01f,
+ 0x0af31bf4,
+ 0x00b87e05,
+ 0x250ef400,
+/* 0x01d8: mmctx_stop */
+ 0xb600abc8,
+ 0xb9f010b4,
+ 0x12b9f00c,
+ 0x01c50080,
+ 0xbd000bf6,
+/* 0x01ed: mmctx_stop_wait */
+ 0xc5008b04,
+ 0x00bbcf01,
+ 0xf412bbc8,
+/* 0x01fa: mmctx_done */
+ 0x94bdf61b,
+ 0x800199f0,
+ 0xf6021700,
+ 0x04bd0009,
+/* 0x020a: strand_wait */
+ 0xa0f900f8,
+ 0xb87e020a,
+ 0xa0fc0000,
+/* 0x0216: strand_pre */
+ 0x0c0900f8,
+ 0x024afc80,
+ 0xbd0009f6,
+ 0x020a7e04,
+/* 0x0227: strand_post */
+ 0x0900f800,
+ 0x4afc800d,
+ 0x0009f602,
+ 0x0a7e04bd,
+ 0x00f80002,
+/* 0x0238: strand_set */
+ 0xfc800f0c,
+ 0x0cf6024f,
+ 0x0c04bd00,
+ 0x4afc800b,
+ 0x000cf602,
+ 0xfc8004bd,
+ 0x0ef6024f,
+ 0x0c04bd00,
+ 0x4afc800a,
+ 0x000cf602,
+ 0x0a7e04bd,
+ 0x00f80002,
+/* 0x0268: strand_ctx_init */
+ 0x99f094bd,
+ 0x37008003,
+ 0x0009f602,
+ 0x167e04bd,
+ 0x030e0002,
+ 0x0002387e,
+ 0xfc80c4bd,
+ 0x0cf60247,
+ 0x0c04bd00,
+ 0x4afc8001,
+ 0x000cf602,
+ 0x0a7e04bd,
+ 0x0c920002,
+ 0x46fc8001,
+ 0x000cf602,
+ 0x020c04bd,
+ 0x024afc80,
+ 0xbd000cf6,
+ 0x020a7e04,
+ 0x02277e00,
+ 0x42008800,
+ 0x20008902,
+ 0x0099cf02,
+/* 0x02c7: ctx_init_strand_loop */
+ 0xf608fe95,
+ 0x8ef6008e,
+ 0x808acf40,
+ 0xb606a5b6,
+ 0xeabb01a0,
+ 0x0480b600,
+ 0xf40192b6,
+ 0xe4b6e81b,
+ 0xf2efbc08,
+ 0x99f094bd,
+ 0x17008003,
+ 0x0009f602,
+ 0x00f804bd,
+/* 0x02f8: error */
+ 0x02050080,
+ 0xbd000ff6,
+ 0x80010f04,
+ 0xf6030700,
+ 0x04bd000f,
+/* 0x030e: init */
+ 0x04bd00f8,
+ 0x410007fe,
+ 0x11cf4200,
+ 0x0911e700,
+ 0x0814b601,
+ 0x020014fe,
+ 0x12004002,
+ 0xbd0002f6,
+ 0x05c94104,
+ 0xbd0010fe,
+ 0x07004024,
+ 0xbd0002f6,
+ 0x20034204,
+ 0x01010080,
+ 0xbd0002f6,
+ 0x20044204,
+ 0x01010480,
+ 0xbd0002f6,
+ 0x200b4204,
+ 0x01010880,
+ 0xbd0002f6,
+ 0x200c4204,
+ 0x01011c80,
+ 0xbd0002f6,
+ 0x01039204,
+ 0x03090080,
+ 0xbd0003f6,
+ 0x87044204,
+ 0xf6040040,
+ 0x04bd0002,
+ 0x00400402,
+ 0x0002f603,
+ 0x31f404bd,
+ 0x96048e10,
+ 0x00657e40,
+ 0xc7feb200,
+ 0x01b590f1,
+ 0x1ff4f003,
+ 0x01020fb5,
+ 0x041fbb01,
+ 0x800112b6,
+ 0xf6010300,
+ 0x04bd0001,
+ 0x01040080,
+ 0xbd0001f6,
+ 0x01004104,
+ 0x627e020f,
+ 0x717e0006,
+ 0x100f0006,
+ 0x0006b37e,
+ 0x98000e98,
+ 0x207e010f,
+ 0x14950001,
+ 0xc0008008,
+ 0x0004f601,
+ 0x008004bd,
+ 0x04f601c1,
+ 0xb704bd00,
+ 0xbb130030,
+ 0xf5b6001f,
+ 0xd3008002,
+ 0x000ff601,
+ 0x15b604bd,
+ 0x0110b608,
+ 0xb20814b6,
+ 0x02687e1f,
+ 0x001fbb00,
+ 0x84020398,
+/* 0x041f: init_gpc */
+ 0xb8502000,
+ 0x0008044e,
+ 0x8f7e1fb2,
+ 0x4eb80000,
+ 0xbd00010c,
+ 0x008f7ef4,
+ 0x044eb800,
+ 0x8f7e0001,
+ 0x4eb80000,
+ 0x0f000100,
+ 0x008f7e02,
+ 0x004eb800,
+/* 0x044e: init_gpc_wait */
+ 0x657e0008,
+ 0xffc80000,
+ 0xf90bf41f,
+ 0x08044eb8,
+ 0x00657e00,
+ 0x001fbb00,
+ 0x800040b7,
+ 0xf40132b6,
+ 0x000fb41b,
+ 0x0006b37e,
+ 0x627e000f,
+ 0x00800006,
+ 0x01f60201,
+ 0xbd04bd00,
+ 0x1f19f014,
+ 0x02300080,
+ 0xbd0001f6,
+/* 0x0491: main */
+ 0x0031f404,
+ 0x0d0028f4,
+ 0x00377e10,
+ 0xf401f400,
+ 0x4001e4b1,
+ 0x00c71bf5,
+ 0x99f094bd,
+ 0x37008004,
+ 0x0009f602,
+ 0x008104bd,
+ 0x11cf02c0,
+ 0xc1008200,
+ 0x0022cf02,
+ 0xf41f13c8,
+ 0x23c8770b,
+ 0x550bf41f,
+ 0x12b220f9,
+ 0x99f094bd,
+ 0x37008007,
+ 0x0009f602,
+ 0x32f404bd,
+ 0x0231f401,
+ 0x0008367e,
+ 0x99f094bd,
+ 0x17008007,
+ 0x0009f602,
+ 0x20fc04bd,
+ 0x99f094bd,
+ 0x37008006,
+ 0x0009f602,
+ 0x31f404bd,
+ 0x08367e01,
+ 0xf094bd00,
+ 0x00800699,
+ 0x09f60217,
+ 0xf404bd00,
+/* 0x0522: chsw_prev_no_next */
+ 0x20f92f0e,
+ 0x32f412b2,
+ 0x0232f401,
+ 0x0008367e,
+ 0x008020fc,
+ 0x02f602c0,
+ 0xf404bd00,
+/* 0x053e: chsw_no_prev */
+ 0x23c8130e,
+ 0x0d0bf41f,
+ 0xf40131f4,
+ 0x367e0232,
+/* 0x054e: chsw_done */
+ 0x01020008,
+ 0x02c30080,
+ 0xbd0002f6,
+ 0xf094bd04,
+ 0x00800499,
+ 0x09f60217,
+ 0xf504bd00,
+/* 0x056b: main_not_ctx_switch */
+ 0xb0ff2a0e,
+ 0x1bf401e4,
+ 0x7ef2b20c,
+ 0xf40007d6,
+/* 0x057a: main_not_ctx_chan */
+ 0xe4b0400e,
+ 0x2c1bf402,
+ 0x99f094bd,
+ 0x37008007,
+ 0x0009f602,
+ 0x32f404bd,
+ 0x0232f401,
+ 0x0008367e,
+ 0x99f094bd,
+ 0x17008007,
+ 0x0009f602,
+ 0x0ef404bd,
+/* 0x05a9: main_not_ctx_save */
+ 0x10ef9411,
+ 0x7e01f5f0,
+ 0xf50002f8,
+/* 0x05b7: main_done */
+ 0xbdfede0e,
+ 0x1f29f024,
+ 0x02300080,
+ 0xbd0002f6,
+ 0xcc0ef504,
+/* 0x05c9: ih */
+ 0xfe80f9fe,
+ 0x80f90188,
+ 0xa0f990f9,
+ 0xd0f9b0f9,
+ 0xf0f9e0f9,
+ 0x004a04bd,
+ 0x00aacf02,
+ 0xf404abc4,
+ 0x100d230b,
+ 0xcf1a004e,
+ 0x004f00ee,
+ 0x00ffcf19,
+ 0x0000047e,
+ 0x0400b0b7,
+ 0x0040010e,
+ 0x000ef61d,
+/* 0x060a: ih_no_fifo */
+ 0xabe404bd,
+ 0x0bf40100,
+ 0x4e100d0c,
+ 0x047e4001,
+/* 0x061a: ih_no_ctxsw */
+ 0xabe40000,
+ 0x0bf40400,
+ 0x01004b10,
+ 0x448ebfb2,
+ 0x8f7e4001,
+/* 0x062e: ih_no_fwmthd */
+ 0x044b0000,
+ 0xffb0bd01,
+ 0x0bf4b4ab,
+ 0x0700800c,
+ 0x000bf603,
+/* 0x0642: ih_no_other */
+ 0x004004bd,
+ 0x000af601,
+ 0xf0fc04bd,
+ 0xd0fce0fc,
+ 0xa0fcb0fc,
+ 0x80fc90fc,
+ 0xfc0088fe,
+ 0x0032f480,
+/* 0x0662: ctx_4170s */
+ 0xf5f001f8,
+ 0x8effb210,
+ 0x7e404170,
+ 0xf800008f,
+/* 0x0671: ctx_4170w */
+ 0x41708e00,
+ 0x00657e40,
+ 0xf0ffb200,
+ 0x1bf410f4,
+/* 0x0683: ctx_redswitch */
+ 0x4e00f8f3,
+ 0xe5f00200,
+ 0x20e5f040,
+ 0x8010e5f0,
+ 0xf6018500,
+ 0x04bd000e,
+/* 0x069a: ctx_redswitch_delay */
+ 0xf2b6080f,
+ 0xfd1bf401,
+ 0x0400e5f1,
+ 0x0100e5f1,
+ 0x01850080,
+ 0xbd000ef6,
+/* 0x06b3: ctx_86c */
+ 0x8000f804,
+ 0xf6022300,
+ 0x04bd000f,
+ 0x148effb2,
+ 0x8f7e408a,
+ 0xffb20000,
+ 0x41a88c8e,
+ 0x00008f7e,
+/* 0x06d2: ctx_mem */
+ 0x008000f8,
+ 0x0ff60284,
+/* 0x06db: ctx_mem_wait */
+ 0x8f04bd00,
+ 0xcf028400,
+ 0xfffd00ff,
+ 0xf61bf405,
+/* 0x06ea: ctx_load */
+ 0x94bd00f8,
+ 0x800599f0,
+ 0xf6023700,
+ 0x04bd0009,
+ 0xb87e0c0a,
+ 0xf4bd0000,
+ 0x02890080,
+ 0xbd000ff6,
+ 0xc1008004,
+ 0x0002f602,
+ 0x008004bd,
+ 0x02f60283,
+ 0x0f04bd00,
+ 0x06d27e07,
+ 0xc0008000,
+ 0x0002f602,
+ 0x0bfe04bd,
+ 0x1f2af000,
+ 0xb60424b6,
+ 0x94bd0220,
+ 0x800899f0,
+ 0xf6023700,
+ 0x04bd0009,
+ 0x02810080,
+ 0xbd0002f6,
+ 0x0000d204,
+ 0x25f08000,
+ 0x88008002,
+ 0x0002f602,
+ 0x100104bd,
+ 0xf0020042,
+ 0x12fa0223,
+ 0xbd03f805,
+ 0x0899f094,
+ 0x02170080,
+ 0xbd0009f6,
+ 0x81019804,
+ 0x981814b6,
+ 0x25b68002,
+ 0x0512fd08,
+ 0xbd1601b5,
+ 0x0999f094,
+ 0x02370080,
+ 0xbd0009f6,
+ 0x81008004,
+ 0x0001f602,
+ 0x010204bd,
+ 0x02880080,
+ 0xbd0002f6,
+ 0x01004104,
+ 0xfa0613f0,
+ 0x03f80501,
+ 0x99f094bd,
+ 0x17008009,
+ 0x0009f602,
+ 0x94bd04bd,
+ 0x800599f0,
+ 0xf6021700,
+ 0x04bd0009,
+/* 0x07d6: ctx_chan */
+ 0xea7e00f8,
+ 0x0c0a0006,
+ 0x0000b87e,
+ 0xd27e050f,
+ 0x00f80006,
+/* 0x07e8: ctx_mmio_exec */
+ 0x80410398,
+ 0xf6028100,
+ 0x04bd0003,
+/* 0x07f6: ctx_mmio_loop */
+ 0x34c434bd,
+ 0x0e1bf4ff,
+ 0xf0020045,
+ 0x35fa0653,
+/* 0x0807: ctx_mmio_pull */
+ 0x9803f805,
+ 0x4f98804e,
+ 0x008f7e81,
+ 0x0830b600,
+ 0xf40112b6,
+/* 0x081a: ctx_mmio_done */
+ 0x0398df1b,
+ 0x81008016,
+ 0x0003f602,
+ 0x00b504bd,
+ 0x01004140,
+ 0xfa0613f0,
+ 0x03f80601,
+/* 0x0836: ctx_xfer */
+ 0x040e00f8,
+ 0x03020080,
+ 0xbd000ef6,
+/* 0x0841: ctx_xfer_idle */
+ 0x00008e04,
+ 0x00eecf03,
+ 0x2000e4f1,
+ 0xf4f51bf4,
+ 0x02f40611,
+/* 0x0855: ctx_xfer_pre */
+ 0x7e100f0c,
+ 0xf40006b3,
+/* 0x085e: ctx_xfer_pre_load */
+ 0x020f1b11,
+ 0x0006627e,
+ 0x0006717e,
+ 0x0006837e,
+ 0x627ef4bd,
+ 0xea7e0006,
+/* 0x0876: ctx_xfer_exec */
+ 0x01980006,
+ 0x8024bd16,
+ 0xf6010500,
+ 0x04bd0002,
+ 0x008e1fb2,
+ 0x8f7e41a5,
+ 0xfcf00000,
+ 0x022cf001,
+ 0xfd0124b6,
+ 0xffb205f2,
+ 0x41a5048e,
+ 0x00008f7e,
+ 0x0002167e,
+ 0xfc8024bd,
+ 0x02f60247,
+ 0xf004bd00,
+ 0x20b6012c,
+ 0x4afc8003,
+ 0x0002f602,
+ 0xacf004bd,
+ 0x06a5f001,
+ 0x0c98000b,
+ 0x010d9800,
+ 0x3d7e000e,
+ 0x080a0001,
+ 0x0000ec7e,
+ 0x00020a7e,
+ 0x0a1201f4,
+ 0x00b87e0c,
+ 0x7e050f00,
+ 0xf40006d2,
+/* 0x08f2: ctx_xfer_post */
+ 0x020f2d02,
+ 0x0006627e,
+ 0xb37ef4bd,
+ 0x277e0006,
+ 0x717e0002,
+ 0xf4bd0006,
+ 0x0006627e,
+ 0x981011f4,
+ 0x11fd4001,
+ 0x070bf405,
+ 0x0007e87e,
+/* 0x091c: ctx_xfer_no_post_mmio */
+/* 0x091c: ctx_xfer_done */
+ 0x000000f8,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h
index 4750984bf380..64dfd75192bf 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnv108.fuc5.h
@@ -342,7 +342,7 @@ uint32_t nv108_grhub_code[] = {
0xb4f000bb,
0x10b4b01f,
0x0af31bf4,
- 0x00b87e02,
+ 0x00b87e05,
0x250ef400,
/* 0x01d8: mmctx_stop */
0xb600abc8,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h
index 132f684b1946..f8f7b278a13f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h
@@ -361,7 +361,7 @@ uint32_t nvc0_grhub_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h
index 84af82418987..624215a005b0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h
@@ -361,7 +361,7 @@ uint32_t nvd7_grhub_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h
index 1c179bdd48cc..6547b3dfc7ed 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h
@@ -361,7 +361,7 @@ uint32_t nve0_grhub_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h
index 229c0ae37228..a5aee5a4302f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h
@@ -361,7 +361,7 @@ uint32_t nvf0_grhub_code[] = {
0x1fb4f000,
0xf410b4b0,
0xa7f0f01b,
- 0xd021f402,
+ 0xd021f405,
/* 0x0223: mmctx_stop */
0xc82b0ef4,
0xb4b600ab,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc
index 6ffe28307dbd..a47d49db5232 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc
@@ -132,6 +132,7 @@
#define NV_PGRAPH_GPCX_GPCCS_FIFO_CMD 0x41a068
#define NV_PGRAPH_GPCX_GPCCS_FIFO_ACK 0x41a074
#define NV_PGRAPH_GPCX_GPCCS_UNITS 0x41a608
+#define NV_PGRAPH_GPCX_GPCCS_CAPS 0x41a108
#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH 0x41a614
#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_UNK11 0x00000800
#define NV_PGRAPH_GPCX_GPCCS_RED_SWITCH_ENABLE 0x00000200
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c b/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c
new file mode 100644
index 000000000000..21c5f31d607f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/P0260.h>
+
+#include "nvc0.h"
+#include "ctxnvc0.h"
+
+/*******************************************************************************
+ * Graphics object classes
+ ******************************************************************************/
+
+static struct nouveau_oclass
+gm107_graph_sclass[] = {
+ { 0x902d, &nouveau_object_ofuncs },
+ { 0xa140, &nouveau_object_ofuncs },
+ { 0xb097, &nouveau_object_ofuncs },
+ { 0xb0c0, &nouveau_object_ofuncs },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+gm107_graph_init_main_0[] = {
+ { 0x400080, 1, 0x04, 0x003003c2 },
+ { 0x400088, 1, 0x04, 0x0001bfe7 },
+ { 0x40008c, 1, 0x04, 0x00060000 },
+ { 0x400090, 1, 0x04, 0x00000030 },
+ { 0x40013c, 1, 0x04, 0x003901f3 },
+ { 0x400140, 1, 0x04, 0x00000100 },
+ { 0x400144, 1, 0x04, 0x00000000 },
+ { 0x400148, 1, 0x04, 0x00000110 },
+ { 0x400138, 1, 0x04, 0x00000000 },
+ { 0x400130, 2, 0x04, 0x00000000 },
+ { 0x400124, 1, 0x04, 0x00000002 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_ds_0[] = {
+ { 0x405844, 1, 0x04, 0x00ffffff },
+ { 0x405850, 1, 0x04, 0x00000000 },
+ { 0x405900, 1, 0x04, 0x00000000 },
+ { 0x405908, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_scc_0[] = {
+ { 0x40803c, 1, 0x04, 0x00000010 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_sked_0[] = {
+ { 0x407010, 1, 0x04, 0x00000000 },
+ { 0x407040, 1, 0x04, 0x40440424 },
+ { 0x407048, 1, 0x04, 0x0000000a },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_prop_0[] = {
+ { 0x418408, 1, 0x04, 0x00000000 },
+ { 0x4184a0, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_setup_1[] = {
+ { 0x4188c8, 2, 0x04, 0x00000000 },
+ { 0x4188d0, 1, 0x04, 0x00010000 },
+ { 0x4188d4, 1, 0x04, 0x00010201 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_zcull_0[] = {
+ { 0x418910, 1, 0x04, 0x00010001 },
+ { 0x418914, 1, 0x04, 0x00000301 },
+ { 0x418918, 1, 0x04, 0x00800000 },
+ { 0x418930, 2, 0x04, 0x00000000 },
+ { 0x418980, 1, 0x04, 0x77777770 },
+ { 0x418984, 3, 0x04, 0x77777777 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_gpc_unk_1[] = {
+ { 0x418d00, 1, 0x04, 0x00000000 },
+ { 0x418f00, 1, 0x04, 0x00000400 },
+ { 0x418f08, 1, 0x04, 0x00000000 },
+ { 0x418e08, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_tpccs_0[] = {
+ { 0x419dc4, 1, 0x04, 0x00000000 },
+ { 0x419dc8, 1, 0x04, 0x00000501 },
+ { 0x419dd0, 1, 0x04, 0x00000000 },
+ { 0x419dd4, 1, 0x04, 0x00000100 },
+ { 0x419dd8, 1, 0x04, 0x00000001 },
+ { 0x419ddc, 1, 0x04, 0x00000002 },
+ { 0x419de0, 1, 0x04, 0x00000001 },
+ { 0x419d0c, 1, 0x04, 0x00000000 },
+ { 0x419d10, 1, 0x04, 0x00000014 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_tex_0[] = {
+ { 0x419ab0, 1, 0x04, 0x00000000 },
+ { 0x419ab8, 1, 0x04, 0x000000e7 },
+ { 0x419abc, 1, 0x04, 0x00000000 },
+ { 0x419acc, 1, 0x04, 0x000000ff },
+ { 0x419ac0, 1, 0x04, 0x00000000 },
+ { 0x419aa8, 2, 0x04, 0x00000000 },
+ { 0x419ad0, 2, 0x04, 0x00000000 },
+ { 0x419ae0, 2, 0x04, 0x00000000 },
+ { 0x419af0, 4, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_pe_0[] = {
+ { 0x419900, 1, 0x04, 0x000000ff },
+ { 0x41980c, 1, 0x04, 0x00000010 },
+ { 0x419844, 1, 0x04, 0x00000000 },
+ { 0x419838, 1, 0x04, 0x000000ff },
+ { 0x419850, 1, 0x04, 0x00000004 },
+ { 0x419854, 2, 0x04, 0x00000000 },
+ { 0x419894, 3, 0x04, 0x00100401 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_l1c_0[] = {
+ { 0x419c98, 1, 0x04, 0x00000000 },
+ { 0x419cc0, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_sm_0[] = {
+ { 0x419e30, 1, 0x04, 0x000000ff },
+ { 0x419e00, 1, 0x04, 0x00000000 },
+ { 0x419ea0, 1, 0x04, 0x00000000 },
+ { 0x419ee4, 1, 0x04, 0x00000000 },
+ { 0x419ea4, 1, 0x04, 0x00000100 },
+ { 0x419ea8, 1, 0x04, 0x01000000 },
+ { 0x419ee8, 1, 0x04, 0x00000091 },
+ { 0x419eb4, 1, 0x04, 0x00000000 },
+ { 0x419ebc, 2, 0x04, 0x00000000 },
+ { 0x419edc, 1, 0x04, 0x000c1810 },
+ { 0x419ed8, 1, 0x04, 0x00000000 },
+ { 0x419ee0, 1, 0x04, 0x00000000 },
+ { 0x419f74, 1, 0x04, 0x00005155 },
+ { 0x419f80, 4, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_l1c_1[] = {
+ { 0x419ccc, 2, 0x04, 0x00000000 },
+ { 0x419c80, 1, 0x04, 0x3f006022 },
+ { 0x419c88, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_pes_0[] = {
+ { 0x41be50, 1, 0x04, 0x000000ff },
+ { 0x41be04, 1, 0x04, 0x00000000 },
+ { 0x41be08, 1, 0x04, 0x00000004 },
+ { 0x41be0c, 1, 0x04, 0x00000008 },
+ { 0x41be10, 1, 0x04, 0x0e3b8bc7 },
+ { 0x41be14, 2, 0x04, 0x00000000 },
+ { 0x41be3c, 5, 0x04, 0x00100401 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_wwdx_0[] = {
+ { 0x41bfd4, 1, 0x04, 0x00800000 },
+ { 0x41bfdc, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_cbm_0[] = {
+ { 0x41becc, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_be_0[] = {
+ { 0x408890, 1, 0x04, 0x000000ff },
+ { 0x40880c, 1, 0x04, 0x00000000 },
+ { 0x408850, 1, 0x04, 0x00000004 },
+ { 0x408878, 1, 0x04, 0x00c81603 },
+ { 0x40887c, 1, 0x04, 0x80543432 },
+ { 0x408880, 1, 0x04, 0x0010581e },
+ { 0x408884, 1, 0x04, 0x00001205 },
+ { 0x408974, 1, 0x04, 0x000000ff },
+ { 0x408910, 9, 0x04, 0x00000000 },
+ { 0x408950, 1, 0x04, 0x00000000 },
+ { 0x408954, 1, 0x04, 0x0000ffff },
+ { 0x408958, 1, 0x04, 0x00000034 },
+ { 0x40895c, 1, 0x04, 0x8531a003 },
+ { 0x408960, 1, 0x04, 0x0561985a },
+ { 0x408964, 1, 0x04, 0x04e15c4f },
+ { 0x408968, 1, 0x04, 0x02808833 },
+ { 0x40896c, 1, 0x04, 0x01f02438 },
+ { 0x408970, 1, 0x04, 0x00012c00 },
+ { 0x408984, 1, 0x04, 0x00000000 },
+ { 0x408988, 1, 0x04, 0x08040201 },
+ { 0x40898c, 1, 0x04, 0x80402010 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gm107_graph_init_sm_1[] = {
+ { 0x419e5c, 1, 0x04, 0x00000000 },
+ { 0x419e58, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gm107_graph_pack_mmio[] = {
+ { gm107_graph_init_main_0 },
+ { nvf0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvc0_graph_init_pd_0 },
+ { gm107_graph_init_ds_0 },
+ { gm107_graph_init_scc_0 },
+ { gm107_graph_init_sked_0 },
+ { nvf0_graph_init_cwd_0 },
+ { gm107_graph_init_prop_0 },
+ { nv108_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { gm107_graph_init_setup_1 },
+ { gm107_graph_init_zcull_0 },
+ { nvc0_graph_init_gpm_0 },
+ { gm107_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { gm107_graph_init_tpccs_0 },
+ { gm107_graph_init_tex_0 },
+ { gm107_graph_init_pe_0 },
+ { gm107_graph_init_l1c_0 },
+ { nvc0_graph_init_mpc_0 },
+ { gm107_graph_init_sm_0 },
+ { gm107_graph_init_l1c_1 },
+ { gm107_graph_init_pes_0 },
+ { gm107_graph_init_wwdx_0 },
+ { gm107_graph_init_cbm_0 },
+ { gm107_graph_init_be_0 },
+ { gm107_graph_init_sm_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+static void
+gm107_graph_init_bios(struct nvc0_graph_priv *priv)
+{
+ static const struct {
+ u32 ctrl;
+ u32 data;
+ } regs[] = {
+ { 0x419ed8, 0x419ee0 },
+ { 0x419ad0, 0x419ad4 },
+ { 0x419ae0, 0x419ae4 },
+ { 0x419af0, 0x419af4 },
+ { 0x419af8, 0x419afc },
+ };
+ struct nouveau_bios *bios = nouveau_bios(priv);
+ struct nvbios_P0260E infoE;
+ struct nvbios_P0260X infoX;
+ int E = -1, X;
+ u8 ver, hdr;
+
+ while (nvbios_P0260Ep(bios, ++E, &ver, &hdr, &infoE)) {
+ if (X = -1, E < ARRAY_SIZE(regs)) {
+ nv_wr32(priv, regs[E].ctrl, infoE.data);
+ while (nvbios_P0260Xp(bios, ++X, &ver, &hdr, &infoX))
+ nv_wr32(priv, regs[E].data, infoX.data);
+ }
+ }
+}
+
+int
+gm107_graph_init(struct nouveau_object *object)
+{
+ struct nvc0_graph_oclass *oclass = (void *)object->oclass;
+ struct nvc0_graph_priv *priv = (void *)object;
+ const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total);
+ u32 data[TPC_MAX / 8] = {};
+ u8 tpcnr[GPC_MAX];
+ int gpc, tpc, ppc, rop;
+ int ret, i;
+
+ ret = nouveau_graph_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000);
+ nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000);
+ nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000);
+ nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8);
+ nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8);
+
+ nvc0_graph_mmio(priv, oclass->mmio);
+
+ gm107_graph_init_bios(priv);
+
+ nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001);
+
+ memset(data, 0x00, sizeof(data));
+ memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+ for (i = 0, gpc = -1; i < priv->tpc_total; i++) {
+ do {
+ gpc = (gpc + 1) % priv->gpc_nr;
+ } while (!tpcnr[gpc]);
+ tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
+
+ data[i / 8] |= tpc << ((i % 8) * 4);
+ }
+
+ nv_wr32(priv, GPC_BCAST(0x0980), data[0]);
+ nv_wr32(priv, GPC_BCAST(0x0984), data[1]);
+ nv_wr32(priv, GPC_BCAST(0x0988), data[2]);
+ nv_wr32(priv, GPC_BCAST(0x098c), data[3]);
+
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0914),
+ priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 |
+ priv->tpc_total);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918);
+ }
+
+ nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918);
+ nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800));
+
+ nv_wr32(priv, 0x400500, 0x00010001);
+
+ nv_wr32(priv, 0x400100, 0xffffffff);
+ nv_wr32(priv, 0x40013c, 0xffffffff);
+ nv_wr32(priv, 0x400124, 0x00000002);
+ nv_wr32(priv, 0x409c24, 0x000e0000);
+
+ nv_wr32(priv, 0x404000, 0xc0000000);
+ nv_wr32(priv, 0x404600, 0xc0000000);
+ nv_wr32(priv, 0x408030, 0xc0000000);
+ nv_wr32(priv, 0x404490, 0xc0000000);
+ nv_wr32(priv, 0x406018, 0xc0000000);
+ nv_wr32(priv, 0x407020, 0x40000000);
+ nv_wr32(priv, 0x405840, 0xc0000000);
+ nv_wr32(priv, 0x405844, 0x00ffffff);
+ nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008);
+
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ for (ppc = 0; ppc < 2 /* priv->ppc_nr[gpc] */; ppc++)
+ nv_wr32(priv, PPC_UNIT(gpc, ppc, 0x038), 0xc0000000);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000);
+ for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x430), 0xc0000000);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x00dffffe);
+ nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x00000005);
+ }
+ nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff);
+ nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff);
+ }
+
+ for (rop = 0; rop < priv->rop_nr; rop++) {
+ nv_wr32(priv, ROP_UNIT(rop, 0x144), 0x40000000);
+ nv_wr32(priv, ROP_UNIT(rop, 0x070), 0x40000000);
+ nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff);
+ nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff);
+ }
+
+ nv_wr32(priv, 0x400108, 0xffffffff);
+ nv_wr32(priv, 0x400138, 0xffffffff);
+ nv_wr32(priv, 0x400118, 0xffffffff);
+ nv_wr32(priv, 0x400130, 0xffffffff);
+ nv_wr32(priv, 0x40011c, 0xffffffff);
+ nv_wr32(priv, 0x400134, 0xffffffff);
+
+ nv_wr32(priv, 0x400054, 0x2c350f63);
+ return nvc0_graph_init_ctxctl(priv);
+}
+
+#include "fuc/hubgm107.fuc5.h"
+
+static struct nvc0_graph_ucode
+gm107_graph_fecs_ucode = {
+ .code.data = gm107_grhub_code,
+ .code.size = sizeof(gm107_grhub_code),
+ .data.data = gm107_grhub_data,
+ .data.size = sizeof(gm107_grhub_data),
+};
+
+#include "fuc/gpcgm107.fuc5.h"
+
+static struct nvc0_graph_ucode
+gm107_graph_gpccs_ucode = {
+ .code.data = gm107_grgpc_code,
+ .code.size = sizeof(gm107_grgpc_code),
+ .data.data = gm107_grgpc_data,
+ .data.size = sizeof(gm107_grgpc_data),
+};
+
+struct nouveau_oclass *
+gm107_graph_oclass = &(struct nvc0_graph_oclass) {
+ .base.handle = NV_ENGINE(GR, 0x07),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_graph_ctor,
+ .dtor = nvc0_graph_dtor,
+ .init = gm107_graph_init,
+ .fini = _nouveau_graph_fini,
+ },
+ .cclass = &gm107_grctx_oclass,
+ .sclass = gm107_graph_sclass,
+ .mmio = gm107_graph_pack_mmio,
+ .fecs.ucode = 0 ? &gm107_graph_fecs_ucode : NULL,
+ .gpccs.ucode = &gm107_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c
index e1af65ead379..00ea1a089822 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c
@@ -23,6 +23,7 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
* Graphics object classes
@@ -38,11 +39,11 @@ nv108_graph_sclass[] = {
};
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-static struct nvc0_graph_init
-nv108_graph_init_regs[] = {
+static const struct nvc0_graph_init
+nv108_graph_init_main_0[] = {
{ 0x400080, 1, 0x04, 0x003083c2 },
{ 0x400088, 1, 0x04, 0x0001bfe7 },
{ 0x40008c, 1, 0x04, 0x00000000 },
@@ -57,66 +58,46 @@ nv108_graph_init_regs[] = {
{}
};
-struct nvc0_graph_init
-nv108_graph_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nv108_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
{ 0x405900, 1, 0x04, 0x00000000 },
{ 0x405908, 1, 0x04, 0x00000000 },
- { 0x405928, 1, 0x04, 0x00000000 },
- { 0x40592c, 1, 0x04, 0x00000000 },
+ { 0x405928, 2, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nv108_graph_init_gpc[] = {
- { 0x418408, 1, 0x04, 0x00000000 },
- { 0x4184a0, 3, 0x04, 0x00000000 },
+const struct nvc0_graph_init
+nv108_graph_init_gpc_unk_0[] = {
{ 0x418604, 1, 0x04, 0x00000000 },
{ 0x418680, 1, 0x04, 0x00000000 },
{ 0x418714, 1, 0x04, 0x00000000 },
{ 0x418384, 2, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_graph_init_setup_1[] = {
{ 0x4188c8, 2, 0x04, 0x00000000 },
{ 0x4188d0, 1, 0x04, 0x00010000 },
{ 0x4188d4, 1, 0x04, 0x00000201 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
- { 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c64, 2, 0x04, 0x00000000 },
- { 0x418c88, 1, 0x04, 0x00000000 },
- { 0x418cb4, 2, 0x04, 0x00000000 },
- { 0x418d00, 1, 0x04, 0x00000000 },
- { 0x418d28, 2, 0x04, 0x00000000 },
- { 0x418f00, 1, 0x04, 0x00000400 },
- { 0x418f08, 1, 0x04, 0x00000000 },
- { 0x418f20, 2, 0x04, 0x00000000 },
- { 0x418e00, 1, 0x04, 0x00000000 },
- { 0x418e08, 1, 0x04, 0x00000000 },
- { 0x418e1c, 2, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nv108_graph_init_tpc[] = {
- { 0x419d0c, 1, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
+static const struct nvc0_graph_init
+nv108_graph_init_tex_0[] = {
{ 0x419ab0, 1, 0x04, 0x00000000 },
{ 0x419ac8, 1, 0x04, 0x00000000 },
{ 0x419ab8, 1, 0x04, 0x000000e7 },
{ 0x419abc, 2, 0x04, 0x00000000 },
{ 0x419ab4, 1, 0x04, 0x00000000 },
{ 0x419aa8, 2, 0x04, 0x00000000 },
- { 0x41980c, 1, 0x04, 0x00000010 },
- { 0x419844, 1, 0x04, 0x00000000 },
- { 0x419850, 1, 0x04, 0x00000004 },
- { 0x419854, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nv108_graph_init_l1c_0[] = {
{ 0x419c98, 1, 0x04, 0x00000000 },
{ 0x419ca8, 1, 0x04, 0x00000000 },
{ 0x419cb0, 1, 0x04, 0x01000000 },
@@ -127,22 +108,47 @@ nv108_graph_init_tpc[] = {
{ 0x419cc0, 2, 0x04, 0x00000000 },
{ 0x419c80, 1, 0x04, 0x00000230 },
{ 0x419ccc, 2, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
- { 0x419e00, 1, 0x04, 0x00000080 },
- { 0x419ea0, 1, 0x04, 0x00000000 },
- { 0x419ee4, 1, 0x04, 0x00000000 },
- { 0x419ea4, 1, 0x04, 0x00000100 },
- { 0x419ea8, 1, 0x04, 0x00000000 },
- { 0x419eb4, 1, 0x04, 0x00000000 },
- { 0x419ebc, 2, 0x04, 0x00000000 },
- { 0x419edc, 1, 0x04, 0x00000000 },
- { 0x419f00, 1, 0x04, 0x00000000 },
- { 0x419ed0, 1, 0x04, 0x00003234 },
- { 0x419f74, 1, 0x04, 0x00015555 },
- { 0x419f80, 4, 0x04, 0x00000000 },
{}
};
+static const struct nvc0_graph_pack
+nv108_graph_pack_mmio[] = {
+ { nv108_graph_init_main_0 },
+ { nvf0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvd9_graph_init_pd_0 },
+ { nv108_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvf0_graph_init_sked_0 },
+ { nvf0_graph_init_cwd_0 },
+ { nvd9_graph_init_prop_0 },
+ { nv108_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nv108_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvd9_graph_init_gpm_0 },
+ { nvf0_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nve4_graph_init_tpccs_0 },
+ { nv108_graph_init_tex_0 },
+ { nve4_graph_init_pe_0 },
+ { nv108_graph_init_l1c_0 },
+ { nvc0_graph_init_mpc_0 },
+ { nvf0_graph_init_sm_0 },
+ { nvd7_graph_init_pes_0 },
+ { nvd7_graph_init_wwdx_0 },
+ { nvd7_graph_init_cbm_0 },
+ { nve4_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
static int
nv108_graph_fini(struct nouveau_object *object, bool suspend)
{
@@ -180,25 +186,6 @@ nv108_graph_fini(struct nouveau_object *object, bool suspend)
return nouveau_graph_fini(&priv->base, suspend);
}
-static struct nvc0_graph_init *
-nv108_graph_init_mmio[] = {
- nv108_graph_init_regs,
- nvf0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvd9_graph_init_unk64xx,
- nv108_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvf0_graph_init_unk70xx,
- nvf0_graph_init_unk5bxx,
- nv108_graph_init_gpc,
- nv108_graph_init_tpc,
- nve4_graph_init_unk,
- nve4_graph_init_unk88xx,
- NULL
-};
-
#include "fuc/hubnv108.fuc5.h"
static struct nvc0_graph_ucode
@@ -230,7 +217,7 @@ nv108_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nv108_grctx_oclass,
.sclass = nv108_graph_sclass,
- .mmio = nv108_graph_init_mmio,
+ .mmio = nv108_graph_pack_mmio,
.fecs.ucode = &nv108_graph_fecs_ucode,
.gpccs.ucode = &nv108_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
index b24559315903..d145e080899a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
@@ -349,7 +349,7 @@ nv20_graph_init(struct nouveau_object *object)
nv_wr32(priv, NV10_PGRAPH_SURFACE, tmp);
/* begin RAM config */
- vramsz = pci_resource_len(nv_device(priv)->pdev, 0) - 1;
+ vramsz = nv_device_resource_len(nv_device(priv), 0) - 1;
nv_wr32(priv, 0x4009A4, nv_rd32(priv, 0x100200));
nv_wr32(priv, 0x4009A8, nv_rd32(priv, 0x100204));
nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
index 193a5de1b482..6477fbf6a550 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
@@ -484,7 +484,7 @@ nv40_graph_init(struct nouveau_object *object)
engine->tile_prog(engine, i);
/* begin RAM config */
- vramsz = pci_resource_len(nv_device(priv)->pdev, 0) - 1;
+ vramsz = nv_device_resource_len(nv_device(priv), 0) - 1;
switch (nv_device(priv)->chipset) {
case 0x40:
nv_wr32(priv, 0x4009A4, nv_rd32(priv, 0x100200));
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
index 7a367c402978..2c7809e1a09b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
@@ -197,34 +197,35 @@ static const struct nouveau_bitfield nv50_pgraph_status[] = {
{ 0x00000080, "UNK7" },
{ 0x00000100, "CTXPROG" },
{ 0x00000200, "VFETCH" },
- { 0x00000400, "CCACHE_UNK4" },
- { 0x00000800, "STRMOUT_GSCHED_UNK5" },
- { 0x00001000, "UNK14XX" },
- { 0x00002000, "UNK24XX_CSCHED" },
- { 0x00004000, "UNK1CXX" },
+ { 0x00000400, "CCACHE_PREGEOM" },
+ { 0x00000800, "STRMOUT_VATTR_POSTGEOM" },
+ { 0x00001000, "VCLIP" },
+ { 0x00002000, "RATTR_APLANE" },
+ { 0x00004000, "TRAST" },
{ 0x00008000, "CLIPID" },
{ 0x00010000, "ZCULL" },
{ 0x00020000, "ENG2D" },
- { 0x00040000, "UNK34XX" },
- { 0x00080000, "TPRAST" },
- { 0x00100000, "TPROP" },
- { 0x00200000, "TEX" },
- { 0x00400000, "TPVP" },
- { 0x00800000, "MP" },
+ { 0x00040000, "RMASK" },
+ { 0x00080000, "TPC_RAST" },
+ { 0x00100000, "TPC_PROP" },
+ { 0x00200000, "TPC_TEX" },
+ { 0x00400000, "TPC_GEOM" },
+ { 0x00800000, "TPC_MP" },
{ 0x01000000, "ROP" },
{}
};
static const char *const nv50_pgraph_vstatus_0[] = {
- "VFETCH", "CCACHE", "UNK4", "UNK5", "GSCHED", "STRMOUT", "UNK14XX", NULL
+ "VFETCH", "CCACHE", "PREGEOM", "POSTGEOM", "VATTR", "STRMOUT", "VCLIP",
+ NULL
};
static const char *const nv50_pgraph_vstatus_1[] = {
- "TPRAST", "TPROP", "TEXTURE", "TPVP", "MP", NULL
+ "TPC_RAST", "TPC_PROP", "TPC_TEX", "TPC_GEOM", "TPC_MP", NULL
};
static const char *const nv50_pgraph_vstatus_2[] = {
- "UNK24XX", "CSCHED", "UNK1CXX", "CLIPID", "ZCULL", "ENG2D", "UNK34XX",
+ "RATTR", "APLANE", "TRAST", "CLIPID", "ZCULL", "ENG2D", "RMASK",
"ROP", NULL
};
@@ -329,6 +330,15 @@ static const struct nouveau_bitfield nv50_mpc_traps[] = {
{}
};
+static const struct nouveau_bitfield nv50_tex_traps[] = {
+ { 0x00000001, "" }, /* any bit set? */
+ { 0x00000002, "FAULT" },
+ { 0x00000004, "STORAGE_TYPE_MISMATCH" },
+ { 0x00000008, "LINEAR_MISMATCH" },
+ { 0x00000020, "WRONG_MEMTYPE" },
+ {}
+};
+
static const struct nouveau_bitfield nv50_graph_trap_m2mf[] = {
{ 0x00000001, "NOTIFY" },
{ 0x00000002, "IN" },
@@ -531,6 +541,13 @@ nv50_priv_tp_trap(struct nv50_graph_priv *priv, int type, u32 ustatus_old,
for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4)
nv_error(priv, "\t0x%08x: 0x%08x\n", r,
nv_rd32(priv, r));
+ if (ustatus) {
+ nv_error(priv, "%s - TP%d:", name, i);
+ nouveau_bitfield_print(nv50_tex_traps,
+ ustatus);
+ pr_cont("\n");
+ ustatus = 0;
+ }
}
break;
case 7: /* MP error */
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
index a73ab209ea88..f3c7329da0a0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
@@ -23,6 +23,7 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
* Graphics object classes
@@ -146,11 +147,11 @@ nvc0_graph_context_dtor(struct nouveau_object *object)
}
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-struct nvc0_graph_init
-nvc0_graph_init_regs[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_main_0[] = {
{ 0x400080, 1, 0x04, 0x003083c2 },
{ 0x400088, 1, 0x04, 0x00006fe7 },
{ 0x40008c, 1, 0x04, 0x00000000 },
@@ -165,95 +166,170 @@ nvc0_graph_init_regs[] = {
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk40xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_fe_0[] = {
{ 0x40415c, 1, 0x04, 0x00000000 },
{ 0x404170, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk44xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_pri_0[] = {
{ 0x404488, 2, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk78xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_rstr2d_0[] = {
{ 0x407808, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk60xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_pd_0[] = {
{ 0x406024, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk58xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
{ 0x405908, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk80xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_scc_0[] = {
{ 0x40803c, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_gpc[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_prop_0[] = {
{ 0x4184a0, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_gpc_unk_0[] = {
{ 0x418604, 1, 0x04, 0x00000000 },
{ 0x418680, 1, 0x04, 0x00000000 },
{ 0x418714, 1, 0x04, 0x80000000 },
{ 0x418384, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_setup_0[] = {
{ 0x418814, 3, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_crstr_0[] = {
{ 0x418b04, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_setup_1[] = {
{ 0x4188c8, 1, 0x04, 0x80000000 },
{ 0x4188cc, 1, 0x04, 0x00000000 },
{ 0x4188d0, 1, 0x04, 0x00010000 },
{ 0x4188d4, 1, 0x04, 0x00000001 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_zcull_0[] = {
{ 0x418910, 1, 0x04, 0x00010001 },
{ 0x418914, 1, 0x04, 0x00000301 },
{ 0x418918, 1, 0x04, 0x00800000 },
{ 0x418980, 1, 0x04, 0x77777770 },
{ 0x418984, 3, 0x04, 0x77777777 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_gpm_0[] = {
{ 0x418c04, 1, 0x04, 0x00000000 },
{ 0x418c88, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_gpc_unk_1[] = {
{ 0x418d00, 1, 0x04, 0x00000000 },
{ 0x418f08, 1, 0x04, 0x00000000 },
{ 0x418e00, 1, 0x04, 0x00000050 },
{ 0x418e08, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_gcc_0[] = {
{ 0x41900c, 1, 0x04, 0x00000000 },
{ 0x419018, 1, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvc0_graph_init_tpc[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_tpccs_0[] = {
{ 0x419d08, 2, 0x04, 0x00000000 },
{ 0x419d10, 1, 0x04, 0x00000014 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_tex_0[] = {
{ 0x419ab0, 1, 0x04, 0x00000000 },
{ 0x419ab8, 1, 0x04, 0x000000e7 },
{ 0x419abc, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_pe_0[] = {
{ 0x41980c, 3, 0x04, 0x00000000 },
{ 0x419844, 1, 0x04, 0x00000000 },
{ 0x41984c, 1, 0x04, 0x00005bc5 },
{ 0x419850, 4, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_l1c_0[] = {
{ 0x419c98, 1, 0x04, 0x00000000 },
{ 0x419ca8, 1, 0x04, 0x80000000 },
{ 0x419cb4, 1, 0x04, 0x00000000 },
{ 0x419cb8, 1, 0x04, 0x00008bf4 },
{ 0x419cbc, 1, 0x04, 0x28137606 },
{ 0x419cc0, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_wwdx_0[] = {
{ 0x419bd4, 1, 0x04, 0x00800000 },
{ 0x419bdc, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_tpccs_1[] = {
{ 0x419d2c, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc0_graph_init_mpc_0[] = {
{ 0x419c0c, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc0_graph_init_sm_0[] = {
{ 0x419e00, 1, 0x04, 0x00000000 },
{ 0x419ea0, 1, 0x04, 0x00000000 },
{ 0x419ea4, 1, 0x04, 0x00000100 },
@@ -270,8 +346,8 @@ nvc0_graph_init_tpc[] = {
{}
};
-struct nvc0_graph_init
-nvc0_graph_init_unk88xx[] = {
+const struct nvc0_graph_init
+nvc0_graph_init_be_0[] = {
{ 0x40880c, 1, 0x04, 0x00000000 },
{ 0x408910, 9, 0x04, 0x00000000 },
{ 0x408950, 1, 0x04, 0x00000000 },
@@ -282,18 +358,64 @@ nvc0_graph_init_unk88xx[] = {
{}
};
-struct nvc0_graph_init
-nvc0_graph_tpc_0[] = {
- { 0x50405c, 1, 0x04, 0x00000001 },
+const struct nvc0_graph_init
+nvc0_graph_init_fe_1[] = {
+ { 0x4040f0, 1, 0x04, 0x00000000 },
{}
};
+const struct nvc0_graph_init
+nvc0_graph_init_pe_1[] = {
+ { 0x419880, 1, 0x04, 0x00000002 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+nvc0_graph_pack_mmio[] = {
+ { nvc0_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvc0_graph_init_pd_0 },
+ { nvc0_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvc0_graph_init_prop_0 },
+ { nvc0_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc0_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvc0_graph_init_gpm_0 },
+ { nvc0_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nvc0_graph_init_tpccs_0 },
+ { nvc0_graph_init_tex_0 },
+ { nvc0_graph_init_pe_0 },
+ { nvc0_graph_init_l1c_0 },
+ { nvc0_graph_init_wwdx_0 },
+ { nvc0_graph_init_tpccs_1 },
+ { nvc0_graph_init_mpc_0 },
+ { nvc0_graph_init_sm_0 },
+ { nvc0_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ { nvc0_graph_init_pe_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
void
-nvc0_graph_mmio(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init)
+nvc0_graph_mmio(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p)
{
- for (; init && init->count; init++) {
- u32 addr = init->addr, i;
- for (i = 0; i < init->count; i++) {
+ const struct nvc0_graph_pack *pack;
+ const struct nvc0_graph_init *init;
+
+ pack_for_each_init(init, pack, p) {
+ u32 next = init->addr + init->count * init->pitch;
+ u32 addr = init->addr;
+ while (addr < next) {
nv_wr32(priv, addr, init->data);
addr += init->pitch;
}
@@ -301,49 +423,53 @@ nvc0_graph_mmio(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init)
}
void
-nvc0_graph_icmd(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init)
+nvc0_graph_icmd(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p)
{
- u32 addr, data;
- int i, j;
+ const struct nvc0_graph_pack *pack;
+ const struct nvc0_graph_init *init;
+ u32 data = 0;
nv_wr32(priv, 0x400208, 0x80000000);
- for (i = 0; init->count; init++, i++) {
- if (!i || data != init->data) {
+
+ pack_for_each_init(init, pack, p) {
+ u32 next = init->addr + init->count * init->pitch;
+ u32 addr = init->addr;
+
+ if ((pack == p && init == p->init) || data != init->data) {
nv_wr32(priv, 0x400204, init->data);
data = init->data;
}
- addr = init->addr;
- for (j = 0; j < init->count; j++) {
+ while (addr < next) {
nv_wr32(priv, 0x400200, addr);
+ nv_wait(priv, 0x400700, 0x00000002, 0x00000000);
addr += init->pitch;
- while (nv_rd32(priv, 0x400700) & 0x00000002) {}
}
}
+
nv_wr32(priv, 0x400208, 0x00000000);
}
void
-nvc0_graph_mthd(struct nvc0_graph_priv *priv, struct nvc0_graph_mthd *mthds)
+nvc0_graph_mthd(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p)
{
- struct nvc0_graph_mthd *mthd;
- struct nvc0_graph_init *init;
- int i = 0, j;
- u32 data;
-
- while ((mthd = &mthds[i++]) && (init = mthd->init)) {
- u32 addr = 0x80000000 | mthd->oclass;
- for (data = 0; init->count; init++) {
- if (init == mthd->init || data != init->data) {
- nv_wr32(priv, 0x40448c, init->data);
- data = init->data;
- }
+ const struct nvc0_graph_pack *pack;
+ const struct nvc0_graph_init *init;
+ u32 data = 0;
- addr = (addr & 0x8000ffff) | (init->addr << 14);
- for (j = 0; j < init->count; j++) {
- nv_wr32(priv, 0x404488, addr);
- addr += init->pitch << 14;
- }
+ pack_for_each_init(init, pack, p) {
+ u32 ctrl = 0x80000000 | pack->type;
+ u32 next = init->addr + init->count * init->pitch;
+ u32 addr = init->addr;
+
+ if ((pack == p && init == p->init) || data != init->data) {
+ nv_wr32(priv, 0x40448c, init->data);
+ data = init->data;
+ }
+
+ while (addr < next) {
+ nv_wr32(priv, 0x404488, ctrl | (addr << 14));
+ addr += init->pitch;
}
}
}
@@ -772,11 +898,12 @@ nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base,
static void
nvc0_graph_init_csdata(struct nvc0_graph_priv *priv,
- struct nvc0_graph_init *init,
+ const struct nvc0_graph_pack *pack,
u32 falcon, u32 starstar, u32 base)
{
- u32 addr = init->addr;
- u32 next = addr;
+ const struct nvc0_graph_pack *iter;
+ const struct nvc0_graph_init *init;
+ u32 addr = ~0, prev = ~0, xfer = 0;
u32 star, temp;
nv_wr32(priv, falcon + 0x01c0, 0x02000000 + starstar);
@@ -786,22 +913,28 @@ nvc0_graph_init_csdata(struct nvc0_graph_priv *priv,
star = temp;
nv_wr32(priv, falcon + 0x01c0, 0x01000000 + star);
- do {
- if (init->addr != next) {
- while (addr < next) {
- u32 nr = min((int)(next - addr) / 4, 32);
- nv_wr32(priv, falcon + 0x01c4,
- ((nr - 1) << 26) | (addr - base));
- addr += nr * 4;
- star += 4;
+ pack_for_each_init(init, iter, pack) {
+ u32 head = init->addr - base;
+ u32 tail = head + init->count * init->pitch;
+ while (head < tail) {
+ if (head != prev + 4 || xfer >= 32) {
+ if (xfer) {
+ u32 data = ((--xfer << 26) | addr);
+ nv_wr32(priv, falcon + 0x01c4, data);
+ star += 4;
+ }
+ addr = head;
+ xfer = 0;
}
- addr = next = init->addr;
+ prev = head;
+ xfer = xfer + 1;
+ head = head + init->pitch;
}
- next += init->count * 4;
- } while ((init++)->count);
+ }
+ nv_wr32(priv, falcon + 0x01c4, (--xfer << 26) | addr);
nv_wr32(priv, falcon + 0x01c0, 0x01000004 + starstar);
- nv_wr32(priv, falcon + 0x01c4, star);
+ nv_wr32(priv, falcon + 0x01c4, star + 4);
}
int
@@ -809,7 +942,6 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
{
struct nvc0_graph_oclass *oclass = (void *)nv_object(priv)->oclass;
struct nvc0_grctx_oclass *cclass = (void *)nv_engine(priv)->cclass;
- struct nvc0_graph_init *init;
u32 r000260;
int i;
@@ -919,10 +1051,6 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
nv_wr32(priv, 0x409184, oclass->fecs.ucode->code.data[i]);
}
- for (i = 0; (init = cclass->hub[i]); i++) {
- nvc0_graph_init_csdata(priv, init, 0x409000, 0x000, 0x000000);
- }
-
/* load GPC microcode */
nv_wr32(priv, 0x41a1c0, 0x01000000);
for (i = 0; i < oclass->gpccs.ucode->data.size / 4; i++)
@@ -936,12 +1064,11 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
}
nv_wr32(priv, 0x000260, r000260);
- if ((init = cclass->gpc[0]))
- nvc0_graph_init_csdata(priv, init, 0x41a000, 0x000, 0x418000);
- if ((init = cclass->gpc[2]))
- nvc0_graph_init_csdata(priv, init, 0x41a000, 0x004, 0x419800);
- if ((init = cclass->gpc[3]))
- nvc0_graph_init_csdata(priv, init, 0x41a000, 0x008, 0x41be00);
+ /* load register lists */
+ nvc0_graph_init_csdata(priv, cclass->hub, 0x409000, 0x000, 0x000000);
+ nvc0_graph_init_csdata(priv, cclass->gpc, 0x41a000, 0x000, 0x418000);
+ nvc0_graph_init_csdata(priv, cclass->tpc, 0x41a000, 0x004, 0x419800);
+ nvc0_graph_init_csdata(priv, cclass->ppc, 0x41a000, 0x008, 0x41be00);
/* start HUB ucode running, it'll init the GPCs */
nv_wr32(priv, 0x40910c, 0x00000000);
@@ -988,8 +1115,7 @@ nvc0_graph_init(struct nouveau_object *object)
nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8);
nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8);
- for (i = 0; oclass->mmio[i]; i++)
- nvc0_graph_mmio(priv, oclass->mmio[i]);
+ nvc0_graph_mmio(priv, oclass->mmio);
memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
for (i = 0, gpc = -1; i < priv->tpc_total; i++) {
@@ -1091,10 +1217,10 @@ nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname,
int ret;
snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname);
- ret = request_firmware(&fw, f, &device->pdev->dev);
+ ret = request_firmware(&fw, f, nv_device_base(device));
if (ret) {
snprintf(f, sizeof(f), "nouveau/%s", fwname);
- ret = request_firmware(&fw, f, &device->pdev->dev);
+ ret = request_firmware(&fw, f, nv_device_base(device));
if (ret) {
nv_error(priv, "failed to load %s\n", fwname);
return ret;
@@ -1220,22 +1346,6 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-struct nvc0_graph_init *
-nvc0_graph_init_mmio[] = {
- nvc0_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvc0_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvc0_graph_init_gpc,
- nvc0_graph_init_tpc,
- nvc0_graph_init_unk88xx,
- nvc0_graph_tpc_0,
- NULL
-};
-
#include "fuc/hubnvc0.fuc.h"
struct nvc0_graph_ucode
@@ -1267,7 +1377,7 @@ nvc0_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nvc0_grctx_oclass,
.sclass = nvc0_graph_sclass,
- .mmio = nvc0_graph_init_mmio,
+ .mmio = nvc0_graph_pack_mmio,
.fecs.ucode = &nvc0_graph_fecs_ucode,
.gpccs.ucode = &nvc0_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
index b0ab6de270b2..90d44616c876 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
@@ -45,6 +45,7 @@
#define ROP_UNIT(u, r) (0x410000 + (u) * 0x400 + (r))
#define GPC_BCAST(r) (0x418000 + (r))
#define GPC_UNIT(t, r) (0x500000 + (t) * 0x8000 + (r))
+#define PPC_UNIT(t, m, r) (0x503000 + (t) * 0x8000 + (m) * 0x200 + (r))
#define TPC_UNIT(t, m, r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r))
struct nvc0_graph_data {
@@ -102,8 +103,6 @@ struct nvc0_graph_chan {
} data[4];
};
-int nvc0_grctx_generate(struct nvc0_graph_priv *);
-
int nvc0_graph_context_ctor(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, void *, u32,
struct nouveau_object **);
@@ -130,34 +129,14 @@ struct nvc0_graph_init {
u32 data;
};
-struct nvc0_graph_mthd {
- u16 oclass;
- struct nvc0_graph_init *init;
-};
-
-struct nvc0_grctx {
- struct nvc0_graph_priv *priv;
- struct nvc0_graph_data *data;
- struct nvc0_graph_mmio *mmio;
- int buffer_nr;
- u64 buffer[4];
- u64 addr;
+struct nvc0_graph_pack {
+ const struct nvc0_graph_init *init;
+ u32 type;
};
-struct nvc0_grctx_oclass {
- struct nouveau_oclass base;
- /* main context generation function */
- void (*main)(struct nvc0_graph_priv *, struct nvc0_grctx *);
- /* context-specific modify-on-first-load list generation function */
- void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *);
- void (*unkn)(struct nvc0_graph_priv *);
- /* mmio context data */
- struct nvc0_graph_init **hub;
- struct nvc0_graph_init **gpc;
- /* indirect context data, generated with icmds/mthds */
- struct nvc0_graph_init *icmd;
- struct nvc0_graph_mthd *mthd;
-};
+#define pack_for_each_init(init, pack, head) \
+ for (pack = head; pack && pack->init; pack++) \
+ for (init = pack->init; init && init->count; init++)
struct nvc0_graph_ucode {
struct nvc0_graph_fuc code;
@@ -171,7 +150,7 @@ struct nvc0_graph_oclass {
struct nouveau_oclass base;
struct nouveau_oclass **cclass;
struct nouveau_oclass *sclass;
- struct nvc0_graph_init **mmio;
+ const struct nvc0_graph_pack *mmio;
struct {
struct nvc0_graph_ucode *ucode;
} fecs;
@@ -180,119 +159,72 @@ struct nvc0_graph_oclass {
} gpccs;
};
-void nvc0_graph_mmio(struct nvc0_graph_priv *, struct nvc0_graph_init *);
-void nvc0_graph_icmd(struct nvc0_graph_priv *, struct nvc0_graph_init *);
-void nvc0_graph_mthd(struct nvc0_graph_priv *, struct nvc0_graph_mthd *);
+void nvc0_graph_mmio(struct nvc0_graph_priv *, const struct nvc0_graph_pack *);
+void nvc0_graph_icmd(struct nvc0_graph_priv *, const struct nvc0_graph_pack *);
+void nvc0_graph_mthd(struct nvc0_graph_priv *, const struct nvc0_graph_pack *);
int nvc0_graph_init_ctxctl(struct nvc0_graph_priv *);
-extern struct nvc0_graph_init nvc0_graph_init_regs[];
-extern struct nvc0_graph_init nvc0_graph_init_unk40xx[];
-extern struct nvc0_graph_init nvc0_graph_init_unk44xx[];
-extern struct nvc0_graph_init nvc0_graph_init_unk78xx[];
-extern struct nvc0_graph_init nvc0_graph_init_unk60xx[];
-extern struct nvc0_graph_init nvc0_graph_init_unk58xx[];
-extern struct nvc0_graph_init nvc0_graph_init_unk80xx[];
-extern struct nvc0_graph_init nvc0_graph_init_gpc[];
-extern struct nvc0_graph_init nvc0_graph_init_unk88xx[];
-extern struct nvc0_graph_init nvc0_graph_tpc_0[];
-
-extern struct nvc0_graph_init nvc3_graph_init_unk58xx[];
-
-extern struct nvc0_graph_init nvd9_graph_init_unk58xx[];
-extern struct nvc0_graph_init nvd9_graph_init_unk64xx[];
-
-extern struct nvc0_graph_init nve4_graph_init_regs[];
-extern struct nvc0_graph_init nve4_graph_init_unk[];
-extern struct nvc0_graph_init nve4_graph_init_unk88xx[];
-
-extern struct nvc0_graph_init nvf0_graph_init_unk40xx[];
-extern struct nvc0_graph_init nvf0_graph_init_unk70xx[];
-extern struct nvc0_graph_init nvf0_graph_init_unk5bxx[];
-extern struct nvc0_graph_init nvf0_graph_init_tpc[];
-
-int nvc0_grctx_generate(struct nvc0_graph_priv *);
-void nvc0_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nvc0_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nvc0_grctx_generate_unkn(struct nvc0_graph_priv *);
-void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *);
-void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *);
-void nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *);
-void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *);
-void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *);
-void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *);
-
-extern struct nouveau_oclass *nvc0_grctx_oclass;
-extern struct nvc0_graph_init *nvc0_grctx_init_hub[];
-extern struct nvc0_graph_init nvc0_grctx_init_base[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk40xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk44xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk46xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk47xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk60xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk64xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk78xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_unk80xx[];
-extern struct nvc0_graph_init nvc0_grctx_init_gpc_0[];
-extern struct nvc0_graph_init nvc0_grctx_init_gpc_1[];
-extern struct nvc0_graph_init nvc0_grctx_init_tpc[];
-extern struct nvc0_graph_init nvc0_grctx_init_icmd[];
-extern struct nvc0_graph_init nvd9_grctx_init_icmd[]; //
-
-extern struct nvc0_graph_mthd nvc0_grctx_init_mthd[];
-extern struct nvc0_graph_init nvc0_grctx_init_902d[];
-extern struct nvc0_graph_init nvc0_grctx_init_9039[];
-extern struct nvc0_graph_init nvc0_grctx_init_90c0[];
-extern struct nvc0_graph_init nvc0_grctx_init_mthd_magic[];
-
-void nvc1_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nvc1_grctx_generate_unkn(struct nvc0_graph_priv *);
-extern struct nouveau_oclass *nvc1_grctx_oclass;
-extern struct nvc0_graph_init nvc1_grctx_init_9097[];
-
-extern struct nouveau_oclass *nvc3_grctx_oclass;
-
-extern struct nouveau_oclass *nvc8_grctx_oclass;
-extern struct nvc0_graph_init nvc8_grctx_init_9197[];
-extern struct nvc0_graph_init nvc8_grctx_init_9297[];
-
-extern struct nouveau_oclass *nvd7_grctx_oclass;
-
-extern struct nouveau_oclass *nvd9_grctx_oclass;
-extern struct nvc0_graph_init nvd9_grctx_init_rop[];
-extern struct nvc0_graph_mthd nvd9_grctx_init_mthd[];
-
-void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nve4_grctx_generate_unkn(struct nvc0_graph_priv *);
-extern struct nouveau_oclass *nve4_grctx_oclass;
-extern struct nvc0_graph_init nve4_grctx_init_unk46xx[];
-extern struct nvc0_graph_init nve4_grctx_init_unk47xx[];
-extern struct nvc0_graph_init nve4_grctx_init_unk58xx[];
-extern struct nvc0_graph_init nve4_grctx_init_unk80xx[];
-extern struct nvc0_graph_init nve4_grctx_init_unk90xx[];
-
-extern struct nouveau_oclass *nvf0_grctx_oclass;
-extern struct nvc0_graph_init nvf0_grctx_init_unk44xx[];
-extern struct nvc0_graph_init nvf0_grctx_init_unk5bxx[];
-extern struct nvc0_graph_init nvf0_grctx_init_unk60xx[];
-
-extern struct nouveau_oclass *nv108_grctx_oclass;
-
-#define mmio_data(s,a,p) do { \
- info->buffer[info->buffer_nr] = round_up(info->addr, (a)); \
- info->addr = info->buffer[info->buffer_nr++] + (s); \
- info->data->size = (s); \
- info->data->align = (a); \
- info->data->access = (p); \
- info->data++; \
-} while(0)
-
-#define mmio_list(r,d,s,b) do { \
- info->mmio->addr = (r); \
- info->mmio->data = (d); \
- info->mmio->shift = (s); \
- info->mmio->buffer = (b); \
- info->mmio++; \
- nv_wr32(priv, (r), (d) | ((s) ? (info->buffer[(b)] >> (s)) : 0)); \
-} while(0)
+/* register init value lists */
+
+extern const struct nvc0_graph_init nvc0_graph_init_main_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_fe_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_pri_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_rstr2d_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_pd_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_ds_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_scc_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_prop_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_gpc_unk_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_setup_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_crstr_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_setup_1[];
+extern const struct nvc0_graph_init nvc0_graph_init_zcull_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_gpm_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_gpc_unk_1[];
+extern const struct nvc0_graph_init nvc0_graph_init_gcc_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_tpccs_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_tex_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_pe_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_l1c_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_wwdx_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_tpccs_1[];
+extern const struct nvc0_graph_init nvc0_graph_init_mpc_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_be_0[];
+extern const struct nvc0_graph_init nvc0_graph_init_fe_1[];
+extern const struct nvc0_graph_init nvc0_graph_init_pe_1[];
+
+extern const struct nvc0_graph_init nvc4_graph_init_ds_0[];
+extern const struct nvc0_graph_init nvc4_graph_init_tex_0[];
+extern const struct nvc0_graph_init nvc4_graph_init_sm_0[];
+
+extern const struct nvc0_graph_init nvc1_graph_init_gpc_unk_0[];
+extern const struct nvc0_graph_init nvc1_graph_init_setup_1[];
+
+extern const struct nvc0_graph_init nvd9_graph_init_pd_0[];
+extern const struct nvc0_graph_init nvd9_graph_init_ds_0[];
+extern const struct nvc0_graph_init nvd9_graph_init_prop_0[];
+extern const struct nvc0_graph_init nvd9_graph_init_gpm_0[];
+extern const struct nvc0_graph_init nvd9_graph_init_gpc_unk_1[];
+extern const struct nvc0_graph_init nvd9_graph_init_tex_0[];
+extern const struct nvc0_graph_init nvd9_graph_init_sm_0[];
+extern const struct nvc0_graph_init nvd9_graph_init_fe_1[];
+
+extern const struct nvc0_graph_init nvd7_graph_init_pes_0[];
+extern const struct nvc0_graph_init nvd7_graph_init_wwdx_0[];
+extern const struct nvc0_graph_init nvd7_graph_init_cbm_0[];
+
+extern const struct nvc0_graph_init nve4_graph_init_main_0[];
+extern const struct nvc0_graph_init nve4_graph_init_tpccs_0[];
+extern const struct nvc0_graph_init nve4_graph_init_pe_0[];
+extern const struct nvc0_graph_init nve4_graph_init_be_0[];
+
+extern const struct nvc0_graph_init nvf0_graph_init_fe_0[];
+extern const struct nvc0_graph_init nvf0_graph_init_sked_0[];
+extern const struct nvc0_graph_init nvf0_graph_init_cwd_0[];
+extern const struct nvc0_graph_init nvf0_graph_init_gpc_unk_1[];
+extern const struct nvc0_graph_init nvf0_graph_init_sm_0[];
+
+extern const struct nvc0_graph_init nv108_graph_init_gpc_unk_0[];
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
index bc4a469b86cb..30cab0b2eba1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
@@ -23,6 +23,7 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
* Graphics object classes
@@ -39,94 +40,82 @@ nvc1_graph_sclass[] = {
};
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-static struct nvc0_graph_init
-nvc1_graph_init_gpc[] = {
- { 0x4184a0, 1, 0x04, 0x00000000 },
+const struct nvc0_graph_init
+nvc1_graph_init_gpc_unk_0[] = {
{ 0x418604, 1, 0x04, 0x00000000 },
{ 0x418680, 1, 0x04, 0x00000000 },
{ 0x418714, 1, 0x04, 0x00000000 },
{ 0x418384, 1, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc1_graph_init_setup_1[] = {
{ 0x4188c8, 2, 0x04, 0x00000000 },
{ 0x4188d0, 1, 0x04, 0x00010000 },
{ 0x4188d4, 1, 0x04, 0x00000001 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
- { 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c88, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc1_graph_init_gpc_unk_1[] = {
{ 0x418d00, 1, 0x04, 0x00000000 },
{ 0x418f08, 1, 0x04, 0x00000000 },
{ 0x418e00, 1, 0x04, 0x00000003 },
{ 0x418e08, 1, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvc1_graph_init_tpc[] = {
- { 0x419d08, 2, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
- { 0x419ab0, 1, 0x04, 0x00000000 },
- { 0x419ac8, 1, 0x04, 0x00000000 },
- { 0x419ab8, 1, 0x04, 0x000000e7 },
- { 0x419abc, 2, 0x04, 0x00000000 },
- { 0x41980c, 2, 0x04, 0x00000000 },
+static const struct nvc0_graph_init
+nvc1_graph_init_pe_0[] = {
+ { 0x41980c, 1, 0x04, 0x00000010 },
+ { 0x419810, 1, 0x04, 0x00000000 },
{ 0x419814, 1, 0x04, 0x00000004 },
{ 0x419844, 1, 0x04, 0x00000000 },
{ 0x41984c, 1, 0x04, 0x00005bc5 },
{ 0x419850, 4, 0x04, 0x00000000 },
{ 0x419880, 1, 0x04, 0x00000002 },
- { 0x419c98, 1, 0x04, 0x00000000 },
- { 0x419ca8, 1, 0x04, 0x80000000 },
- { 0x419cb4, 1, 0x04, 0x00000000 },
- { 0x419cb8, 1, 0x04, 0x00008bf4 },
- { 0x419cbc, 1, 0x04, 0x28137606 },
- { 0x419cc0, 2, 0x04, 0x00000000 },
- { 0x419bd4, 1, 0x04, 0x00800000 },
- { 0x419bdc, 1, 0x04, 0x00000000 },
- { 0x419d2c, 1, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
- { 0x419e00, 1, 0x04, 0x00000000 },
- { 0x419ea0, 1, 0x04, 0x00000000 },
- { 0x419ea4, 1, 0x04, 0x00000100 },
- { 0x419ea8, 1, 0x04, 0x00001100 },
- { 0x419eac, 1, 0x04, 0x11100702 },
- { 0x419eb0, 1, 0x04, 0x00000003 },
- { 0x419eb4, 4, 0x04, 0x00000000 },
- { 0x419ec8, 1, 0x04, 0x0e063818 },
- { 0x419ecc, 1, 0x04, 0x0e060e06 },
- { 0x419ed0, 1, 0x04, 0x00003818 },
- { 0x419ed4, 1, 0x04, 0x011104f1 },
- { 0x419edc, 1, 0x04, 0x00000000 },
- { 0x419f00, 1, 0x04, 0x00000000 },
- { 0x419f2c, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init *
-nvc1_graph_init_mmio[] = {
- nvc0_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvc3_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvc1_graph_init_gpc,
- nvc1_graph_init_tpc,
- nvc0_graph_init_unk88xx,
- nvc0_graph_tpc_0,
- NULL
+static const struct nvc0_graph_pack
+nvc1_graph_pack_mmio[] = {
+ { nvc0_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvc0_graph_init_pd_0 },
+ { nvc4_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvc0_graph_init_prop_0 },
+ { nvc1_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvc0_graph_init_gpm_0 },
+ { nvc1_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nvc0_graph_init_tpccs_0 },
+ { nvc4_graph_init_tex_0 },
+ { nvc1_graph_init_pe_0 },
+ { nvc0_graph_init_l1c_0 },
+ { nvc0_graph_init_wwdx_0 },
+ { nvc0_graph_init_tpccs_1 },
+ { nvc0_graph_init_mpc_0 },
+ { nvc4_graph_init_sm_0 },
+ { nvc0_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ {}
};
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
struct nouveau_oclass *
nvc1_graph_oclass = &(struct nvc0_graph_oclass) {
.base.handle = NV_ENGINE(GR, 0xc1),
@@ -138,7 +127,7 @@ nvc1_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nvc1_grctx_oclass,
.sclass = nvc1_graph_sclass,
- .mmio = nvc1_graph_init_mmio,
+ .mmio = nvc1_graph_pack_mmio,
.fecs.ucode = &nvc0_graph_fecs_ucode,
.gpccs.ucode = &nvc0_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc4.c
index d44b3b3ee800..e82e70c53132 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc4.c
@@ -23,13 +23,14 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-struct nvc0_graph_init
-nvc3_graph_init_unk58xx[] = {
+const struct nvc0_graph_init
+nvc4_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
{ 0x405900, 1, 0x04, 0x00002834 },
@@ -37,29 +38,27 @@ nvc3_graph_init_unk58xx[] = {
{}
};
-static struct nvc0_graph_init
-nvc3_graph_init_tpc[] = {
- { 0x419d08, 2, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
+const struct nvc0_graph_init
+nvc4_graph_init_tex_0[] = {
{ 0x419ab0, 1, 0x04, 0x00000000 },
{ 0x419ac8, 1, 0x04, 0x00000000 },
{ 0x419ab8, 1, 0x04, 0x000000e7 },
{ 0x419abc, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvc4_graph_init_pe_0[] = {
{ 0x41980c, 3, 0x04, 0x00000000 },
{ 0x419844, 1, 0x04, 0x00000000 },
{ 0x41984c, 1, 0x04, 0x00005bc5 },
{ 0x419850, 4, 0x04, 0x00000000 },
{ 0x419880, 1, 0x04, 0x00000002 },
- { 0x419c98, 1, 0x04, 0x00000000 },
- { 0x419ca8, 1, 0x04, 0x80000000 },
- { 0x419cb4, 1, 0x04, 0x00000000 },
- { 0x419cb8, 1, 0x04, 0x00008bf4 },
- { 0x419cbc, 1, 0x04, 0x28137606 },
- { 0x419cc0, 2, 0x04, 0x00000000 },
- { 0x419bd4, 1, 0x04, 0x00800000 },
- { 0x419bdc, 1, 0x04, 0x00000000 },
- { 0x419d2c, 1, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvc4_graph_init_sm_0[] = {
{ 0x419e00, 1, 0x04, 0x00000000 },
{ 0x419ea0, 1, 0x04, 0x00000000 },
{ 0x419ea4, 1, 0x04, 0x00000100 },
@@ -77,24 +76,43 @@ nvc3_graph_init_tpc[] = {
{}
};
-static struct nvc0_graph_init *
-nvc3_graph_init_mmio[] = {
- nvc0_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvc3_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvc0_graph_init_gpc,
- nvc3_graph_init_tpc,
- nvc0_graph_init_unk88xx,
- nvc0_graph_tpc_0,
- NULL
+static const struct nvc0_graph_pack
+nvc4_graph_pack_mmio[] = {
+ { nvc0_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvc0_graph_init_pd_0 },
+ { nvc4_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvc0_graph_init_prop_0 },
+ { nvc0_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc0_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvc0_graph_init_gpm_0 },
+ { nvc0_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nvc0_graph_init_tpccs_0 },
+ { nvc4_graph_init_tex_0 },
+ { nvc4_graph_init_pe_0 },
+ { nvc0_graph_init_l1c_0 },
+ { nvc0_graph_init_wwdx_0 },
+ { nvc0_graph_init_tpccs_1 },
+ { nvc0_graph_init_mpc_0 },
+ { nvc4_graph_init_sm_0 },
+ { nvc0_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ {}
};
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
struct nouveau_oclass *
-nvc3_graph_oclass = &(struct nvc0_graph_oclass) {
+nvc4_graph_oclass = &(struct nvc0_graph_oclass) {
.base.handle = NV_ENGINE(GR, 0xc3),
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvc0_graph_ctor,
@@ -102,9 +120,9 @@ nvc3_graph_oclass = &(struct nvc0_graph_oclass) {
.init = nvc0_graph_init,
.fini = _nouveau_graph_fini,
},
- .cclass = &nvc3_grctx_oclass,
+ .cclass = &nvc4_grctx_oclass,
.sclass = nvc0_graph_sclass,
- .mmio = nvc3_graph_init_mmio,
+ .mmio = nvc4_graph_pack_mmio,
.fecs.ucode = &nvc0_graph_fecs_ucode,
.gpccs.ucode = &nvc0_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
index 02845e567314..a6bf783e1256 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
@@ -23,6 +23,7 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
* Graphics object classes
@@ -40,58 +41,11 @@ nvc8_graph_sclass[] = {
};
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-static struct nvc0_graph_init
-nvc8_graph_init_gpc[] = {
- { 0x4184a0, 1, 0x04, 0x00000000 },
- { 0x418604, 1, 0x04, 0x00000000 },
- { 0x418680, 1, 0x04, 0x00000000 },
- { 0x418714, 1, 0x04, 0x80000000 },
- { 0x418384, 1, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
- { 0x4188c8, 2, 0x04, 0x00000000 },
- { 0x4188d0, 1, 0x04, 0x00010000 },
- { 0x4188d4, 1, 0x04, 0x00000001 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
- { 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c88, 1, 0x04, 0x00000000 },
- { 0x418d00, 1, 0x04, 0x00000000 },
- { 0x418f08, 1, 0x04, 0x00000000 },
- { 0x418e00, 1, 0x04, 0x00000050 },
- { 0x418e08, 1, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
- {}
-};
-
-static struct nvc0_graph_init
-nvc8_graph_init_tpc[] = {
- { 0x419d08, 2, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
- { 0x419ab0, 1, 0x04, 0x00000000 },
- { 0x419ab8, 1, 0x04, 0x000000e7 },
- { 0x419abc, 2, 0x04, 0x00000000 },
- { 0x41980c, 3, 0x04, 0x00000000 },
- { 0x419844, 1, 0x04, 0x00000000 },
- { 0x41984c, 1, 0x04, 0x00005bc5 },
- { 0x419850, 4, 0x04, 0x00000000 },
- { 0x419c98, 1, 0x04, 0x00000000 },
- { 0x419ca8, 1, 0x04, 0x80000000 },
- { 0x419cb4, 1, 0x04, 0x00000000 },
- { 0x419cb8, 1, 0x04, 0x00008bf4 },
- { 0x419cbc, 1, 0x04, 0x28137606 },
- { 0x419cc0, 2, 0x04, 0x00000000 },
- { 0x419bd4, 1, 0x04, 0x00800000 },
- { 0x419bdc, 1, 0x04, 0x00000000 },
- { 0x419d2c, 1, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
+static const struct nvc0_graph_init
+nvc8_graph_init_sm_0[] = {
{ 0x419e00, 1, 0x04, 0x00000000 },
{ 0x419ea0, 1, 0x04, 0x00000000 },
{ 0x419ea4, 1, 0x04, 0x00000100 },
@@ -108,22 +62,42 @@ nvc8_graph_init_tpc[] = {
{}
};
-static struct nvc0_graph_init *
-nvc8_graph_init_mmio[] = {
- nvc0_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvc0_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvc8_graph_init_gpc,
- nvc8_graph_init_tpc,
- nvc0_graph_init_unk88xx,
- nvc0_graph_tpc_0,
- NULL
+static const struct nvc0_graph_pack
+nvc8_graph_pack_mmio[] = {
+ { nvc0_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvc0_graph_init_pd_0 },
+ { nvc0_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvc0_graph_init_prop_0 },
+ { nvc0_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvc0_graph_init_gpm_0 },
+ { nvc0_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nvc0_graph_init_tpccs_0 },
+ { nvc0_graph_init_tex_0 },
+ { nvc0_graph_init_pe_0 },
+ { nvc0_graph_init_l1c_0 },
+ { nvc0_graph_init_wwdx_0 },
+ { nvc0_graph_init_tpccs_1 },
+ { nvc0_graph_init_mpc_0 },
+ { nvc8_graph_init_sm_0 },
+ { nvc0_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ { nvc0_graph_init_pe_1 },
+ {}
};
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
struct nouveau_oclass *
nvc8_graph_oclass = &(struct nvc0_graph_oclass) {
.base.handle = NV_ENGINE(GR, 0xc8),
@@ -135,7 +109,7 @@ nvc8_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nvc8_grctx_oclass,
.sclass = nvc8_graph_sclass,
- .mmio = nvc8_graph_init_mmio,
+ .mmio = nvc8_graph_pack_mmio,
.fecs.ucode = &nvc0_graph_fecs_ucode,
.gpccs.ucode = &nvc0_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
index 5052d7ab4d72..2a6a94e2a041 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
@@ -23,6 +23,77 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
+
+/*******************************************************************************
+ * PGRAPH register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+nvd7_graph_init_pe_0[] = {
+ { 0x41980c, 1, 0x04, 0x00000010 },
+ { 0x419844, 1, 0x04, 0x00000000 },
+ { 0x41984c, 1, 0x04, 0x00005bc8 },
+ { 0x419850, 3, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd7_graph_init_pes_0[] = {
+ { 0x41be04, 1, 0x04, 0x00000000 },
+ { 0x41be08, 1, 0x04, 0x00000004 },
+ { 0x41be0c, 1, 0x04, 0x00000000 },
+ { 0x41be10, 1, 0x04, 0x003b8bc7 },
+ { 0x41be14, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd7_graph_init_wwdx_0[] = {
+ { 0x41bfd4, 1, 0x04, 0x00800000 },
+ { 0x41bfdc, 1, 0x04, 0x00000000 },
+ { 0x41bff8, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd7_graph_init_cbm_0[] = {
+ { 0x41becc, 1, 0x04, 0x00000000 },
+ { 0x41bee8, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+nvd7_graph_pack_mmio[] = {
+ { nvc0_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvd9_graph_init_pd_0 },
+ { nvd9_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvd9_graph_init_prop_0 },
+ { nvc1_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvd9_graph_init_gpm_0 },
+ { nvd9_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nvc0_graph_init_tpccs_0 },
+ { nvd9_graph_init_tex_0 },
+ { nvd7_graph_init_pe_0 },
+ { nvc0_graph_init_l1c_0 },
+ { nvc0_graph_init_mpc_0 },
+ { nvd9_graph_init_sm_0 },
+ { nvd7_graph_init_pes_0 },
+ { nvd7_graph_init_wwdx_0 },
+ { nvd7_graph_init_cbm_0 },
+ { nvc0_graph_init_be_0 },
+ { nvd9_graph_init_fe_1 },
+ {}
+};
/*******************************************************************************
* PGRAPH engine/subdev functions
@@ -48,108 +119,6 @@ nvd7_graph_gpccs_ucode = {
.data.size = sizeof(nvd7_grgpc_data),
};
-static struct nvc0_graph_init
-nvd7_graph_init_gpc[] = {
- { 0x418408, 1, 0x04, 0x00000000 },
- { 0x4184a0, 1, 0x04, 0x00000000 },
- { 0x4184a4, 2, 0x04, 0x00000000 },
- { 0x418604, 1, 0x04, 0x00000000 },
- { 0x418680, 1, 0x04, 0x00000000 },
- { 0x418714, 1, 0x04, 0x00000000 },
- { 0x418384, 1, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
- { 0x4188c8, 2, 0x04, 0x00000000 },
- { 0x4188d0, 1, 0x04, 0x00010000 },
- { 0x4188d4, 1, 0x04, 0x00000001 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
- { 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c64, 1, 0x04, 0x00000000 },
- { 0x418c68, 1, 0x04, 0x00000000 },
- { 0x418c88, 1, 0x04, 0x00000000 },
- { 0x418cb4, 2, 0x04, 0x00000000 },
- { 0x418d00, 1, 0x04, 0x00000000 },
- { 0x418d28, 1, 0x04, 0x00000000 },
- { 0x418f00, 1, 0x04, 0x00000000 },
- { 0x418f08, 1, 0x04, 0x00000000 },
- { 0x418f20, 2, 0x04, 0x00000000 },
- { 0x418e00, 1, 0x04, 0x00000003 },
- { 0x418e08, 1, 0x04, 0x00000000 },
- { 0x418e1c, 1, 0x04, 0x00000000 },
- { 0x418e20, 1, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
- {}
-};
-
-static struct nvc0_graph_init
-nvd7_graph_init_tpc[] = {
- { 0x419d08, 2, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
- { 0x419ab0, 1, 0x04, 0x00000000 },
- { 0x419ac8, 1, 0x04, 0x00000000 },
- { 0x419ab8, 1, 0x04, 0x000000e7 },
- { 0x419abc, 2, 0x04, 0x00000000 },
- { 0x419ab4, 1, 0x04, 0x00000000 },
- { 0x41980c, 1, 0x04, 0x00000010 },
- { 0x419844, 1, 0x04, 0x00000000 },
- { 0x41984c, 1, 0x04, 0x00005bc8 },
- { 0x419850, 2, 0x04, 0x00000000 },
- { 0x419c98, 1, 0x04, 0x00000000 },
- { 0x419ca8, 1, 0x04, 0x80000000 },
- { 0x419cb4, 1, 0x04, 0x00000000 },
- { 0x419cb8, 1, 0x04, 0x00008bf4 },
- { 0x419cbc, 1, 0x04, 0x28137606 },
- { 0x419cc0, 2, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
- { 0x419e00, 1, 0x04, 0x00000000 },
- { 0x419ea0, 1, 0x04, 0x00000000 },
- { 0x419ea4, 1, 0x04, 0x00000100 },
- { 0x419ea8, 1, 0x04, 0x02001100 },
- { 0x419eac, 1, 0x04, 0x11100702 },
- { 0x419eb0, 1, 0x04, 0x00000003 },
- { 0x419eb4, 4, 0x04, 0x00000000 },
- { 0x419ec8, 1, 0x04, 0x0e063818 },
- { 0x419ecc, 1, 0x04, 0x0e060e06 },
- { 0x419ed0, 1, 0x04, 0x00003818 },
- { 0x419ed4, 1, 0x04, 0x011104f1 },
- { 0x419edc, 1, 0x04, 0x00000000 },
- { 0x419f00, 1, 0x04, 0x00000000 },
- { 0x419f2c, 1, 0x04, 0x00000000 },
- {}
-};
-
-static struct nvc0_graph_init
-nvd7_graph_init_tpc_0[] = {
- { 0x40402c, 1, 0x04, 0x00000000 },
- { 0x4040f0, 1, 0x04, 0x00000000 },
- { 0x404174, 1, 0x04, 0x00000000 },
- { 0x503018, 1, 0x04, 0x00000001 },
- {}
-};
-
-static struct nvc0_graph_init *
-nvd7_graph_init_mmio[] = {
- nvc0_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvd9_graph_init_unk64xx,
- nvd9_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvd7_graph_init_gpc,
- nvd7_graph_init_tpc,
- nve4_graph_init_unk,
- nvc0_graph_init_unk88xx,
- nvd7_graph_init_tpc_0,
- NULL
-};
-
struct nouveau_oclass *
nvd7_graph_oclass = &(struct nvc0_graph_oclass) {
.base.handle = NV_ENGINE(GR, 0xd7),
@@ -161,7 +130,7 @@ nvd7_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nvd7_grctx_oclass,
.sclass = nvc8_graph_sclass,
- .mmio = nvd7_graph_init_mmio,
+ .mmio = nvd7_graph_pack_mmio,
.fecs.ucode = &nvd7_graph_fecs_ucode,
.gpccs.ucode = &nvd7_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c
index 652098e0df3f..00fdf202fb92 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c
@@ -23,76 +23,70 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-struct nvc0_graph_init
-nvd9_graph_init_unk64xx[] = {
+const struct nvc0_graph_init
+nvd9_graph_init_pd_0[] = {
+ { 0x406024, 1, 0x04, 0x00000000 },
{ 0x4064f0, 3, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvd9_graph_init_unk58xx[] = {
+const struct nvc0_graph_init
+nvd9_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
{ 0x405900, 1, 0x04, 0x00002834 },
{ 0x405908, 1, 0x04, 0x00000000 },
- { 0x405928, 1, 0x04, 0x00000000 },
- { 0x40592c, 1, 0x04, 0x00000000 },
+ { 0x405928, 2, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvd9_graph_init_gpc[] = {
+const struct nvc0_graph_init
+nvd9_graph_init_prop_0[] = {
{ 0x418408, 1, 0x04, 0x00000000 },
- { 0x4184a0, 1, 0x04, 0x00000000 },
- { 0x4184a4, 2, 0x04, 0x00000000 },
- { 0x418604, 1, 0x04, 0x00000000 },
- { 0x418680, 1, 0x04, 0x00000000 },
- { 0x418714, 1, 0x04, 0x00000000 },
- { 0x418384, 1, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
- { 0x4188c8, 2, 0x04, 0x00000000 },
- { 0x4188d0, 1, 0x04, 0x00010000 },
- { 0x4188d4, 1, 0x04, 0x00000001 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
+ { 0x4184a0, 3, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_graph_init_gpm_0[] = {
{ 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c64, 1, 0x04, 0x00000000 },
- { 0x418c68, 1, 0x04, 0x00000000 },
+ { 0x418c64, 2, 0x04, 0x00000000 },
{ 0x418c88, 1, 0x04, 0x00000000 },
{ 0x418cb4, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_graph_init_gpc_unk_1[] = {
{ 0x418d00, 1, 0x04, 0x00000000 },
- { 0x418d28, 1, 0x04, 0x00000000 },
- { 0x418d2c, 1, 0x04, 0x00000000 },
+ { 0x418d28, 2, 0x04, 0x00000000 },
{ 0x418f00, 1, 0x04, 0x00000000 },
{ 0x418f08, 1, 0x04, 0x00000000 },
{ 0x418f20, 2, 0x04, 0x00000000 },
{ 0x418e00, 1, 0x04, 0x00000003 },
{ 0x418e08, 1, 0x04, 0x00000000 },
- { 0x418e1c, 1, 0x04, 0x00000000 },
- { 0x418e20, 1, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
+ { 0x418e1c, 2, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvd9_graph_init_tpc[] = {
- { 0x419d08, 2, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
+const struct nvc0_graph_init
+nvd9_graph_init_tex_0[] = {
{ 0x419ab0, 1, 0x04, 0x00000000 },
{ 0x419ac8, 1, 0x04, 0x00000000 },
{ 0x419ab8, 1, 0x04, 0x000000e7 },
{ 0x419abc, 2, 0x04, 0x00000000 },
{ 0x419ab4, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_graph_init_pe_0[] = {
{ 0x41980c, 1, 0x04, 0x00000010 },
{ 0x419810, 1, 0x04, 0x00000000 },
{ 0x419814, 1, 0x04, 0x00000004 },
@@ -100,20 +94,26 @@ nvd9_graph_init_tpc[] = {
{ 0x41984c, 1, 0x04, 0x0000a918 },
{ 0x419850, 4, 0x04, 0x00000000 },
{ 0x419880, 1, 0x04, 0x00000002 },
- { 0x419c98, 1, 0x04, 0x00000000 },
- { 0x419ca8, 1, 0x04, 0x80000000 },
- { 0x419cb4, 1, 0x04, 0x00000000 },
- { 0x419cb8, 1, 0x04, 0x00008bf4 },
- { 0x419cbc, 1, 0x04, 0x28137606 },
- { 0x419cc0, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_graph_init_wwdx_0[] = {
{ 0x419bd4, 1, 0x04, 0x00800000 },
{ 0x419bdc, 1, 0x04, 0x00000000 },
- { 0x419bf8, 1, 0x04, 0x00000000 },
- { 0x419bfc, 1, 0x04, 0x00000000 },
+ { 0x419bf8, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvd9_graph_init_tpccs_1[] = {
{ 0x419d2c, 1, 0x04, 0x00000000 },
- { 0x419d48, 1, 0x04, 0x00000000 },
- { 0x419d4c, 1, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
+ { 0x419d48, 2, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvd9_graph_init_sm_0[] = {
{ 0x419e00, 1, 0x04, 0x00000000 },
{ 0x419ea0, 1, 0x04, 0x00000000 },
{ 0x419ea4, 1, 0x04, 0x00000100 },
@@ -131,23 +131,49 @@ nvd9_graph_init_tpc[] = {
{}
};
-static struct nvc0_graph_init *
-nvd9_graph_init_mmio[] = {
- nvc0_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvd9_graph_init_unk64xx,
- nvd9_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvd9_graph_init_gpc,
- nvd9_graph_init_tpc,
- nvc0_graph_init_unk88xx,
- nvc0_graph_tpc_0,
- NULL
+const struct nvc0_graph_init
+nvd9_graph_init_fe_1[] = {
+ { 0x40402c, 1, 0x04, 0x00000000 },
+ { 0x4040f0, 1, 0x04, 0x00000000 },
+ { 0x404174, 1, 0x04, 0x00000000 },
+ {}
};
+static const struct nvc0_graph_pack
+nvd9_graph_pack_mmio[] = {
+ { nvc0_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvd9_graph_init_pd_0 },
+ { nvd9_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvd9_graph_init_prop_0 },
+ { nvc1_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvd9_graph_init_gpm_0 },
+ { nvd9_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nvc0_graph_init_tpccs_0 },
+ { nvd9_graph_init_tex_0 },
+ { nvd9_graph_init_pe_0 },
+ { nvc0_graph_init_l1c_0 },
+ { nvd9_graph_init_wwdx_0 },
+ { nvd9_graph_init_tpccs_1 },
+ { nvc0_graph_init_mpc_0 },
+ { nvd9_graph_init_sm_0 },
+ { nvc0_graph_init_be_0 },
+ { nvd9_graph_init_fe_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
struct nouveau_oclass *
nvd9_graph_oclass = &(struct nvc0_graph_oclass) {
.base.handle = NV_ENGINE(GR, 0xd9),
@@ -159,7 +185,7 @@ nvd9_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nvd9_grctx_oclass,
.sclass = nvc8_graph_sclass,
- .mmio = nvd9_graph_init_mmio,
+ .mmio = nvd9_graph_pack_mmio,
.fecs.ucode = &nvc0_graph_fecs_ucode,
.gpccs.ucode = &nvc0_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
index 05ec09c88517..f7c011217175 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
@@ -23,6 +23,7 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
* Graphics object classes
@@ -38,11 +39,11 @@ nve4_graph_sclass[] = {
};
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-struct nvc0_graph_init
-nve4_graph_init_regs[] = {
+const struct nvc0_graph_init
+nve4_graph_init_main_0[] = {
{ 0x400080, 1, 0x04, 0x003083c2 },
{ 0x400088, 1, 0x04, 0x0001ffe7 },
{ 0x40008c, 1, 0x04, 0x00000000 },
@@ -57,81 +58,59 @@ nve4_graph_init_regs[] = {
{}
};
-static struct nvc0_graph_init
-nve4_graph_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nve4_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
{ 0x405900, 1, 0x04, 0x0000ff34 },
{ 0x405908, 1, 0x04, 0x00000000 },
- { 0x405928, 1, 0x04, 0x00000000 },
- { 0x40592c, 1, 0x04, 0x00000000 },
+ { 0x405928, 2, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nve4_graph_init_unk70xx[] = {
+static const struct nvc0_graph_init
+nve4_graph_init_sked_0[] = {
{ 0x407010, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nve4_graph_init_unk5bxx[] = {
+static const struct nvc0_graph_init
+nve4_graph_init_cwd_0[] = {
{ 0x405b50, 1, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nve4_graph_init_gpc[] = {
- { 0x418408, 1, 0x04, 0x00000000 },
- { 0x4184a0, 1, 0x04, 0x00000000 },
- { 0x4184a4, 2, 0x04, 0x00000000 },
- { 0x418604, 1, 0x04, 0x00000000 },
- { 0x418680, 1, 0x04, 0x00000000 },
- { 0x418714, 1, 0x04, 0x00000000 },
- { 0x418384, 1, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
- { 0x4188c8, 2, 0x04, 0x00000000 },
- { 0x4188d0, 1, 0x04, 0x00010000 },
- { 0x4188d4, 1, 0x04, 0x00000001 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
- { 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c64, 1, 0x04, 0x00000000 },
- { 0x418c68, 1, 0x04, 0x00000000 },
- { 0x418c88, 1, 0x04, 0x00000000 },
- { 0x418cb4, 2, 0x04, 0x00000000 },
+static const struct nvc0_graph_init
+nve4_graph_init_gpc_unk_1[] = {
{ 0x418d00, 1, 0x04, 0x00000000 },
- { 0x418d28, 1, 0x04, 0x00000000 },
- { 0x418d2c, 1, 0x04, 0x00000000 },
+ { 0x418d28, 2, 0x04, 0x00000000 },
{ 0x418f00, 1, 0x04, 0x00000000 },
{ 0x418f08, 1, 0x04, 0x00000000 },
{ 0x418f20, 2, 0x04, 0x00000000 },
{ 0x418e00, 1, 0x04, 0x00000060 },
{ 0x418e08, 1, 0x04, 0x00000000 },
- { 0x418e1c, 1, 0x04, 0x00000000 },
- { 0x418e20, 1, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
+ { 0x418e1c, 2, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nve4_graph_init_tpc[] = {
+const struct nvc0_graph_init
+nve4_graph_init_tpccs_0[] = {
{ 0x419d0c, 1, 0x04, 0x00000000 },
{ 0x419d10, 1, 0x04, 0x00000014 },
- { 0x419ab0, 1, 0x04, 0x00000000 },
- { 0x419ac8, 1, 0x04, 0x00000000 },
- { 0x419ab8, 1, 0x04, 0x000000e7 },
- { 0x419abc, 2, 0x04, 0x00000000 },
- { 0x419ab4, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nve4_graph_init_pe_0[] = {
{ 0x41980c, 1, 0x04, 0x00000010 },
{ 0x419844, 1, 0x04, 0x00000000 },
{ 0x419850, 1, 0x04, 0x00000004 },
{ 0x419854, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_graph_init_l1c_0[] = {
{ 0x419c98, 1, 0x04, 0x00000000 },
{ 0x419ca8, 1, 0x04, 0x00000000 },
{ 0x419cb0, 1, 0x04, 0x01000000 },
@@ -141,39 +120,25 @@ nve4_graph_init_tpc[] = {
{ 0x419cbc, 1, 0x04, 0x28137646 },
{ 0x419cc0, 2, 0x04, 0x00000000 },
{ 0x419c80, 1, 0x04, 0x00020232 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nve4_graph_init_sm_0[] = {
{ 0x419e00, 1, 0x04, 0x00000000 },
{ 0x419ea0, 1, 0x04, 0x00000000 },
{ 0x419ee4, 1, 0x04, 0x00000000 },
{ 0x419ea4, 1, 0x04, 0x00000100 },
{ 0x419ea8, 1, 0x04, 0x00000000 },
- { 0x419eb4, 1, 0x04, 0x00000000 },
- { 0x419eb8, 3, 0x04, 0x00000000 },
+ { 0x419eb4, 4, 0x04, 0x00000000 },
{ 0x419edc, 1, 0x04, 0x00000000 },
{ 0x419f00, 1, 0x04, 0x00000000 },
{ 0x419f74, 1, 0x04, 0x00000555 },
{}
};
-struct nvc0_graph_init
-nve4_graph_init_unk[] = {
- { 0x41be04, 1, 0x04, 0x00000000 },
- { 0x41be08, 1, 0x04, 0x00000004 },
- { 0x41be0c, 1, 0x04, 0x00000000 },
- { 0x41be10, 1, 0x04, 0x003b8bc7 },
- { 0x41be14, 2, 0x04, 0x00000000 },
- { 0x41bfd4, 1, 0x04, 0x00800000 },
- { 0x41bfdc, 1, 0x04, 0x00000000 },
- { 0x41bff8, 1, 0x04, 0x00000000 },
- { 0x41bffc, 1, 0x04, 0x00000000 },
- { 0x41becc, 1, 0x04, 0x00000000 },
- { 0x41bee8, 1, 0x04, 0x00000000 },
- { 0x41beec, 1, 0x04, 0x00000000 },
- {}
-};
-
-struct nvc0_graph_init
-nve4_graph_init_unk88xx[] = {
+const struct nvc0_graph_init
+nve4_graph_init_be_0[] = {
{ 0x40880c, 1, 0x04, 0x00000000 },
{ 0x408850, 1, 0x04, 0x00000004 },
{ 0x408910, 9, 0x04, 0x00000000 },
@@ -186,6 +151,67 @@ nve4_graph_init_unk88xx[] = {
{}
};
+static const struct nvc0_graph_pack
+nve4_graph_pack_mmio[] = {
+ { nve4_graph_init_main_0 },
+ { nvc0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvd9_graph_init_pd_0 },
+ { nve4_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nve4_graph_init_sked_0 },
+ { nve4_graph_init_cwd_0 },
+ { nvd9_graph_init_prop_0 },
+ { nvc1_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvd9_graph_init_gpm_0 },
+ { nve4_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nve4_graph_init_tpccs_0 },
+ { nvd9_graph_init_tex_0 },
+ { nve4_graph_init_pe_0 },
+ { nve4_graph_init_l1c_0 },
+ { nvc0_graph_init_mpc_0 },
+ { nve4_graph_init_sm_0 },
+ { nvd7_graph_init_pes_0 },
+ { nvd7_graph_init_wwdx_0 },
+ { nvd7_graph_init_cbm_0 },
+ { nve4_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+static int
+nve4_graph_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nvc0_graph_priv *priv = (void *)object;
+
+ /*XXX: this is a nasty hack to power on gr on certain boards
+ * where it's disabled by therm, somehow. ideally it'd
+ * be nice to know when we should be doing this, and why,
+ * but, it's yet to be determined. for now we test for
+ * the particular mmio error that occurs in the situation,
+ * and then bash therm in the way nvidia do.
+ */
+ nv_mask(priv, 0x000200, 0x08001000, 0x08001000);
+ nv_rd32(priv, 0x000200);
+ if (nv_rd32(priv, 0x400700) == 0xbadf1000) {
+ nv_mask(priv, 0x000200, 0x08001000, 0x00000000);
+ nv_rd32(priv, 0x000200);
+ nv_mask(priv, 0x020004, 0xc0000000, 0x40000000);
+ }
+
+ return nouveau_graph_fini(&priv->base, suspend);
+}
+
int
nve4_graph_init(struct nouveau_object *object)
{
@@ -210,8 +236,7 @@ nve4_graph_init(struct nouveau_object *object)
nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8);
nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8);
- for (i = 0; oclass->mmio[i]; i++)
- nvc0_graph_mmio(priv, oclass->mmio[i]);
+ nvc0_graph_mmio(priv, oclass->mmio);
nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001);
@@ -298,25 +323,6 @@ nve4_graph_init(struct nouveau_object *object)
return nvc0_graph_init_ctxctl(priv);
}
-static struct nvc0_graph_init *
-nve4_graph_init_mmio[] = {
- nve4_graph_init_regs,
- nvc0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvd9_graph_init_unk64xx,
- nve4_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nve4_graph_init_unk70xx,
- nve4_graph_init_unk5bxx,
- nve4_graph_init_gpc,
- nve4_graph_init_tpc,
- nve4_graph_init_unk,
- nve4_graph_init_unk88xx,
- NULL
-};
-
#include "fuc/hubnve0.fuc.h"
static struct nvc0_graph_ucode
@@ -344,11 +350,11 @@ nve4_graph_oclass = &(struct nvc0_graph_oclass) {
.ctor = nvc0_graph_ctor,
.dtor = nvc0_graph_dtor,
.init = nve4_graph_init,
- .fini = _nouveau_graph_fini,
+ .fini = nve4_graph_fini,
},
.cclass = &nve4_grctx_oclass,
.sclass = nve4_graph_sclass,
- .mmio = nve4_graph_init_mmio,
+ .mmio = nve4_graph_pack_mmio,
.fecs.ucode = &nve4_graph_fecs_ucode,
.gpccs.ucode = &nve4_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
index b1acb9939d95..c96762122b9b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
@@ -23,6 +23,7 @@
*/
#include "nvc0.h"
+#include "ctxnvc0.h"
/*******************************************************************************
* Graphics object classes
@@ -38,86 +39,57 @@ nvf0_graph_sclass[] = {
};
/*******************************************************************************
- * PGRAPH engine/subdev functions
+ * PGRAPH register lists
******************************************************************************/
-struct nvc0_graph_init
-nvf0_graph_init_unk40xx[] = {
+const struct nvc0_graph_init
+nvf0_graph_init_fe_0[] = {
{ 0x40415c, 1, 0x04, 0x00000000 },
{ 0x404170, 1, 0x04, 0x00000000 },
{ 0x4041b4, 1, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvf0_graph_init_unk58xx[] = {
+static const struct nvc0_graph_init
+nvf0_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
{ 0x405900, 1, 0x04, 0x0000ff00 },
{ 0x405908, 1, 0x04, 0x00000000 },
- { 0x405928, 1, 0x04, 0x00000000 },
- { 0x40592c, 1, 0x04, 0x00000000 },
+ { 0x405928, 2, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvf0_graph_init_unk70xx[] = {
+const struct nvc0_graph_init
+nvf0_graph_init_sked_0[] = {
{ 0x407010, 1, 0x04, 0x00000000 },
{ 0x407040, 1, 0x04, 0x80440424 },
{ 0x407048, 1, 0x04, 0x0000000a },
{}
};
-struct nvc0_graph_init
-nvf0_graph_init_unk5bxx[] = {
+const struct nvc0_graph_init
+nvf0_graph_init_cwd_0[] = {
{ 0x405b44, 1, 0x04, 0x00000000 },
{ 0x405b50, 1, 0x04, 0x00000000 },
{}
};
-static struct nvc0_graph_init
-nvf0_graph_init_gpc[] = {
- { 0x418408, 1, 0x04, 0x00000000 },
- { 0x4184a0, 1, 0x04, 0x00000000 },
- { 0x4184a4, 2, 0x04, 0x00000000 },
- { 0x418604, 1, 0x04, 0x00000000 },
- { 0x418680, 1, 0x04, 0x00000000 },
- { 0x418714, 1, 0x04, 0x00000000 },
- { 0x418384, 1, 0x04, 0x00000000 },
- { 0x418814, 3, 0x04, 0x00000000 },
- { 0x418b04, 1, 0x04, 0x00000000 },
- { 0x4188c8, 2, 0x04, 0x00000000 },
- { 0x4188d0, 1, 0x04, 0x00010000 },
- { 0x4188d4, 1, 0x04, 0x00000001 },
- { 0x418910, 1, 0x04, 0x00010001 },
- { 0x418914, 1, 0x04, 0x00000301 },
- { 0x418918, 1, 0x04, 0x00800000 },
- { 0x418980, 1, 0x04, 0x77777770 },
- { 0x418984, 3, 0x04, 0x77777777 },
- { 0x418c04, 1, 0x04, 0x00000000 },
- { 0x418c64, 1, 0x04, 0x00000000 },
- { 0x418c68, 1, 0x04, 0x00000000 },
- { 0x418c88, 1, 0x04, 0x00000000 },
- { 0x418cb4, 2, 0x04, 0x00000000 },
+const struct nvc0_graph_init
+nvf0_graph_init_gpc_unk_1[] = {
{ 0x418d00, 1, 0x04, 0x00000000 },
- { 0x418d28, 1, 0x04, 0x00000000 },
- { 0x418d2c, 1, 0x04, 0x00000000 },
+ { 0x418d28, 2, 0x04, 0x00000000 },
{ 0x418f00, 1, 0x04, 0x00000400 },
{ 0x418f08, 1, 0x04, 0x00000000 },
- { 0x418f20, 1, 0x04, 0x00000000 },
- { 0x418f24, 1, 0x04, 0x00000000 },
+ { 0x418f20, 2, 0x04, 0x00000000 },
{ 0x418e00, 1, 0x04, 0x00000000 },
{ 0x418e08, 1, 0x04, 0x00000000 },
{ 0x418e1c, 2, 0x04, 0x00000000 },
- { 0x41900c, 1, 0x04, 0x00000000 },
- { 0x419018, 1, 0x04, 0x00000000 },
{}
};
-struct nvc0_graph_init
-nvf0_graph_init_tpc[] = {
- { 0x419d0c, 1, 0x04, 0x00000000 },
- { 0x419d10, 1, 0x04, 0x00000014 },
+static const struct nvc0_graph_init
+nvf0_graph_init_tex_0[] = {
{ 0x419ab0, 1, 0x04, 0x00000000 },
{ 0x419ac8, 1, 0x04, 0x00000000 },
{ 0x419ab8, 1, 0x04, 0x000000e7 },
@@ -125,10 +97,11 @@ nvf0_graph_init_tpc[] = {
{ 0x419abc, 2, 0x04, 0x00000000 },
{ 0x419ab4, 1, 0x04, 0x00000000 },
{ 0x419aa8, 2, 0x04, 0x00000000 },
- { 0x41980c, 1, 0x04, 0x00000010 },
- { 0x419844, 1, 0x04, 0x00000000 },
- { 0x419850, 1, 0x04, 0x00000004 },
- { 0x419854, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+nvf0_graph_init_l1c_0[] = {
{ 0x419c98, 1, 0x04, 0x00000000 },
{ 0x419ca8, 1, 0x04, 0x00000000 },
{ 0x419cb0, 1, 0x04, 0x01000000 },
@@ -139,7 +112,11 @@ nvf0_graph_init_tpc[] = {
{ 0x419cc0, 2, 0x04, 0x00000000 },
{ 0x419c80, 1, 0x04, 0x00020230 },
{ 0x419ccc, 2, 0x04, 0x00000000 },
- { 0x419c0c, 1, 0x04, 0x00000000 },
+ {}
+};
+
+const struct nvc0_graph_init
+nvf0_graph_init_sm_0[] = {
{ 0x419e00, 1, 0x04, 0x00000080 },
{ 0x419ea0, 1, 0x04, 0x00000000 },
{ 0x419ee4, 1, 0x04, 0x00000000 },
@@ -155,6 +132,44 @@ nvf0_graph_init_tpc[] = {
{}
};
+static const struct nvc0_graph_pack
+nvf0_graph_pack_mmio[] = {
+ { nve4_graph_init_main_0 },
+ { nvf0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvd9_graph_init_pd_0 },
+ { nvf0_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvf0_graph_init_sked_0 },
+ { nvf0_graph_init_cwd_0 },
+ { nvd9_graph_init_prop_0 },
+ { nvc1_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvd9_graph_init_gpm_0 },
+ { nvf0_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nve4_graph_init_tpccs_0 },
+ { nvf0_graph_init_tex_0 },
+ { nve4_graph_init_pe_0 },
+ { nvf0_graph_init_l1c_0 },
+ { nvc0_graph_init_mpc_0 },
+ { nvf0_graph_init_sm_0 },
+ { nvd7_graph_init_pes_0 },
+ { nvd7_graph_init_wwdx_0 },
+ { nvd7_graph_init_cbm_0 },
+ { nve4_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
static int
nvf0_graph_fini(struct nouveau_object *object, bool suspend)
{
@@ -192,25 +207,6 @@ nvf0_graph_fini(struct nouveau_object *object, bool suspend)
return nouveau_graph_fini(&priv->base, suspend);
}
-static struct nvc0_graph_init *
-nvf0_graph_init_mmio[] = {
- nve4_graph_init_regs,
- nvf0_graph_init_unk40xx,
- nvc0_graph_init_unk44xx,
- nvc0_graph_init_unk78xx,
- nvc0_graph_init_unk60xx,
- nvd9_graph_init_unk64xx,
- nvf0_graph_init_unk58xx,
- nvc0_graph_init_unk80xx,
- nvf0_graph_init_unk70xx,
- nvf0_graph_init_unk5bxx,
- nvf0_graph_init_gpc,
- nvf0_graph_init_tpc,
- nve4_graph_init_unk,
- nve4_graph_init_unk88xx,
- NULL
-};
-
#include "fuc/hubnvf0.fuc.h"
static struct nvc0_graph_ucode
@@ -242,7 +238,7 @@ nvf0_graph_oclass = &(struct nvc0_graph_oclass) {
},
.cclass = &nvf0_grctx_oclass,
.sclass = nvf0_graph_sclass,
- .mmio = nvf0_graph_init_mmio,
+ .mmio = nvf0_graph_pack_mmio,
.fecs.ucode = &nvf0_graph_fecs_ucode,
.gpccs.ucode = &nvf0_graph_gpccs_ucode,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/xtensa.c b/drivers/gpu/drm/nouveau/core/engine/xtensa.c
index 5f6ede7c4892..92384759d2f5 100644
--- a/drivers/gpu/drm/nouveau/core/engine/xtensa.c
+++ b/drivers/gpu/drm/nouveau/core/engine/xtensa.c
@@ -112,7 +112,7 @@ _nouveau_xtensa_init(struct nouveau_object *object)
snprintf(name, sizeof(name), "nouveau/nv84_xuc%03x",
xtensa->addr >> 12);
- ret = request_firmware(&fw, name, &device->pdev->dev);
+ ret = request_firmware(&fw, name, nv_device_base(device));
if (ret) {
nv_warn(xtensa, "unable to load firmware %s\n", name);
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h
index e71a4325e670..9c0cd73462d9 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/class.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/class.h
@@ -258,6 +258,7 @@ struct nv04_display_scanoutpos {
* 9070: NVD0_DISP
* 9170: NVE0_DISP
* 9270: NVF0_DISP
+ * 9470: GM107_DISP
*/
#define NV50_DISP_CLASS 0x00005070
@@ -268,6 +269,7 @@ struct nv04_display_scanoutpos {
#define NVD0_DISP_CLASS 0x00009070
#define NVE0_DISP_CLASS 0x00009170
#define NVF0_DISP_CLASS 0x00009270
+#define GM107_DISP_CLASS 0x00009470
#define NV50_DISP_MTHD 0x00000000
#define NV50_DISP_MTHD_HEAD 0x00000003
@@ -342,6 +344,7 @@ struct nv50_display_class {
* 907a: NVD0_DISP_CURS
* 917a: NVE0_DISP_CURS
* 927a: NVF0_DISP_CURS
+ * 947a: GM107_DISP_CURS
*/
#define NV50_DISP_CURS_CLASS 0x0000507a
@@ -352,6 +355,7 @@ struct nv50_display_class {
#define NVD0_DISP_CURS_CLASS 0x0000907a
#define NVE0_DISP_CURS_CLASS 0x0000917a
#define NVF0_DISP_CURS_CLASS 0x0000927a
+#define GM107_DISP_CURS_CLASS 0x0000947a
struct nv50_display_curs_class {
u32 head;
@@ -365,6 +369,7 @@ struct nv50_display_curs_class {
* 907b: NVD0_DISP_OIMM
* 917b: NVE0_DISP_OIMM
* 927b: NVE0_DISP_OIMM
+ * 947b: GM107_DISP_OIMM
*/
#define NV50_DISP_OIMM_CLASS 0x0000507b
@@ -375,6 +380,7 @@ struct nv50_display_curs_class {
#define NVD0_DISP_OIMM_CLASS 0x0000907b
#define NVE0_DISP_OIMM_CLASS 0x0000917b
#define NVF0_DISP_OIMM_CLASS 0x0000927b
+#define GM107_DISP_OIMM_CLASS 0x0000947b
struct nv50_display_oimm_class {
u32 head;
@@ -388,6 +394,7 @@ struct nv50_display_oimm_class {
* 907c: NVD0_DISP_SYNC
* 917c: NVE0_DISP_SYNC
* 927c: NVF0_DISP_SYNC
+ * 947c: GM107_DISP_SYNC
*/
#define NV50_DISP_SYNC_CLASS 0x0000507c
@@ -398,6 +405,7 @@ struct nv50_display_oimm_class {
#define NVD0_DISP_SYNC_CLASS 0x0000907c
#define NVE0_DISP_SYNC_CLASS 0x0000917c
#define NVF0_DISP_SYNC_CLASS 0x0000927c
+#define GM107_DISP_SYNC_CLASS 0x0000947c
struct nv50_display_sync_class {
u32 pushbuf;
@@ -412,6 +420,7 @@ struct nv50_display_sync_class {
* 907d: NVD0_DISP_MAST
* 917d: NVE0_DISP_MAST
* 927d: NVF0_DISP_MAST
+ * 947d: GM107_DISP_MAST
*/
#define NV50_DISP_MAST_CLASS 0x0000507d
@@ -422,6 +431,7 @@ struct nv50_display_sync_class {
#define NVD0_DISP_MAST_CLASS 0x0000907d
#define NVE0_DISP_MAST_CLASS 0x0000917d
#define NVF0_DISP_MAST_CLASS 0x0000927d
+#define GM107_DISP_MAST_CLASS 0x0000947d
struct nv50_display_mast_class {
u32 pushbuf;
@@ -435,6 +445,7 @@ struct nv50_display_mast_class {
* 907e: NVD0_DISP_OVLY
* 917e: NVE0_DISP_OVLY
* 927e: NVF0_DISP_OVLY
+ * 947e: GM107_DISP_OVLY
*/
#define NV50_DISP_OVLY_CLASS 0x0000507e
@@ -445,6 +456,7 @@ struct nv50_display_mast_class {
#define NVD0_DISP_OVLY_CLASS 0x0000907e
#define NVE0_DISP_OVLY_CLASS 0x0000917e
#define NVF0_DISP_OVLY_CLASS 0x0000927e
+#define GM107_DISP_OVLY_CLASS 0x0000947e
struct nv50_display_ovly_class {
u32 pushbuf;
diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h
index 7b8ea221b00d..a8a9a9cf16cb 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/device.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/device.h
@@ -40,6 +40,7 @@ enum nv_subdev_type {
NVDEV_ENGINE_FIRST,
NVDEV_ENGINE_DMAOBJ = NVDEV_ENGINE_FIRST,
+ NVDEV_ENGINE_IFB,
NVDEV_ENGINE_FIFO,
NVDEV_ENGINE_SW,
NVDEV_ENGINE_GR,
@@ -65,6 +66,7 @@ struct nouveau_device {
struct list_head head;
struct pci_dev *pdev;
+ struct platform_device *platformdev;
u64 handle;
const char *cfgopt;
@@ -84,6 +86,7 @@ struct nouveau_device {
NV_C0 = 0xc0,
NV_D0 = 0xd0,
NV_E0 = 0xe0,
+ GM100 = 0x110,
} card_type;
u32 chipset;
u32 crystal;
@@ -140,4 +143,32 @@ nv_device_match(struct nouveau_object *object, u16 dev, u16 ven, u16 sub)
device->pdev->subsystem_device == sub;
}
+static inline bool
+nv_device_is_pci(struct nouveau_device *device)
+{
+ return device->pdev != NULL;
+}
+
+static inline struct device *
+nv_device_base(struct nouveau_device *device)
+{
+ return nv_device_is_pci(device) ? &device->pdev->dev :
+ &device->platformdev->dev;
+}
+
+resource_size_t
+nv_device_resource_start(struct nouveau_device *device, unsigned int bar);
+
+resource_size_t
+nv_device_resource_len(struct nouveau_device *device, unsigned int bar);
+
+dma_addr_t
+nv_device_map_page(struct nouveau_device *device, struct page *page);
+
+void
+nv_device_unmap_page(struct nouveau_device *device, dma_addr_t addr);
+
+int
+nv_device_get_irq(struct nouveau_device *device, bool stall);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/namedb.h b/drivers/gpu/drm/nouveau/core/include/core/namedb.h
index 8897e0886085..f5b5fd8e1fc9 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/namedb.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/namedb.h
@@ -33,7 +33,7 @@ nv_namedb(void *obj)
int nouveau_namedb_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, u32 pclass,
- struct nouveau_oclass *, u32 engcls,
+ struct nouveau_oclass *, u64 engcls,
int size, void **);
int _nouveau_namedb_ctor(struct nouveau_object *, struct nouveau_object *,
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/device.h b/drivers/gpu/drm/nouveau/core/include/engine/device.h
index b3dd2c4c2f1e..672d3c8f4145 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/device.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/device.h
@@ -3,11 +3,20 @@
#include <core/device.h>
-#define nouveau_device_create(p,n,s,c,d,u) \
- nouveau_device_create_((p), (n), (s), (c), (d), sizeof(**u), (void **)u)
+struct platform_device;
-int nouveau_device_create_(struct pci_dev *, u64 name, const char *sname,
- const char *cfg, const char *dbg, int, void **);
+enum nv_bus_type {
+ NOUVEAU_BUS_PCI,
+ NOUVEAU_BUS_PLATFORM,
+};
+
+#define nouveau_device_create(p,t,n,s,c,d,u) \
+ nouveau_device_create_((void *)(p), (t), (n), (s), (c), (d), \
+ sizeof(**u), (void **)u)
+
+int nouveau_device_create_(void *, enum nv_bus_type type, u64 name,
+ const char *sname, const char *cfg, const char *dbg,
+ int, void **);
int nv04_identify(struct nouveau_device *);
int nv10_identify(struct nouveau_device *);
@@ -17,6 +26,7 @@ int nv40_identify(struct nouveau_device *);
int nv50_identify(struct nouveau_device *);
int nvc0_identify(struct nouveau_device *);
int nve0_identify(struct nouveau_device *);
+int gm100_identify(struct nouveau_device *);
struct nouveau_device *nouveau_device_find(u64 name);
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h
index 4b21fabfbddb..fd0c68804de3 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h
@@ -36,14 +36,15 @@ void _nouveau_disp_dtor(struct nouveau_object *);
#define _nouveau_disp_init _nouveau_engine_init
#define _nouveau_disp_fini _nouveau_engine_fini
-extern struct nouveau_oclass nv04_disp_oclass;
-extern struct nouveau_oclass nv50_disp_oclass;
-extern struct nouveau_oclass nv84_disp_oclass;
-extern struct nouveau_oclass nva0_disp_oclass;
-extern struct nouveau_oclass nv94_disp_oclass;
-extern struct nouveau_oclass nva3_disp_oclass;
-extern struct nouveau_oclass nvd0_disp_oclass;
-extern struct nouveau_oclass nve0_disp_oclass;
-extern struct nouveau_oclass nvf0_disp_oclass;
+extern struct nouveau_oclass *nv04_disp_oclass;
+extern struct nouveau_oclass *nv50_disp_oclass;
+extern struct nouveau_oclass *nv84_disp_oclass;
+extern struct nouveau_oclass *nva0_disp_oclass;
+extern struct nouveau_oclass *nv94_disp_oclass;
+extern struct nouveau_oclass *nva3_disp_oclass;
+extern struct nouveau_oclass *nvd0_disp_oclass;
+extern struct nouveau_oclass *nve0_disp_oclass;
+extern struct nouveau_oclass *nvf0_disp_oclass;
+extern struct nouveau_oclass *gm107_disp_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/graph.h b/drivers/gpu/drm/nouveau/core/include/engine/graph.h
index 97705618de97..871edfdf3d5b 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/graph.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/graph.h
@@ -63,13 +63,14 @@ extern struct nouveau_oclass nv40_graph_oclass;
extern struct nouveau_oclass nv50_graph_oclass;
extern struct nouveau_oclass *nvc0_graph_oclass;
extern struct nouveau_oclass *nvc1_graph_oclass;
-extern struct nouveau_oclass *nvc3_graph_oclass;
+extern struct nouveau_oclass *nvc4_graph_oclass;
extern struct nouveau_oclass *nvc8_graph_oclass;
extern struct nouveau_oclass *nvd7_graph_oclass;
extern struct nouveau_oclass *nvd9_graph_oclass;
extern struct nouveau_oclass *nve4_graph_oclass;
extern struct nouveau_oclass *nvf0_graph_oclass;
extern struct nouveau_oclass *nv108_graph_oclass;
+extern struct nouveau_oclass *gm107_graph_oclass;
extern const struct nouveau_bitfield nv04_graph_nsource[];
extern struct nouveau_ofuncs nv04_graph_ofuncs;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/P0260.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/P0260.h
new file mode 100644
index 000000000000..bba01ab1e049
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/P0260.h
@@ -0,0 +1,23 @@
+#ifndef __NVBIOS_P0260_H__
+#define __NVBIOS_P0260_H__
+
+u32 nvbios_P0260Te(struct nouveau_bios *,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz);
+
+struct nvbios_P0260E {
+ u32 data;
+};
+
+u32 nvbios_P0260Ee(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr);
+u32 nvbios_P0260Ep(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_P0260E *);
+
+struct nvbios_P0260X {
+ u32 data;
+};
+
+u32 nvbios_P0260Xe(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr);
+u32 nvbios_P0260Xp(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_P0260X *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
index c1270548fd0d..a32feb3f3fb6 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
@@ -16,6 +16,7 @@ enum dcb_connector_type {
DCB_CONNECTOR_eDP = 0x47,
DCB_CONNECTOR_HDMI_0 = 0x60,
DCB_CONNECTOR_HDMI_1 = 0x61,
+ DCB_CONNECTOR_HDMI_C = 0x63,
DCB_CONNECTOR_DMS59_DP0 = 0x64,
DCB_CONNECTOR_DMS59_DP1 = 0x65,
DCB_CONNECTOR_NONE = 0xff
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h
index c5e6d1e6ac1d..c086ac6d677d 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h
@@ -61,6 +61,6 @@ struct nvbios_ramcfg {
};
u8 nvbios_ramcfg_count(struct nouveau_bios *);
-u8 nvbios_ramcfg_index(struct nouveau_bios *);
+u8 nvbios_ramcfg_index(struct nouveau_subdev *);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
index 083541dbe9c8..8dc5051df55d 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
@@ -31,6 +31,12 @@ struct nouveau_therm_trip_point {
int hysteresis;
};
+enum nvbios_therm_fan_mode {
+ NVBIOS_THERM_FAN_TRIP = 0,
+ NVBIOS_THERM_FAN_LINEAR = 1,
+ NVBIOS_THERM_FAN_OTHER = 2,
+};
+
struct nvbios_therm_fan {
u16 pwm_freq;
@@ -40,6 +46,7 @@ struct nvbios_therm_fan {
u16 bump_period;
u16 slow_down_period;
+ enum nvbios_therm_fan_mode fan_mode;
struct nouveau_therm_trip_point trip[NOUVEAU_TEMP_FAN_TRIP_MAX];
u8 nr_fan_trip;
u8 linear_min_temp;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h b/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h
index ed1ac68c38b3..e292271a84e4 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h
@@ -9,6 +9,7 @@ struct nouveau_devinit {
bool post;
void (*meminit)(struct nouveau_devinit *);
int (*pll_set)(struct nouveau_devinit *, u32 type, u32 freq);
+ u32 (*mmio)(struct nouveau_devinit *, u32 addr);
};
static inline struct nouveau_devinit *
@@ -28,5 +29,6 @@ extern struct nouveau_oclass *nv98_devinit_oclass;
extern struct nouveau_oclass *nva3_devinit_oclass;
extern struct nouveau_oclass *nvaf_devinit_oclass;
extern struct nouveau_oclass *nvc0_devinit_oclass;
+extern struct nouveau_oclass *gm107_devinit_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
index d7ecafbae1ca..58c7ccdebb01 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
@@ -105,6 +105,7 @@ extern struct nouveau_oclass *nvaa_fb_oclass;
extern struct nouveau_oclass *nvaf_fb_oclass;
extern struct nouveau_oclass *nvc0_fb_oclass;
extern struct nouveau_oclass *nve0_fb_oclass;
+extern struct nouveau_oclass *gm107_fb_oclass;
#include <subdev/bios/ramcfg.h>
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h b/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
index a1985ed3d58d..c9c1950b7743 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
@@ -35,6 +35,7 @@ nouveau_ltcg(void *obj)
#define _nouveau_ltcg_init _nouveau_subdev_init
#define _nouveau_ltcg_fini _nouveau_subdev_fini
-extern struct nouveau_oclass nvc0_ltcg_oclass;
+extern struct nouveau_oclass *gf100_ltcg_oclass;
+extern struct nouveau_oclass *gm107_ltcg_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h
index 3c6738edd127..72b176831be6 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h
@@ -12,6 +12,7 @@ struct nouveau_mc_intr {
struct nouveau_mc {
struct nouveau_subdev base;
bool use_msi;
+ unsigned int irq;
};
static inline struct nouveau_mc *
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
index 69891d4a3fe7..d4a68179e586 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
@@ -31,7 +31,7 @@ struct nouveau_therm {
int (*pwm_ctrl)(struct nouveau_therm *, int line, bool);
int (*pwm_get)(struct nouveau_therm *, int line, u32 *, u32 *);
int (*pwm_set)(struct nouveau_therm *, int line, u32, u32);
- int (*pwm_clock)(struct nouveau_therm *);
+ int (*pwm_clock)(struct nouveau_therm *, int line);
int (*fan_get)(struct nouveau_therm *);
int (*fan_set)(struct nouveau_therm *, int);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/timer.h b/drivers/gpu/drm/nouveau/core/include/subdev/timer.h
index 9ab70dfe5b02..db9be803a874 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/timer.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/timer.h
@@ -59,5 +59,6 @@ int nouveau_timer_create_(struct nouveau_object *, struct nouveau_engine *,
struct nouveau_oclass *, int size, void **);
extern struct nouveau_oclass nv04_timer_oclass;
+extern struct nouveau_oclass gk20a_timer_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/os.h b/drivers/gpu/drm/nouveau/core/os.h
index 191e739f30d1..d0ced94ca54c 100644
--- a/drivers/gpu/drm/nouveau/core/os.h
+++ b/drivers/gpu/drm/nouveau/core/os.h
@@ -5,6 +5,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/bitops.h>
#include <linux/firmware.h>
@@ -23,17 +24,6 @@
#include <asm/unaligned.h>
-static inline int
-ffsll(u64 mask)
-{
- int i;
- for (i = 0; i < 64; i++) {
- if (mask & (1ULL << i))
- return i + 1;
- }
- return 0;
-}
-
#ifndef ioread32_native
#ifdef __BIG_ENDIAN
#define ioread16_native ioread16be
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
index 7098ddd54678..bdf594116f3f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
@@ -118,8 +118,8 @@ nouveau_bar_create_(struct nouveau_object *parent,
if (ret)
return ret;
- bar->iomem = ioremap(pci_resource_start(device->pdev, 3),
- pci_resource_len(device->pdev, 3));
+ bar->iomem = ioremap(nv_device_resource_start(device, 3),
+ nv_device_resource_len(device, 3));
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c
index 090d594a21b3..f748ba49dfc8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c
@@ -139,7 +139,7 @@ nv50_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
/* BAR3 */
start = 0x0100000000ULL;
- limit = start + pci_resource_len(device->pdev, 3);
+ limit = start + nv_device_resource_len(device, 3);
ret = nouveau_vm_new(device, start, limit, start, &vm);
if (ret)
@@ -173,7 +173,7 @@ nv50_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
/* BAR1 */
start = 0x0000000000ULL;
- limit = start + pci_resource_len(device->pdev, 1);
+ limit = start + nv_device_resource_len(device, 1);
ret = nouveau_vm_new(device, start, limit--, start, &vm);
if (ret)
@@ -231,7 +231,7 @@ static int
nv50_bar_init(struct nouveau_object *object)
{
struct nv50_bar_priv *priv = (void *)object;
- int ret;
+ int ret, i;
ret = nouveau_bar_init(&priv->base);
if (ret)
@@ -249,6 +249,8 @@ nv50_bar_init(struct nouveau_object *object)
nv_wr32(priv, 0x001704, 0x40000000 | priv->mem->addr >> 12);
nv_wr32(priv, 0x001708, 0x80000000 | priv->bar1->node->offset >> 4);
nv_wr32(priv, 0x00170c, 0x80000000 | priv->bar3->node->offset >> 4);
+ for (i = 0; i < 8; i++)
+ nv_wr32(priv, 0x001900 + (i * 4), 0x00000000);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
index bac5e754de35..3f30db62e656 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
@@ -84,7 +84,6 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_object **pobject)
{
struct nouveau_device *device = nv_device(parent);
- struct pci_dev *pdev = device->pdev;
struct nvc0_bar_priv *priv;
struct nouveau_gpuobj *mem;
struct nouveau_vm *vm;
@@ -107,14 +106,14 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- ret = nouveau_vm_new(device, 0, pci_resource_len(pdev, 3), 0, &vm);
+ ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 3), 0, &vm);
if (ret)
return ret;
atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
ret = nouveau_gpuobj_new(nv_object(priv), NULL,
- (pci_resource_len(pdev, 3) >> 12) * 8,
+ (nv_device_resource_len(device, 3) >> 12) * 8,
0x1000, NVOBJ_FLAG_ZERO_ALLOC,
&vm->pgt[0].obj[0]);
vm->pgt[0].refcount[0] = 1;
@@ -128,8 +127,8 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[0].pgd->addr));
nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[0].pgd->addr));
- nv_wo32(mem, 0x0208, lower_32_bits(pci_resource_len(pdev, 3) - 1));
- nv_wo32(mem, 0x020c, upper_32_bits(pci_resource_len(pdev, 3) - 1));
+ nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 3) - 1));
+ nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 3) - 1));
/* BAR1 */
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0,
@@ -143,7 +142,7 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- ret = nouveau_vm_new(device, 0, pci_resource_len(pdev, 1), 0, &vm);
+ ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 1), 0, &vm);
if (ret)
return ret;
@@ -156,8 +155,8 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[1].pgd->addr));
nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[1].pgd->addr));
- nv_wo32(mem, 0x0208, lower_32_bits(pci_resource_len(pdev, 1) - 1));
- nv_wo32(mem, 0x020c, upper_32_bits(pci_resource_len(pdev, 1) - 1));
+ nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 1) - 1));
+ nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 1) - 1));
priv->base.alloc = nouveau_bar_alloc;
priv->base.kmap = nvc0_bar_kmap;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/P0260.c b/drivers/gpu/drm/nouveau/core/subdev/bios/P0260.c
new file mode 100644
index 000000000000..199f4e5f7488
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/P0260.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/ramcfg.h>
+#include <subdev/bios/P0260.h>
+
+u32
+nvbios_P0260Te(struct nouveau_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz)
+{
+ struct bit_entry bit_P;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2 && bit_P.length > 0x63)
+ data = nv_ro32(bios, bit_P.offset + 0x60);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 1);
+ *cnt = nv_ro08(bios, data + 2);
+ *len = 4;
+ *xnr = nv_ro08(bios, data + 3);
+ *xsz = 4;
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Ee(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt, xnr, xsz;
+ u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, len, &xnr, &xsz);
+ if (data && idx < cnt)
+ return data + hdr + (idx * *len);
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Ep(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len,
+ struct nvbios_P0260E *info)
+{
+ u32 data = nvbios_P0260Ee(bios, idx, ver, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->data = nv_ro32(bios, data);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Xe(struct nouveau_bios *bios, int idx, u8 *ver, u8 *xsz)
+{
+ u8 hdr, cnt, len, xnr;
+ u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, &len, &xnr, xsz);
+ if (data && idx < xnr)
+ return data + hdr + (cnt * len) + (idx * *xsz);
+ return 0x00000000;
+}
+
+u32
+nvbios_P0260Xp(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_P0260X *info)
+{
+ u32 data = nvbios_P0260Xe(bios, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->data = nv_ro32(bios, data);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
index ef0c9c4a8cc3..e9df94f96d78 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
@@ -90,10 +90,26 @@ nouveau_bios_shadow_pramin(struct nouveau_bios *bios)
int i;
if (device->card_type >= NV_50) {
- if ( device->card_type < NV_C0 ||
- !(nv_rd32(bios, 0x022500) & 0x00000001))
- addr = (u64)(nv_rd32(bios, 0x619f04) & 0xffffff00) << 8;
+ if (device->card_type >= NV_C0 && device->card_type < GM100) {
+ if (nv_rd32(bios, 0x022500) & 0x00000001)
+ return;
+ } else
+ if (device->card_type >= GM100) {
+ if (nv_rd32(bios, 0x021c04) & 0x00000001)
+ return;
+ }
+
+ addr = nv_rd32(bios, 0x619f04);
+ if (!(addr & 0x00000008)) {
+ nv_debug(bios, "... not enabled\n");
+ return;
+ }
+ if ( (addr & 0x00000003) != 1) {
+ nv_debug(bios, "... not in vram\n");
+ return;
+ }
+ addr = (u64)(addr >> 8) << 8;
if (!addr) {
addr = (u64)nv_rd32(bios, 0x001700) << 16;
addr += 0xf0000;
@@ -141,6 +157,10 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
pcireg = 0x001850;
access = nv_mask(bios, pcireg, 0x00000001, 0x00000000);
+ /* WARNING: PROM accesses should always be 32-bits aligned. Other
+ * accesses work on most chipset but do not on Kepler chipsets
+ */
+
/* bail if no rom signature, with a workaround for a PROM reading
* issue on some chipsets. the first read after a period of
* inactivity returns the wrong result, so retry the first header
@@ -148,31 +168,32 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
*/
i = 16;
do {
- if (nv_rd08(bios, 0x300000) == 0x55)
+ if ((nv_rd32(bios, 0x300000) & 0xffff) == 0xaa55)
break;
} while (i--);
- if (!i || nv_rd08(bios, 0x300001) != 0xaa)
- goto out;
-
- /* additional check (see note below) - read PCI record header */
- pcir = nv_rd08(bios, 0x300018) |
- nv_rd08(bios, 0x300019) << 8;
- if (nv_rd08(bios, 0x300000 + pcir) != 'P' ||
- nv_rd08(bios, 0x300001 + pcir) != 'C' ||
- nv_rd08(bios, 0x300002 + pcir) != 'I' ||
- nv_rd08(bios, 0x300003 + pcir) != 'R')
+ if (!i)
goto out;
/* read entire bios image to system memory */
- bios->size = nv_rd08(bios, 0x300002) * 512;
+ bios->size = ((nv_rd32(bios, 0x300000) >> 16) & 0xff) * 512;
if (!bios->size)
goto out;
bios->data = kmalloc(bios->size, GFP_KERNEL);
if (bios->data) {
- for (i = 0; i < bios->size; i++)
- nv_wo08(bios, i, nv_rd08(bios, 0x300000 + i));
+ for (i = 0; i < bios->size; i+=4)
+ nv_wo32(bios, i, nv_rd32(bios, 0x300000 + i));
+ }
+
+ /* check the PCI record header */
+ pcir = nv_ro16(bios, 0x0018);
+ if (bios->data[pcir + 0] != 'P' ||
+ bios->data[pcir + 1] != 'C' ||
+ bios->data[pcir + 2] != 'I' ||
+ bios->data[pcir + 3] != 'R') {
+ bios->size = 0;
+ kfree(bios->data);
}
out:
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
index 2d9b9d7a7992..88606bfaf847 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
@@ -142,9 +142,36 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
if (*ver >= 0x40) {
u32 conf = nv_ro32(bios, dcb + 0x04);
switch (outp->type) {
+ case DCB_OUTPUT_DP:
+ switch (conf & 0x00e00000) {
+ case 0x00000000:
+ outp->dpconf.link_bw = 0x06;
+ break;
+ case 0x00200000:
+ outp->dpconf.link_bw = 0x0a;
+ break;
+ case 0x00400000:
+ default:
+ outp->dpconf.link_bw = 0x14;
+ break;
+ }
+
+ switch (conf & 0x0f000000) {
+ case 0x0f000000:
+ outp->dpconf.link_nr = 4;
+ break;
+ case 0x03000000:
+ outp->dpconf.link_nr = 2;
+ break;
+ case 0x01000000:
+ default:
+ outp->dpconf.link_nr = 1;
+ break;
+ }
+
+ /* fall-through... */
case DCB_OUTPUT_TMDS:
case DCB_OUTPUT_LVDS:
- case DCB_OUTPUT_DP:
outp->link = (conf & 0x00000030) >> 4;
outp->sorconf.link = outp->link; /*XXX*/
outp->extdev = 0x00;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
index de201baeb053..acaeaf79e3f0 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
@@ -118,6 +118,8 @@ init_conn(struct nvbios_init *init)
static inline u32
init_nvreg(struct nvbios_init *init, u32 reg)
{
+ struct nouveau_devinit *devinit = nouveau_devinit(init->bios);
+
/* C51 (at least) sometimes has the lower bits set which the VBIOS
* interprets to mean that access needs to go through certain IO
* ports instead. The NVIDIA binary driver has been seen to access
@@ -147,6 +149,9 @@ init_nvreg(struct nvbios_init *init, u32 reg)
if (reg & ~0x00fffffc)
warn("unknown bits in register 0x%08x\n", reg);
+
+ if (devinit->mmio)
+ reg = devinit->mmio(devinit, reg);
return reg;
}
@@ -154,7 +159,7 @@ static u32
init_rd32(struct nvbios_init *init, u32 reg)
{
reg = init_nvreg(init, reg);
- if (init_exec(init))
+ if (reg != ~0 && init_exec(init))
return nv_rd32(init->subdev, reg);
return 0x00000000;
}
@@ -163,7 +168,7 @@ static void
init_wr32(struct nvbios_init *init, u32 reg, u32 val)
{
reg = init_nvreg(init, reg);
- if (init_exec(init))
+ if (reg != ~0 && init_exec(init))
nv_wr32(init->subdev, reg, val);
}
@@ -171,7 +176,7 @@ static u32
init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val)
{
reg = init_nvreg(init, reg);
- if (init_exec(init)) {
+ if (reg != ~0 && init_exec(init)) {
u32 tmp = nv_rd32(init->subdev, reg);
nv_wr32(init->subdev, reg, (tmp & ~mask) | val);
return tmp;
@@ -410,7 +415,7 @@ init_ram_restrict(struct nvbios_init *init)
* in case *not* re-reading the strap causes similar breakage.
*/
if (!init->ramcfg || init->bios->version.major < 0x70)
- init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->bios);
+ init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev);
return (init->ramcfg & 0x7fffffff);
}
@@ -845,9 +850,8 @@ init_idx_addr_latched(struct nvbios_init *init)
u32 data = nv_ro32(bios, init->offset + 13);
u8 count = nv_ro08(bios, init->offset + 17);
- trace("INDEX_ADDRESS_LATCHED\t"
- "R[0x%06x] : R[0x%06x]\n\tCTRL &= 0x%08x |= 0x%08x\n",
- creg, dreg, mask, data);
+ trace("INDEX_ADDRESS_LATCHED\tR[0x%06x] : R[0x%06x]\n", creg, dreg);
+ trace("\tCTRL &= 0x%08x |= 0x%08x\n", mask, data);
init->offset += 18;
while (count--) {
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/ramcfg.c b/drivers/gpu/drm/nouveau/core/subdev/bios/ramcfg.c
index 991aedda999b..6c401f70ab99 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/ramcfg.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/ramcfg.c
@@ -27,9 +27,9 @@
#include <subdev/bios/ramcfg.h>
static u8
-nvbios_ramcfg_strap(struct nouveau_bios *bios)
+nvbios_ramcfg_strap(struct nouveau_subdev *subdev)
{
- return (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2;
+ return (nv_rd32(subdev, 0x101000) & 0x0000003c) >> 2;
}
u8
@@ -48,9 +48,10 @@ nvbios_ramcfg_count(struct nouveau_bios *bios)
}
u8
-nvbios_ramcfg_index(struct nouveau_bios *bios)
+nvbios_ramcfg_index(struct nouveau_subdev *subdev)
{
- u8 strap = nvbios_ramcfg_strap(bios);
+ struct nouveau_bios *bios = nouveau_bios(subdev);
+ u8 strap = nvbios_ramcfg_strap(subdev);
u32 xlat = 0x00000000;
struct bit_entry bit_M;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c b/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c
index 22ac6dbd6c8f..d15854094078 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c
@@ -164,6 +164,7 @@ nvbios_therm_fan_parse(struct nouveau_bios *bios,
i = 0;
fan->nr_fan_trip = 0;
+ fan->fan_mode = NVBIOS_THERM_FAN_OTHER;
while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
s16 value = nv_ro16(bios, entry + 1);
@@ -174,6 +175,8 @@ nvbios_therm_fan_parse(struct nouveau_bios *bios,
break;
case 0x24:
fan->nr_fan_trip++;
+ if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP)
+ fan->fan_mode = NVBIOS_THERM_FAN_TRIP;
cur_trip = &fan->trip[fan->nr_fan_trip - 1];
cur_trip->hysteresis = value & 0xf;
cur_trip->temp = (value & 0xff0) >> 4;
@@ -194,11 +197,19 @@ nvbios_therm_fan_parse(struct nouveau_bios *bios,
fan->slow_down_period = value;
break;
case 0x46:
+ if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR)
+ fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
fan->linear_min_temp = nv_ro08(bios, entry + 1);
fan->linear_max_temp = nv_ro08(bios, entry + 2);
break;
}
}
+ /* starting from fermi, fan management is always linear */
+ if (nv_device(bios)->card_type >= NV_C0 &&
+ fan->fan_mode == NVBIOS_THERM_FAN_OTHER) {
+ fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c
index 8fa34e8152c2..239acfe876c3 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c
@@ -96,5 +96,6 @@ nouveau_devinit_create_(struct nouveau_object *parent,
devinit->post = nouveau_boolopt(device->cfgopt, "NvForcePost", false);
devinit->meminit = impl->meminit;
devinit->pll_set = impl->pll_set;
+ devinit->mmio = impl->mmio;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h
index 6b56a0f4cb40..4fe49cf4c99a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h
@@ -24,6 +24,8 @@
*
*/
+#include <core/device.h>
+
#define NV04_PFB_BOOT_0 0x00100000
# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
@@ -60,10 +62,10 @@
# define NV10_PFB_REFCTRL_VALID_1 (1 << 31)
static inline struct io_mapping *
-fbmem_init(struct pci_dev *pdev)
+fbmem_init(struct nouveau_device *dev)
{
- return io_mapping_create_wc(pci_resource_start(pdev, 1),
- pci_resource_len(pdev, 1));
+ return io_mapping_create_wc(nv_device_resource_start(dev, 1),
+ nv_device_resource_len(dev, 1));
}
static inline void
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/gm107.c
new file mode 100644
index 000000000000..c69bc7f54e37
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/gm107.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+static u64
+gm107_devinit_disable(struct nouveau_devinit *devinit)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 r021c00 = nv_rd32(priv, 0x021c00);
+ u32 r021c04 = nv_rd32(priv, 0x021c04);
+ u64 disable = 0ULL;
+
+ if (r021c00 & 0x00000001)
+ disable |= (1ULL << NVDEV_ENGINE_COPY0);
+ if (r021c00 & 0x00000004)
+ disable |= (1ULL << NVDEV_ENGINE_COPY2);
+ if (r021c04 & 0x00000001)
+ disable |= (1ULL << NVDEV_ENGINE_DISP);
+
+ return disable;
+}
+
+struct nouveau_oclass *
+gm107_devinit_oclass = &(struct nouveau_devinit_impl) {
+ .base.handle = NV_SUBDEV(DEVINIT, 0x07),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nv50_devinit_ctor,
+ .dtor = _nouveau_devinit_dtor,
+ .init = nv50_devinit_init,
+ .fini = _nouveau_devinit_fini,
+ },
+ .pll_set = nvc0_devinit_pll_set,
+ .disable = gm107_devinit_disable,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c
index 7037eae46e44..052ad690b468 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c
@@ -38,7 +38,7 @@ nv04_devinit_meminit(struct nouveau_devinit *devinit)
int i;
/* Map the framebuffer aperture */
- fb = fbmem_init(nv_device(priv)->pdev);
+ fb = fbmem_init(nv_device(priv));
if (!fb) {
nv_error(priv, "failed to map fb\n");
return;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c
index 98b7e6780dc7..4a19c10e5178 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c
@@ -53,7 +53,7 @@ nv05_devinit_meminit(struct nouveau_devinit *devinit)
int i, v;
/* Map the framebuffer aperture */
- fb = fbmem_init(nv_device(priv)->pdev);
+ fb = fbmem_init(nv_device(priv));
if (!fb) {
nv_error(priv, "failed to map fb\n");
return;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c
index 32b3d2131a7f..3b8d657da279 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c
@@ -46,7 +46,7 @@ nv10_devinit_meminit(struct nouveau_devinit *devinit)
mem_width_count = 2;
/* Map the framebuffer aperture */
- fb = fbmem_init(nv_device(priv)->pdev);
+ fb = fbmem_init(nv_device(priv));
if (!fb) {
nv_error(priv, "failed to map fb\n");
return;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c
index 4689ba303b0b..04bc9732644c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c
@@ -37,7 +37,7 @@ nv20_devinit_meminit(struct nouveau_devinit *devinit)
struct io_mapping *fb;
/* Map the framebuffer aperture */
- fb = fbmem_init(nv_device(priv)->pdev);
+ fb = fbmem_init(nv_device(priv));
if (!fb) {
nv_error(priv, "failed to map fb\n");
return;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.h
index 141c27e9f182..51d5076333ec 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.h
@@ -5,6 +5,7 @@
struct nv50_devinit_priv {
struct nouveau_devinit base;
+ u32 r001540;
};
int nv50_devinit_ctor(struct nouveau_object *, struct nouveau_object *,
@@ -15,4 +16,6 @@ int nv50_devinit_pll_set(struct nouveau_devinit *, u32, u32);
int nva3_devinit_pll_set(struct nouveau_devinit *, u32, u32);
+int nvc0_devinit_pll_set(struct nouveau_devinit *, u32, u32);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c
index 6dedf1dad7f7..006cf348bda7 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c
@@ -81,6 +81,55 @@ nva3_devinit_disable(struct nouveau_devinit *devinit)
return disable;
}
+static u32
+nva3_devinit_mmio_part[] = {
+ 0x100720, 0x1008bc, 4,
+ 0x100a20, 0x100adc, 4,
+ 0x100d80, 0x100ddc, 4,
+ 0x110000, 0x110f9c, 4,
+ 0x111000, 0x11103c, 8,
+ 0x111080, 0x1110fc, 4,
+ 0x111120, 0x1111fc, 4,
+ 0x111300, 0x1114bc, 4,
+ 0,
+};
+
+static u32
+nva3_devinit_mmio(struct nouveau_devinit *devinit, u32 addr)
+{
+ struct nv50_devinit_priv *priv = (void *)devinit;
+ u32 *mmio = nva3_devinit_mmio_part;
+
+ /* the init tables on some boards have INIT_RAM_RESTRICT_ZM_REG_GROUP
+ * instructions which touch registers that may not even exist on
+ * some configurations (Quadro 400), which causes the register
+ * interface to screw up for some amount of time after attempting to
+ * write to one of these, and results in all sorts of things going
+ * horribly wrong.
+ *
+ * the binary driver avoids touching these registers at all, however,
+ * the video bios doesn't care and does what the scripts say. it's
+ * presumed that the io-port access to priv registers isn't effected
+ * by the screw-up bug mentioned above.
+ *
+ * really, a new opcode should've been invented to handle these
+ * requirements, but whatever, it's too late for that now.
+ */
+ while (mmio[0]) {
+ if (addr >= mmio[0] && addr <= mmio[1]) {
+ u32 part = (addr / mmio[2]) & 7;
+ if (!priv->r001540)
+ priv->r001540 = nv_rd32(priv, 0x001540);
+ if (part >= hweight8((priv->r001540 >> 16) & 0xff))
+ return ~0;
+ return addr;
+ }
+ mmio += 3;
+ }
+
+ return addr;
+}
+
struct nouveau_oclass *
nva3_devinit_oclass = &(struct nouveau_devinit_impl) {
.base.handle = NV_SUBDEV(DEVINIT, 0xa3),
@@ -92,4 +141,5 @@ nva3_devinit_oclass = &(struct nouveau_devinit_impl) {
},
.pll_set = nva3_devinit_pll_set,
.disable = nva3_devinit_disable,
+ .mmio = nva3_devinit_mmio,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c
index fa7e63766b1b..30c765747eea 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c
@@ -24,7 +24,7 @@
#include "nv50.h"
-static int
+int
nvc0_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
{
struct nv50_devinit_priv *priv = (void *)devinit;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h
index 822a2fbf44a5..f0e8683ad840 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h
@@ -11,6 +11,7 @@ struct nouveau_devinit_impl {
void (*meminit)(struct nouveau_devinit *);
int (*pll_set)(struct nouveau_devinit *, u32 type, u32 freq);
u64 (*disable)(struct nouveau_devinit *);
+ u32 (*mmio)(struct nouveau_devinit *, u32);
};
#define nouveau_devinit_create(p,e,o,d) \
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gm107.c
new file mode 100644
index 000000000000..c4840aedc2dc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gm107.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nvc0.h"
+
+struct nouveau_oclass *
+gm107_fb_oclass = &(struct nouveau_fb_impl) {
+ .base.handle = NV_SUBDEV(FB, 0x07),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_fb_ctor,
+ .dtor = nvc0_fb_dtor,
+ .init = nvc0_fb_init,
+ .fini = _nouveau_fb_fini,
+ },
+ .memtype = nvc0_fb_memtype_valid,
+ .ram = &gm107_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
index cbc7f00c1278..1fc55c1e91a1 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
@@ -250,10 +250,8 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (priv->r100c08_page) {
- priv->r100c08 = pci_map_page(device->pdev, priv->r100c08_page,
- 0, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(device->pdev, priv->r100c08))
+ priv->r100c08 = nv_device_map_page(device, priv->r100c08_page);
+ if (!priv->r100c08)
nv_warn(priv, "failed 0x100c08 page map\n");
} else {
nv_warn(priv, "failed 0x100c08 page alloc\n");
@@ -270,8 +268,7 @@ nv50_fb_dtor(struct nouveau_object *object)
struct nv50_fb_priv *priv = (void *)object;
if (priv->r100c08_page) {
- pci_unmap_page(device->pdev, priv->r100c08, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
+ nv_device_unmap_page(device, priv->r100c08);
__free_page(priv->r100c08_page);
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
index 45470e1f0385..0670ae33ee45 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
@@ -70,8 +70,7 @@ nvc0_fb_dtor(struct nouveau_object *object)
struct nvc0_fb_priv *priv = (void *)object;
if (priv->r100c10_page) {
- pci_unmap_page(device->pdev, priv->r100c10, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
+ nv_device_unmap_page(device, priv->r100c10);
__free_page(priv->r100c10_page);
}
@@ -94,10 +93,8 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (priv->r100c10_page) {
- priv->r100c10 = pci_map_page(device->pdev, priv->r100c10_page,
- 0, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(device->pdev, priv->r100c10))
+ priv->r100c10 = nv_device_map_page(device, priv->r100c10_page);
+ if (!priv->r100c10)
return -EFAULT;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h
index 9e1931eb746f..705a06d755ad 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.h
@@ -18,12 +18,14 @@ int nvc0_fb_init(struct nouveau_object *);
bool nvc0_fb_memtype_valid(struct nouveau_fb *, u32);
-#define nvc0_ram_create(p,e,o,d) \
- nvc0_ram_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvc0_ram_create(p,e,o,m,d) \
+ nvc0_ram_create_((p), (e), (o), (m), sizeof(**d), (void **)d)
int nvc0_ram_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int, void **);
+ struct nouveau_oclass *, u32, int, void **);
int nvc0_ram_get(struct nouveau_fb *, u64, u32, u32, u32,
struct nouveau_mem **);
void nvc0_ram_put(struct nouveau_fb *, struct nouveau_mem **);
+int nve0_ram_init(struct nouveau_object*);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
index edaf95dee612..da74c889aed4 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
@@ -32,6 +32,7 @@ extern struct nouveau_oclass nva3_ram_oclass;
extern struct nouveau_oclass nvaa_ram_oclass;
extern struct nouveau_oclass nvc0_ram_oclass;
extern struct nouveau_oclass nve0_ram_oclass;
+extern struct nouveau_oclass gm107_ram_oclass;
int nouveau_sddr3_calc(struct nouveau_ram *ram);
int nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramgm107.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgm107.c
new file mode 100644
index 000000000000..4c6363595c79
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgm107.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nvc0.h"
+
+struct gm107_ram {
+ struct nouveau_ram base;
+};
+
+static int
+gm107_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gm107_ram *ram;
+ int ret;
+
+ ret = nvc0_ram_create(parent, engine, oclass, 0x021c14, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nouveau_oclass
+gm107_ram_oclass = {
+ .handle = 0,
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gm107_ram_ctor,
+ .dtor = _nouveau_ram_dtor,
+ .init = nve0_ram_init,
+ .fini = _nouveau_ram_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
index c7fdb3a9e88b..ef91b6e893af 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
@@ -91,7 +91,7 @@ nv50_ram_calc(struct nouveau_fb *pfb, u32 freq)
} while (perfE.memory < freq);
/* locate specific data set for the attached memory */
- strap = nvbios_ramcfg_index(bios);
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
if (strap >= cnt) {
nv_error(pfb, "invalid ramcfg strap\n");
return -EINVAL;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
index f4ae8aa46a25..6eb97f16fbda 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
@@ -98,7 +98,7 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
}
/* locate specific data set for the attached memory */
- strap = nvbios_ramcfg_index(bios);
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
if (strap >= cnt) {
nv_error(pfb, "invalid ramcfg strap\n");
return -EINVAL;
@@ -335,21 +335,23 @@ nva3_ram_init(struct nouveau_object *object)
/* prepare for ddr link training, and load training patterns */
switch (ram->base.type) {
case NV_MEM_TYPE_DDR3: {
- static const u32 pattern[16] = {
- 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee,
- 0x00000000, 0x11111111, 0x44444444, 0xdddddddd,
- 0x33333333, 0x55555555, 0x77777777, 0x66666666,
- 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb,
- };
-
- nv_wr32(pfb, 0x100538, 0x10001ff6); /*XXX*/
- nv_wr32(pfb, 0x1005a8, 0x0000ffff);
- nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001);
- for (i = 0; i < 0x30; i++) {
- nv_wr32(pfb, 0x10f8c0, (i << 8) | i);
- nv_wr32(pfb, 0x10f8e0, (i << 8) | i);
- nv_wr32(pfb, 0x10f900, pattern[i % 16]);
- nv_wr32(pfb, 0x10f920, pattern[i % 16]);
+ if (nv_device(pfb)->chipset == 0xa8) {
+ static const u32 pattern[16] = {
+ 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee,
+ 0x00000000, 0x11111111, 0x44444444, 0xdddddddd,
+ 0x33333333, 0x55555555, 0x77777777, 0x66666666,
+ 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb,
+ };
+
+ nv_wr32(pfb, 0x100538, 0x10001ff6); /*XXX*/
+ nv_wr32(pfb, 0x1005a8, 0x0000ffff);
+ nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001);
+ for (i = 0; i < 0x30; i++) {
+ nv_wr32(pfb, 0x10f8c0, (i << 8) | i);
+ nv_wr32(pfb, 0x10f8e0, (i << 8) | i);
+ nv_wr32(pfb, 0x10f900, pattern[i % 16]);
+ nv_wr32(pfb, 0x10f920, pattern[i % 16]);
+ }
}
}
break;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
index 0391b824ee76..8edc92224c84 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
@@ -152,7 +152,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq)
}
/* locate specific data set for the attached memory */
- strap = nvbios_ramcfg_index(bios);
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
if (strap >= cnt) {
nv_error(pfb, "invalid ramcfg strap\n");
return -EINVAL;
@@ -505,7 +505,8 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
int
nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, int size, void **pobject)
+ struct nouveau_oclass *oclass, u32 maskaddr, int size,
+ void **pobject)
{
struct nouveau_fb *pfb = nouveau_fb(parent);
struct nouveau_bios *bios = nouveau_bios(pfb);
@@ -513,7 +514,7 @@ nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
u32 parts = nv_rd32(pfb, 0x022438);
- u32 pmask = nv_rd32(pfb, 0x022554);
+ u32 pmask = nv_rd32(pfb, maskaddr);
u32 bsize = nv_rd32(pfb, 0x10f20c);
u32 offset, length;
bool uniform = true;
@@ -630,7 +631,7 @@ nvc0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nvc0_ram *ram;
int ret;
- ret = nvc0_ram_create(parent, engine, oclass, &ram);
+ ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram);
*pobject = nv_object(ram);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
index 3257c522a021..16752192cf87 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
@@ -950,10 +950,11 @@ nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq,
}
/* locate specific data set for the attached memory */
+ strap = nvbios_ramcfg_index(nv_subdev(pfb));
ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data,
ram->base.rammap.version,
- ram->base.rammap.size, cnt, len,
- nvbios_ramcfg_index(bios),
+ ram->base.rammap.size,
+ cnt, len, strap,
&ram->base.ramcfg.version,
&ram->base.ramcfg.size,
&data->bios);
@@ -1123,7 +1124,7 @@ nve0_ram_tidy(struct nouveau_fb *pfb)
ram_exec(fuc, false);
}
-static int
+int
nve0_ram_init(struct nouveau_object *object)
{
struct nouveau_fb *pfb = (void *)object->parent;
@@ -1226,7 +1227,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
int ret, i;
u32 tmp;
- ret = nvc0_ram_create(parent, engine, oclass, &ram);
+ ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram);
*pobject = nv_object(ram);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
index c4c1d415e7fe..2ef774731629 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
@@ -46,7 +46,8 @@ nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
u8 unk0 = !!(data & 0x02000000);
u8 unk1 = !!(data & 0x04000000);
u32 val = (unk1 << 16) | unk0;
- u32 reg = regs[line >> 4]; line &= 0x0f;
+ u32 reg = regs[line >> 4];
+ u32 lsh = line & 0x0f;
if ( func == DCB_GPIO_UNUSED ||
(match != DCB_GPIO_UNUSED && match != func))
@@ -54,7 +55,7 @@ nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
gpio->set(gpio, 0, func, line, defs);
- nv_mask(priv, reg, 0x00010001 << line, val << line);
+ nv_mask(priv, reg, 0x00010001 << lsh, val << lsh);
}
}
@@ -79,7 +80,7 @@ nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
if (nv50_gpio_location(line, &reg, &shift))
return -EINVAL;
- nv_mask(gpio, reg, 7 << shift, (((dir ^ 1) << 1) | out) << shift);
+ nv_mask(gpio, reg, 3 << shift, (((dir ^ 1) << 1) | out) << shift);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
index c33c03d2f4af..378e05b88e6f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
@@ -111,7 +111,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
snprintf(port->adapter.name, sizeof(port->adapter.name),
"nouveau-%s-%d", device->name, index);
port->adapter.owner = THIS_MODULE;
- port->adapter.dev.parent = &device->pdev->dev;
+ port->adapter.dev.parent = nv_device_base(device);
port->index = index;
port->func = func;
i2c_set_adapdata(&port->adapter, i2c);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c
index ec0b9661d614..8803809f9fc5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c
@@ -50,7 +50,6 @@ nv40_instmem_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_object **pobject)
{
struct nouveau_device *device = nv_device(parent);
- struct pci_dev *pdev = device->pdev;
struct nv04_instmem_priv *priv;
int ret, bar, vs;
@@ -60,13 +59,13 @@ nv40_instmem_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return ret;
/* map bar */
- if (pci_resource_len(pdev, 2))
+ if (nv_device_resource_len(device, 2))
bar = 2;
else
bar = 3;
- priv->iomem = ioremap(pci_resource_start(pdev, bar),
- pci_resource_len(pdev, bar));
+ priv->iomem = ioremap(nv_device_resource_start(device, bar),
+ nv_device_resource_len(device, bar));
if (!priv->iomem) {
nv_error(priv, "unable to map PRAMIN BAR\n");
return -EFAULT;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.c
index cce65cc56514..f2f3338a967a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.c
@@ -22,44 +22,35 @@
* Authors: Ben Skeggs
*/
-#include <subdev/ltcg.h>
#include <subdev/fb.h>
#include <subdev/timer.h>
-struct nvc0_ltcg_priv {
- struct nouveau_ltcg base;
- u32 part_nr;
- u32 subp_nr;
- u32 num_tags;
- u32 tag_base;
- struct nouveau_mm tags;
- struct nouveau_mm_node *tag_ram;
-};
+#include "gf100.h"
static void
-nvc0_ltcg_subp_isr(struct nvc0_ltcg_priv *priv, int unit, int subp)
+gf100_ltcg_lts_isr(struct gf100_ltcg_priv *priv, int ltc, int lts)
{
- u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400);
- u32 stat = nv_rd32(priv, subp_base + 0x020);
+ u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400);
+ u32 stat = nv_rd32(priv, base + 0x020);
if (stat) {
- nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", unit, subp, stat);
- nv_wr32(priv, subp_base + 0x020, stat);
+ nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat);
+ nv_wr32(priv, base + 0x020, stat);
}
}
static void
-nvc0_ltcg_intr(struct nouveau_subdev *subdev)
+gf100_ltcg_intr(struct nouveau_subdev *subdev)
{
- struct nvc0_ltcg_priv *priv = (void *)subdev;
- u32 units;
-
- units = nv_rd32(priv, 0x00017c);
- while (units) {
- u32 subp, unit = ffs(units) - 1;
- for (subp = 0; subp < priv->subp_nr; subp++)
- nvc0_ltcg_subp_isr(priv, unit, subp);
- units &= ~(1 << unit);
+ struct gf100_ltcg_priv *priv = (void *)subdev;
+ u32 mask;
+
+ mask = nv_rd32(priv, 0x00017c);
+ while (mask) {
+ u32 lts, ltc = __ffs(mask);
+ for (lts = 0; lts < priv->lts_nr; lts++)
+ gf100_ltcg_lts_isr(priv, ltc, lts);
+ mask &= ~(1 << ltc);
}
/* we do something horribly wrong and upset PMFB a lot, so mask off
@@ -68,11 +59,11 @@ nvc0_ltcg_intr(struct nouveau_subdev *subdev)
nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
}
-static int
-nvc0_ltcg_tags_alloc(struct nouveau_ltcg *ltcg, u32 n,
+int
+gf100_ltcg_tags_alloc(struct nouveau_ltcg *ltcg, u32 n,
struct nouveau_mm_node **pnode)
{
- struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
int ret;
ret = nouveau_mm_head(&priv->tags, 1, n, n, 1, pnode);
@@ -82,18 +73,18 @@ nvc0_ltcg_tags_alloc(struct nouveau_ltcg *ltcg, u32 n,
return ret;
}
-static void
-nvc0_ltcg_tags_free(struct nouveau_ltcg *ltcg, struct nouveau_mm_node **pnode)
+void
+gf100_ltcg_tags_free(struct nouveau_ltcg *ltcg, struct nouveau_mm_node **pnode)
{
- struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
nouveau_mm_free(&priv->tags, pnode);
}
static void
-nvc0_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
+gf100_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
{
- struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
u32 last = first + count - 1;
int p, i;
@@ -104,16 +95,16 @@ nvc0_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
nv_wr32(priv, 0x17e8c8, 0x4); /* trigger clear */
/* wait until it's finished with clearing */
- for (p = 0; p < priv->part_nr; ++p) {
- for (i = 0; i < priv->subp_nr; ++i)
+ for (p = 0; p < priv->ltc_nr; ++p) {
+ for (i = 0; i < priv->lts_nr; ++i)
nv_wait(priv, 0x1410c8 + p * 0x2000 + i * 0x400, ~0, 0);
}
}
/* TODO: Figure out tag memory details and drop the over-cautious allocation.
*/
-static int
-nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
+int
+gf100_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct gf100_ltcg_priv *priv)
{
u32 tag_size, tag_margin, tag_align;
int ret;
@@ -124,7 +115,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
priv->num_tags = 1 << 17; /* we have 17 bits in PTE */
priv->num_tags = (priv->num_tags + 63) & ~63; /* round up to 64 */
- tag_align = priv->part_nr * 0x800;
+ tag_align = priv->ltc_nr * 0x800;
tag_margin = (tag_align < 0x6000) ? 0x6000 : tag_align;
/* 4 part 4 sub: 0x2000 bytes for 56 tags */
@@ -157,11 +148,11 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
}
static int
-nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+gf100_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nvc0_ltcg_priv *priv;
+ struct gf100_ltcg_priv *priv;
struct nouveau_fb *pfb = nouveau_fb(parent);
u32 parts, mask;
int ret, i;
@@ -175,27 +166,27 @@ nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
mask = nv_rd32(priv, 0x022554);
for (i = 0; i < parts; i++) {
if (!(mask & (1 << i)))
- priv->part_nr++;
+ priv->ltc_nr++;
}
- priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 28;
+ priv->lts_nr = nv_rd32(priv, 0x17e8dc) >> 28;
- ret = nvc0_ltcg_init_tag_ram(pfb, priv);
+ ret = gf100_ltcg_init_tag_ram(pfb, priv);
if (ret)
return ret;
- priv->base.tags_alloc = nvc0_ltcg_tags_alloc;
- priv->base.tags_free = nvc0_ltcg_tags_free;
- priv->base.tags_clear = nvc0_ltcg_tags_clear;
+ priv->base.tags_alloc = gf100_ltcg_tags_alloc;
+ priv->base.tags_free = gf100_ltcg_tags_free;
+ priv->base.tags_clear = gf100_ltcg_tags_clear;
- nv_subdev(priv)->intr = nvc0_ltcg_intr;
+ nv_subdev(priv)->intr = gf100_ltcg_intr;
return 0;
}
-static void
-nvc0_ltcg_dtor(struct nouveau_object *object)
+void
+gf100_ltcg_dtor(struct nouveau_object *object)
{
struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
- struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
struct nouveau_fb *pfb = nouveau_fb(ltcg->base.base.parent);
nouveau_mm_fini(&priv->tags);
@@ -205,10 +196,10 @@ nvc0_ltcg_dtor(struct nouveau_object *object)
}
static int
-nvc0_ltcg_init(struct nouveau_object *object)
+gf100_ltcg_init(struct nouveau_object *object)
{
struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
- struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
int ret;
ret = nouveau_ltcg_init(ltcg);
@@ -216,20 +207,20 @@ nvc0_ltcg_init(struct nouveau_object *object)
return ret;
nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
- nv_wr32(priv, 0x17e8d8, priv->part_nr);
+ nv_wr32(priv, 0x17e8d8, priv->ltc_nr);
if (nv_device(ltcg)->card_type >= NV_E0)
- nv_wr32(priv, 0x17e000, priv->part_nr);
+ nv_wr32(priv, 0x17e000, priv->ltc_nr);
nv_wr32(priv, 0x17e8d4, priv->tag_base);
return 0;
}
-struct nouveau_oclass
-nvc0_ltcg_oclass = {
+struct nouveau_oclass *
+gf100_ltcg_oclass = &(struct nouveau_oclass) {
.handle = NV_SUBDEV(LTCG, 0xc0),
.ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvc0_ltcg_ctor,
- .dtor = nvc0_ltcg_dtor,
- .init = nvc0_ltcg_init,
+ .ctor = gf100_ltcg_ctor,
+ .dtor = gf100_ltcg_dtor,
+ .init = gf100_ltcg_init,
.fini = _nouveau_ltcg_fini,
},
};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h
new file mode 100644
index 000000000000..87b10b8412ea
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h
@@ -0,0 +1,21 @@
+#ifndef __NVKM_LTCG_PRIV_GF100_H__
+#define __NVKM_LTCG_PRIV_GF100_H__
+
+#include <subdev/ltcg.h>
+
+struct gf100_ltcg_priv {
+ struct nouveau_ltcg base;
+ u32 ltc_nr;
+ u32 lts_nr;
+ u32 num_tags;
+ u32 tag_base;
+ struct nouveau_mm tags;
+ struct nouveau_mm_node *tag_ram;
+};
+
+void gf100_ltcg_dtor(struct nouveau_object *);
+int gf100_ltcg_init_tag_ram(struct nouveau_fb *, struct gf100_ltcg_priv *);
+int gf100_ltcg_tags_alloc(struct nouveau_ltcg *, u32, struct nouveau_mm_node **);
+void gf100_ltcg_tags_free(struct nouveau_ltcg *, struct nouveau_mm_node **);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c
new file mode 100644
index 000000000000..e79d0e81de40
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/fb.h>
+#include <subdev/timer.h>
+
+#include "gf100.h"
+
+static void
+gm107_ltcg_lts_isr(struct gf100_ltcg_priv *priv, int ltc, int lts)
+{
+ u32 base = 0x140000 + (ltc * 0x2000) + (lts * 0x400);
+ u32 stat = nv_rd32(priv, base + 0x00c);
+
+ if (stat) {
+ nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat);
+ nv_wr32(priv, base + 0x00c, stat);
+ }
+}
+
+static void
+gm107_ltcg_intr(struct nouveau_subdev *subdev)
+{
+ struct gf100_ltcg_priv *priv = (void *)subdev;
+ u32 mask;
+
+ mask = nv_rd32(priv, 0x00017c);
+ while (mask) {
+ u32 lts, ltc = __ffs(mask);
+ for (lts = 0; lts < priv->lts_nr; lts++)
+ gm107_ltcg_lts_isr(priv, ltc, lts);
+ mask &= ~(1 << ltc);
+ }
+
+ /* we do something horribly wrong and upset PMFB a lot, so mask off
+ * interrupts from it after the first one until it's fixed
+ */
+ nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
+}
+
+static void
+gm107_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
+{
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
+ u32 last = first + count - 1;
+ int p, i;
+
+ BUG_ON((first > last) || (last >= priv->num_tags));
+
+ nv_wr32(priv, 0x17e270, first);
+ nv_wr32(priv, 0x17e274, last);
+ nv_wr32(priv, 0x17e26c, 0x4); /* trigger clear */
+
+ /* wait until it's finished with clearing */
+ for (p = 0; p < priv->ltc_nr; ++p) {
+ for (i = 0; i < priv->lts_nr; ++i)
+ nv_wait(priv, 0x14046c + p * 0x2000 + i * 0x200, ~0, 0);
+ }
+}
+
+static int
+gm107_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gf100_ltcg_priv *priv;
+ struct nouveau_fb *pfb = nouveau_fb(parent);
+ u32 parts, mask;
+ int ret, i;
+
+ ret = nouveau_ltcg_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ parts = nv_rd32(priv, 0x022438);
+ mask = nv_rd32(priv, 0x021c14);
+ for (i = 0; i < parts; i++) {
+ if (!(mask & (1 << i)))
+ priv->ltc_nr++;
+ }
+ priv->lts_nr = nv_rd32(priv, 0x17e280) >> 28;
+
+ ret = gf100_ltcg_init_tag_ram(pfb, priv);
+ if (ret)
+ return ret;
+
+ priv->base.tags_alloc = gf100_ltcg_tags_alloc;
+ priv->base.tags_free = gf100_ltcg_tags_free;
+ priv->base.tags_clear = gm107_ltcg_tags_clear;
+
+ nv_subdev(priv)->intr = gm107_ltcg_intr;
+ return 0;
+}
+
+static int
+gm107_ltcg_init(struct nouveau_object *object)
+{
+ struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
+ struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
+ int ret;
+
+ ret = nouveau_ltcg_init(ltcg);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x17e27c, priv->ltc_nr);
+ nv_wr32(priv, 0x17e278, priv->tag_base);
+ return 0;
+}
+
+struct nouveau_oclass *
+gm107_ltcg_oclass = &(struct nouveau_oclass) {
+ .handle = NV_SUBDEV(LTCG, 0xff),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gm107_ltcg_ctor,
+ .dtor = gf100_ltcg_dtor,
+ .init = gm107_ltcg_init,
+ .fini = _nouveau_ltcg_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
index b4b9943773bc..8a5555192fa5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
@@ -93,7 +93,7 @@ _nouveau_mc_dtor(struct nouveau_object *object)
{
struct nouveau_device *device = nv_device(object);
struct nouveau_mc *pmc = (void *)object;
- free_irq(device->pdev->irq, pmc);
+ free_irq(pmc->irq, pmc);
if (pmc->use_msi)
pci_disable_msi(device->pdev);
nouveau_subdev_destroy(&pmc->base);
@@ -114,33 +114,44 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- switch (device->pdev->device & 0x0ff0) {
- case 0x00f0:
- case 0x02e0:
- /* BR02? NFI how these would be handled yet exactly */
- break;
- default:
- switch (device->chipset) {
- case 0xaa: break; /* reported broken, nv also disable it */
- default:
- pmc->use_msi = true;
+ if (nv_device_is_pci(device))
+ switch (device->pdev->device & 0x0ff0) {
+ case 0x00f0:
+ case 0x02e0:
+ /* BR02? NFI how these would be handled yet exactly */
break;
+ default:
+ switch (device->chipset) {
+ case 0xaa:
+ /* reported broken, nv also disable it */
+ break;
+ default:
+ pmc->use_msi = true;
+ break;
}
- }
- pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI", pmc->use_msi);
- if (pmc->use_msi && oclass->msi_rearm) {
- pmc->use_msi = pci_enable_msi(device->pdev) == 0;
- if (pmc->use_msi) {
- nv_info(pmc, "MSI interrupts enabled\n");
- oclass->msi_rearm(pmc);
+ pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI",
+ pmc->use_msi);
+
+ if (pmc->use_msi && oclass->msi_rearm) {
+ pmc->use_msi = pci_enable_msi(device->pdev) == 0;
+ if (pmc->use_msi) {
+ nv_info(pmc, "MSI interrupts enabled\n");
+ oclass->msi_rearm(pmc);
+ }
+ } else {
+ pmc->use_msi = false;
}
- } else {
- pmc->use_msi = false;
}
- ret = request_irq(device->pdev->irq, nouveau_mc_intr,
- IRQF_SHARED, "nouveau", pmc);
+ ret = nv_device_get_irq(device, true);
+ if (ret < 0)
+ return ret;
+ pmc->irq = ret;
+
+ ret = request_irq(pmc->irq, nouveau_mc_intr, IRQF_SHARED, "nouveau",
+ pmc);
+
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mxm/base.c b/drivers/gpu/drm/nouveau/core/subdev/mxm/base.c
index 13c5af88a601..51fcf7960417 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mxm/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mxm/base.c
@@ -96,7 +96,7 @@ mxm_shadow_dsm(struct nouveau_mxm *mxm, u8 version)
acpi_handle handle;
int rev;
- handle = ACPI_HANDLE(&device->pdev->dev);
+ handle = ACPI_HANDLE(nv_device_base(device));
if (!handle)
return false;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
index 80e584a1bd1c..9ad01da6eacb 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
@@ -110,16 +110,18 @@ nouveau_therm_update(struct nouveau_therm *therm, int mode)
poll = false;
break;
case NOUVEAU_THERM_CTRL_AUTO:
- if (priv->fan->bios.nr_fan_trip) {
+ switch(priv->fan->bios.fan_mode) {
+ case NVBIOS_THERM_FAN_TRIP:
duty = nouveau_therm_update_trip(therm);
- } else
- if (priv->fan->bios.linear_min_temp ||
- priv->fan->bios.linear_max_temp) {
+ break;
+ case NVBIOS_THERM_FAN_LINEAR:
duty = nouveau_therm_update_linear(therm);
- } else {
+ break;
+ case NVBIOS_THERM_FAN_OTHER:
if (priv->cstate)
duty = priv->cstate;
poll = false;
+ break;
}
immd = false;
break;
@@ -179,7 +181,7 @@ nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode)
/* do not allow automatic fan management if the thermal sensor is
* not available */
- if (priv->mode == NOUVEAU_THERM_CTRL_AUTO && therm->temp_get(therm) < 0)
+ if (mode == NOUVEAU_THERM_CTRL_AUTO && therm->temp_get(therm) < 0)
return -EINVAL;
if (priv->mode == mode)
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
index 95f6129eeede..016990a8252c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
@@ -54,8 +54,10 @@ nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target)
/* check that we're not already at the target duty cycle */
duty = fan->get(therm);
- if (duty == target)
- goto done;
+ if (duty == target) {
+ spin_unlock_irqrestore(&fan->lock, flags);
+ return 0;
+ }
/* smooth out the fanspeed increase/decrease */
if (!immediate && duty >= 0) {
@@ -73,8 +75,15 @@ nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target)
nv_debug(therm, "FAN update: %d\n", duty);
ret = fan->set(therm, duty);
- if (ret)
- goto done;
+ if (ret) {
+ spin_unlock_irqrestore(&fan->lock, flags);
+ return ret;
+ }
+
+ /* fan speed updated, drop the fan lock before grabbing the
+ * alarm-scheduling lock and risking a deadlock
+ */
+ spin_unlock_irqrestore(&fan->lock, flags);
/* schedule next fan update, if not at target speed already */
if (list_empty(&fan->alarm.head) && target != duty) {
@@ -92,8 +101,6 @@ nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target)
ptimer->alarm(ptimer, delay * 1000 * 1000, &fan->alarm);
}
-done:
- spin_unlock_irqrestore(&fan->lock, flags);
return ret;
}
@@ -185,11 +192,8 @@ nouveau_therm_fan_set_defaults(struct nouveau_therm *therm)
priv->fan->bios.max_duty = 100;
priv->fan->bios.bump_period = 500;
priv->fan->bios.slow_down_period = 2000;
-/*XXX: talk to mupuf */
-#if 0
priv->fan->bios.linear_min_temp = 40;
priv->fan->bios.linear_max_temp = 85;
-#endif
}
static void
@@ -235,7 +239,8 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm)
/* attempt to locate a drivable fan, and determine control method */
ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func);
if (ret == 0) {
- if (func.log[0] & DCB_GPIO_LOG_DIR_IN) {
+ /* FIXME: is this really the place to perform such checks ? */
+ if (func.line != 16 && func.log[0] & DCB_GPIO_LOG_DIR_IN) {
nv_debug(therm, "GPIO_FAN is in input mode\n");
ret = -EINVAL;
} else {
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
index 5f71db8e8992..9a5c07340263 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
@@ -67,7 +67,7 @@ nouveau_fanpwm_set(struct nouveau_therm *therm, int percent)
if (priv->base.bios.pwm_freq) {
divs = 1;
if (therm->pwm_clock)
- divs = therm->pwm_clock(therm);
+ divs = therm->pwm_clock(therm, priv->func.line);
divs /= priv->base.bios.pwm_freq;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
index 8cf7597a2182..321db927d638 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
@@ -93,7 +93,7 @@ nv50_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
}
int
-nv50_fan_pwm_clock(struct nouveau_therm *therm)
+nv50_fan_pwm_clock(struct nouveau_therm *therm, int line)
{
int chipset = nv_device(therm)->chipset;
int crystal = nv_device(therm)->crystal;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
index 4dd4f81ae873..43fec17ea540 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
@@ -32,10 +32,12 @@ static int
pwm_info(struct nouveau_therm *therm, int line)
{
u32 gpio = nv_rd32(therm, 0x00d610 + (line * 0x04));
+
switch (gpio & 0x000000c0) {
case 0x00000000: /* normal mode, possibly pwm forced off by us */
case 0x00000040: /* nvio special */
switch (gpio & 0x0000001f) {
+ case 0x00: return 2;
case 0x19: return 1;
case 0x1c: return 0;
default:
@@ -56,8 +58,9 @@ nvd0_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
int indx = pwm_info(therm, line);
if (indx < 0)
return indx;
-
- nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+ else if (indx < 2)
+ nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+ /* nothing to do for indx == 2, it seems hardwired to PTHERM */
return 0;
}
@@ -67,10 +70,15 @@ nvd0_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
int indx = pwm_info(therm, line);
if (indx < 0)
return indx;
-
- if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
- *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
- *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+ else if (indx < 2) {
+ if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
+ *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
+ *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+ return 0;
+ }
+ } else if (indx == 2) {
+ *divs = nv_rd32(therm, 0x0200d8) & 0x1fff;
+ *duty = nv_rd32(therm, 0x0200dc) & 0x1fff;
return 0;
}
@@ -83,16 +91,26 @@ nvd0_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
int indx = pwm_info(therm, line);
if (indx < 0)
return indx;
-
- nv_wr32(therm, 0x00e114 + (indx * 8), divs);
- nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+ else if (indx < 2) {
+ nv_wr32(therm, 0x00e114 + (indx * 8), divs);
+ nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+ } else if (indx == 2) {
+ nv_mask(therm, 0x0200d8, 0x1fff, divs); /* keep the high bits */
+ nv_wr32(therm, 0x0200dc, duty | 0x40000000);
+ }
return 0;
}
static int
-nvd0_fan_pwm_clock(struct nouveau_therm *therm)
+nvd0_fan_pwm_clock(struct nouveau_therm *therm, int line)
{
- return (nv_device(therm)->crystal * 1000) / 20;
+ int indx = pwm_info(therm, line);
+ if (indx < 0)
+ return 0;
+ else if (indx < 2)
+ return (nv_device(therm)->crystal * 1000) / 20;
+ else
+ return nv_device(therm)->crystal * 1000 / 10;
}
static int
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
index 96f8f95693ce..916fca5c7816 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
@@ -143,7 +143,7 @@ void nv40_therm_intr(struct nouveau_subdev *);
int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool);
int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
-int nv50_fan_pwm_clock(struct nouveau_therm *);
+int nv50_fan_pwm_clock(struct nouveau_therm *, int);
int nv84_temp_get(struct nouveau_therm *therm);
int nv84_therm_fini(struct nouveau_object *object, bool suspend);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/timer/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/timer/gk20a.c
new file mode 100644
index 000000000000..37484db1f7fc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/timer/gk20a.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv04.h"
+
+static int
+gk20a_timer_init(struct nouveau_object *object)
+{
+ struct nv04_timer_priv *priv = (void *)object;
+ u32 hi = upper_32_bits(priv->suspend_time);
+ u32 lo = lower_32_bits(priv->suspend_time);
+ int ret;
+
+ ret = nouveau_timer_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_debug(priv, "time low : 0x%08x\n", lo);
+ nv_debug(priv, "time high : 0x%08x\n", hi);
+
+ /* restore the time before suspend */
+ nv_wr32(priv, NV04_PTIMER_TIME_1, hi);
+ nv_wr32(priv, NV04_PTIMER_TIME_0, lo);
+ return 0;
+}
+
+struct nouveau_oclass
+gk20a_timer_oclass = {
+ .handle = NV_SUBDEV(TIMER, 0xff),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nv04_timer_ctor,
+ .dtor = nv04_timer_dtor,
+ .init = gk20a_timer_init,
+ .fini = nv04_timer_fini,
+ }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c
index c0bdd10358d7..240ed0b983a9 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c
@@ -22,22 +22,7 @@
* Authors: Ben Skeggs
*/
-#include <subdev/timer.h>
-
-#define NV04_PTIMER_INTR_0 0x009100
-#define NV04_PTIMER_INTR_EN_0 0x009140
-#define NV04_PTIMER_NUMERATOR 0x009200
-#define NV04_PTIMER_DENOMINATOR 0x009210
-#define NV04_PTIMER_TIME_0 0x009400
-#define NV04_PTIMER_TIME_1 0x009410
-#define NV04_PTIMER_ALARM_0 0x009420
-
-struct nv04_timer_priv {
- struct nouveau_timer base;
- struct list_head alarms;
- spinlock_t lock;
- u64 suspend_time;
-};
+#include "nv04.h"
static u64
nv04_timer_read(struct nouveau_timer *ptimer)
@@ -142,35 +127,14 @@ nv04_timer_intr(struct nouveau_subdev *subdev)
}
}
-static int
-nv04_timer_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv04_timer_priv *priv;
- int ret;
-
- ret = nouveau_timer_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.base.intr = nv04_timer_intr;
- priv->base.read = nv04_timer_read;
- priv->base.alarm = nv04_timer_alarm;
- priv->base.alarm_cancel = nv04_timer_alarm_cancel;
- priv->suspend_time = 0;
-
- INIT_LIST_HEAD(&priv->alarms);
- spin_lock_init(&priv->lock);
- return 0;
-}
-
-static void
-nv04_timer_dtor(struct nouveau_object *object)
+int
+nv04_timer_fini(struct nouveau_object *object, bool suspend)
{
struct nv04_timer_priv *priv = (void *)object;
- return nouveau_timer_destroy(&priv->base);
+ if (suspend)
+ priv->suspend_time = nv04_timer_read(&priv->base);
+ nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
+ return nouveau_timer_fini(&priv->base, suspend);
}
static int
@@ -257,14 +221,35 @@ nv04_timer_init(struct nouveau_object *object)
return 0;
}
-static int
-nv04_timer_fini(struct nouveau_object *object, bool suspend)
+void
+nv04_timer_dtor(struct nouveau_object *object)
{
struct nv04_timer_priv *priv = (void *)object;
- if (suspend)
- priv->suspend_time = nv04_timer_read(&priv->base);
- nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
- return nouveau_timer_fini(&priv->base, suspend);
+ return nouveau_timer_destroy(&priv->base);
+}
+
+int
+nv04_timer_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nv04_timer_priv *priv;
+ int ret;
+
+ ret = nouveau_timer_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.intr = nv04_timer_intr;
+ priv->base.read = nv04_timer_read;
+ priv->base.alarm = nv04_timer_alarm;
+ priv->base.alarm_cancel = nv04_timer_alarm_cancel;
+ priv->suspend_time = 0;
+
+ INIT_LIST_HEAD(&priv->alarms);
+ spin_lock_init(&priv->lock);
+ return 0;
}
struct nouveau_oclass
diff --git a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.h b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.h
new file mode 100644
index 000000000000..4bc152697c37
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.h
@@ -0,0 +1,27 @@
+#ifndef __NVKM_TIMER_NV04_H__
+#define __NVKM_TIMER_NV04_H__
+
+#include "priv.h"
+
+#define NV04_PTIMER_INTR_0 0x009100
+#define NV04_PTIMER_INTR_EN_0 0x009140
+#define NV04_PTIMER_NUMERATOR 0x009200
+#define NV04_PTIMER_DENOMINATOR 0x009210
+#define NV04_PTIMER_TIME_0 0x009400
+#define NV04_PTIMER_TIME_1 0x009410
+#define NV04_PTIMER_ALARM_0 0x009420
+
+struct nv04_timer_priv {
+ struct nouveau_timer base;
+ struct list_head alarms;
+ spinlock_t lock;
+ u64 suspend_time;
+};
+
+int nv04_timer_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void nv04_timer_dtor(struct nouveau_object *);
+int nv04_timer_fini(struct nouveau_object *, bool);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/timer/priv.h b/drivers/gpu/drm/nouveau/core/subdev/timer/priv.h
new file mode 100644
index 000000000000..799dae3f2300
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/timer/priv.h
@@ -0,0 +1,6 @@
+#ifndef __NVKM_TIMER_PRIV_H__
+#define __NVKM_TIMER_PRIV_H__
+
+#include <subdev/timer.h>
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 0e3270c3ffd2..41be3424c906 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -239,7 +239,7 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
struct drm_device *dev = crtc->dev;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
/* Calculate our timings */
int horizDisplay = (mode->crtc_hdisplay >> 3) - 1;
@@ -574,7 +574,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
regp->CRTC[NV_CIO_CRE_86] = 0x1;
}
- regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
+ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->primary->fb->depth + 1) / 8;
/* Enable slaved mode (called MODE_TV in nv4ref.h) */
if (lvds_output || tmds_output || tv_output)
regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
@@ -588,7 +588,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL |
NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON;
- if (crtc->fb->depth == 16)
+ if (crtc->primary->fb->depth == 16)
regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
if (nv_device(drm->device)->chipset >= 0x11)
regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
@@ -609,7 +609,7 @@ static int
nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
{
struct nv04_display *disp = nv04_display(crtc->dev);
- struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
+ struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->primary->fb);
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ret;
@@ -808,7 +808,7 @@ nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start,
* mark the lut values as dirty by setting depth==0, and it'll be
* uploaded on the first mode_set_base()
*/
- if (!nv_crtc->base.fb) {
+ if (!nv_crtc->base.primary->fb) {
nv_crtc->lut.depth = 0;
return;
}
@@ -832,7 +832,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
NV_DEBUG(drm, "index %d\n", nv_crtc->index);
/* no fb bound */
- if (!atomic && !crtc->fb) {
+ if (!atomic && !crtc->primary->fb) {
NV_DEBUG(drm, "No FB bound\n");
return 0;
}
@@ -844,8 +844,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
drm_fb = passed_fb;
fb = nouveau_framebuffer(passed_fb);
} else {
- drm_fb = crtc->fb;
- fb = nouveau_framebuffer(crtc->fb);
+ drm_fb = crtc->primary->fb;
+ fb = nouveau_framebuffer(crtc->primary->fb);
}
nv_crtc->fb.offset = fb->nvbo->bo.offset;
@@ -857,9 +857,9 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
/* Update the framebuffer format. */
regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3;
- regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8;
+ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->primary->fb->depth + 1) / 8;
regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
- if (crtc->fb->depth == 16)
+ if (crtc->primary->fb->depth == 16)
regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX);
NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL,
@@ -1048,7 +1048,7 @@ nouveau_crtc_set_config(struct drm_mode_set *set)
/* get a pm reference here */
ret = pm_runtime_get_sync(dev->dev);
- if (ret < 0)
+ if (ret < 0 && ret != -EACCES)
return ret;
ret = drm_crtc_helper_set_config(set);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index 7fdc51e2a571..a2d669b4acf2 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -415,7 +415,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
/* Output property. */
if ((nv_connector->dithering_mode == DITHERING_MODE_ON) ||
(nv_connector->dithering_mode == DITHERING_MODE_AUTO &&
- encoder->crtc->fb->depth > connector->display_info.bpc * 3)) {
+ encoder->crtc->primary->fb->depth > connector->display_info.bpc * 3)) {
if (nv_device(drm->device)->chipset == 0x11)
regp->dither = savep->dither | 0x00010000;
else {
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index 900fae01793e..b13f441c6431 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -97,6 +97,7 @@ nouveau_abi16_swclass(struct nouveau_drm *drm)
case NV_C0:
case NV_D0:
case NV_E0:
+ case GM100:
return 0x906e;
}
@@ -139,7 +140,7 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
/* destroy channel object, all children will be killed too */
if (chan->chan) {
- abi16->handles &= ~(1 << (chan->chan->handle & 0xffff));
+ abi16->handles &= ~(1ULL << (chan->chan->handle & 0xffff));
nouveau_channel_del(&chan->chan);
}
@@ -179,12 +180,21 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
getparam->value = device->chipset;
break;
case NOUVEAU_GETPARAM_PCI_VENDOR:
- getparam->value = dev->pdev->vendor;
+ if (nv_device_is_pci(device))
+ getparam->value = dev->pdev->vendor;
+ else
+ getparam->value = 0;
break;
case NOUVEAU_GETPARAM_PCI_DEVICE:
- getparam->value = dev->pdev->device;
+ if (nv_device_is_pci(device))
+ getparam->value = dev->pdev->device;
+ else
+ getparam->value = 0;
break;
case NOUVEAU_GETPARAM_BUS_TYPE:
+ if (!nv_device_is_pci(device))
+ getparam->value = 3;
+ else
if (drm_pci_device_is_agp(dev))
getparam->value = 0;
else
@@ -270,8 +280,8 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
return nouveau_abi16_put(abi16, -EINVAL);
/* allocate "abi16 channel" data and make up a handle for it */
- init->channel = ffsll(~abi16->handles);
- if (!init->channel--)
+ init->channel = __ffs64(~abi16->handles);
+ if (~abi16->handles == 0)
return nouveau_abi16_put(abi16, -ENOSPC);
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
@@ -280,7 +290,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
INIT_LIST_HEAD(&chan->notifiers);
list_add(&chan->head, &abi16->channels);
- abi16->handles |= (1 << init->channel);
+ abi16->handles |= (1ULL << init->channel);
/* create channel object and initialise dma and fence management */
ret = nouveau_channel_new(drm, cli, NVDRM_DEVICE, NVDRM_CHAN |
diff --git a/drivers/gpu/drm/nouveau/nouveau_agp.c b/drivers/gpu/drm/nouveau/nouveau_agp.c
index 2953c4e91e1a..51666daddb94 100644
--- a/drivers/gpu/drm/nouveau/nouveau_agp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_agp.c
@@ -75,7 +75,7 @@ nouveau_agp_enabled(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
- if (!drm_pci_device_is_agp(dev) || !dev->agp)
+ if (!dev->pdev || !drm_pci_device_is_agp(dev) || !dev->agp)
return false;
if (drm->agp.stat == UNKNOWN) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 4c3feaaa1037..8268a4ccac15 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -1474,9 +1474,12 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
case 0:
entry->dpconf.link_bw = 162000;
break;
- default:
+ case 1:
entry->dpconf.link_bw = 270000;
break;
+ default:
+ entry->dpconf.link_bw = 540000;
+ break;
}
switch ((conf & 0x0f000000) >> 24) {
case 0xf:
@@ -2069,6 +2072,10 @@ nouveau_bios_init(struct drm_device *dev)
struct nvbios *bios = &drm->vbios;
int ret;
+ /* only relevant for PCI devices */
+ if (!dev->pdev)
+ return 0;
+
if (!NVInitVBIOS(dev))
return -ENODEV;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 4aed1714b9ab..b6dc85c614be 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -1255,7 +1255,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
/* fallthrough, tiled memory */
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
- mem->bus.base = pci_resource_start(dev->pdev, 1);
+ mem->bus.base = nv_device_resource_start(nouveau_dev(dev), 1);
mem->bus.is_iomem = true;
if (nv_device(drm->device)->card_type >= NV_50) {
struct nouveau_bar *bar = nouveau_bar(drm->device);
@@ -1293,7 +1293,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct nouveau_device *device = nv_device(drm->device);
- u32 mappable = pci_resource_len(device->pdev, 1) >> PAGE_SHIFT;
+ u32 mappable = nv_device_resource_len(device, 1) >> PAGE_SHIFT;
int ret;
/* as long as the bo isn't in vram, and isn't tiled, we've got
@@ -1331,6 +1331,7 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
{
struct ttm_dma_tt *ttm_dma = (void *)ttm;
struct nouveau_drm *drm;
+ struct nouveau_device *device;
struct drm_device *dev;
unsigned i;
int r;
@@ -1348,6 +1349,7 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
}
drm = nouveau_bdev(ttm->bdev);
+ device = nv_device(drm->device);
dev = drm->dev;
#if __OS_HAS_AGP
@@ -1368,13 +1370,12 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
}
for (i = 0; i < ttm->num_pages; i++) {
- ttm_dma->dma_address[i] = pci_map_page(dev->pdev, ttm->pages[i],
- 0, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(dev->pdev, ttm_dma->dma_address[i])) {
+ ttm_dma->dma_address[i] = nv_device_map_page(device,
+ ttm->pages[i]);
+ if (!ttm_dma->dma_address[i]) {
while (--i) {
- pci_unmap_page(dev->pdev, ttm_dma->dma_address[i],
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ nv_device_unmap_page(device,
+ ttm_dma->dma_address[i]);
ttm_dma->dma_address[i] = 0;
}
ttm_pool_unpopulate(ttm);
@@ -1389,6 +1390,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
{
struct ttm_dma_tt *ttm_dma = (void *)ttm;
struct nouveau_drm *drm;
+ struct nouveau_device *device;
struct drm_device *dev;
unsigned i;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
@@ -1397,6 +1399,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
return;
drm = nouveau_bdev(ttm->bdev);
+ device = nv_device(drm->device);
dev = drm->dev;
#if __OS_HAS_AGP
@@ -1415,8 +1418,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
for (i = 0; i < ttm->num_pages; i++) {
if (ttm_dma->dma_address[i]) {
- pci_unmap_page(dev->pdev, ttm_dma->dma_address[i],
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ nv_device_unmap_page(device, ttm_dma->dma_address[i]);
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c
index cc5152be2cf1..ccb6b452d6d0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.c
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.c
@@ -154,7 +154,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli,
* nfi why this exists, it came from the -nv ddx.
*/
args.flags = NV_DMA_TARGET_PCI | NV_DMA_ACCESS_RDWR;
- args.start = pci_resource_start(device->pdev, 1);
+ args.start = nv_device_resource_start(device, 1);
args.limit = args.start + limit;
} else {
args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 1674882d60d5..d07ce028af51 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -255,7 +255,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
}
ret = pm_runtime_get_sync(connector->dev->dev);
- if (ret < 0)
+ if (ret < 0 && ret != -EACCES)
return conn_status;
i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
@@ -960,7 +960,8 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb)
case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
case DCB_CONNECTOR_HDMI_0 :
- case DCB_CONNECTOR_HDMI_1 : return DRM_MODE_CONNECTOR_HDMIA;
+ case DCB_CONNECTOR_HDMI_1 :
+ case DCB_CONNECTOR_HDMI_C : return DRM_MODE_CONNECTOR_HDMIA;
default:
break;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 24011596af43..3ff030dc1ee3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -105,7 +105,7 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
if (retry) ndelay(crtc->linedur_ns);
} while (retry--);
- *hpos = calc(args.hblanks, args.hblanke, args.htotal, args.hline);
+ *hpos = args.hline;
*vpos = calc(args.vblanks, args.vblanke, args.vtotal, args.vline);
if (stime) *stime = ns_to_ktime(args.time[0]);
if (etime) *etime = ns_to_ktime(args.time[1]);
@@ -419,6 +419,7 @@ int
nouveau_display_create(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_device *device = nouveau_dev(dev);
struct nouveau_display *disp;
int ret, gen;
@@ -459,7 +460,7 @@ nouveau_display_create(struct drm_device *dev)
}
dev->mode_config.funcs = &nouveau_mode_config_funcs;
- dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1);
+ dev->mode_config.fb_base = nv_device_resource_start(device, 1);
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
@@ -488,6 +489,7 @@ nouveau_display_create(struct drm_device *dev)
if (drm->vbios.dcb.entries) {
static const u16 oclass[] = {
+ GM107_DISP_CLASS,
NVF0_DISP_CLASS,
NVE0_DISP_CLASS,
NVD0_DISP_CLASS,
@@ -569,7 +571,7 @@ nouveau_display_suspend(struct drm_device *dev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
- nouveau_fb = nouveau_framebuffer(crtc->fb);
+ nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
if (!nouveau_fb || !nouveau_fb->nvbo)
continue;
@@ -596,7 +598,7 @@ nouveau_display_repin(struct drm_device *dev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
- nouveau_fb = nouveau_framebuffer(crtc->fb);
+ nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
if (!nouveau_fb || !nouveau_fb->nvbo)
continue;
@@ -693,7 +695,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1;
struct drm_device *dev = crtc->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo;
+ struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->primary->fb)->nvbo;
struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;
struct nouveau_page_flip_state *s;
struct nouveau_channel *chan = drm->channel;
@@ -767,7 +769,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
goto fail_unreserve;
/* Update the crtc struct and cleanup */
- crtc->fb = fb;
+ crtc->primary->fb = fb;
nouveau_bo_fence(old_bo, fence);
ttm_bo_unreserve(&old_bo->bo);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 4ee702ac8907..ddd83756b9a2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -33,6 +33,7 @@
#include <core/client.h>
#include <core/gpuobj.h>
#include <core/class.h>
+#include <core/option.h>
#include <engine/device.h>
#include <engine/disp.h>
@@ -81,7 +82,7 @@ module_param_named(runpm, nouveau_runtime_pm, int, 0400);
static struct drm_driver driver;
static u64
-nouveau_name(struct pci_dev *pdev)
+nouveau_pci_name(struct pci_dev *pdev)
{
u64 name = (u64)pci_domain_nr(pdev->bus) << 32;
name |= pdev->bus->number << 16;
@@ -89,15 +90,30 @@ nouveau_name(struct pci_dev *pdev)
return name | PCI_FUNC(pdev->devfn);
}
+static u64
+nouveau_platform_name(struct platform_device *platformdev)
+{
+ return platformdev->id;
+}
+
+static u64
+nouveau_name(struct drm_device *dev)
+{
+ if (dev->pdev)
+ return nouveau_pci_name(dev->pdev);
+ else
+ return nouveau_platform_name(dev->platformdev);
+}
+
static int
-nouveau_cli_create(struct pci_dev *pdev, const char *name,
+nouveau_cli_create(u64 name, const char *sname,
int size, void **pcli)
{
struct nouveau_cli *cli;
int ret;
*pcli = NULL;
- ret = nouveau_client_create_(name, nouveau_name(pdev), nouveau_config,
+ ret = nouveau_client_create_(sname, name, nouveau_config,
nouveau_debug, size, pcli);
cli = *pcli;
if (ret) {
@@ -281,7 +297,8 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
remove_conflicting_framebuffers(aper, "nouveaufb", boot);
kfree(aper);
- ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev),
+ ret = nouveau_device_create(pdev, NOUVEAU_BUS_PCI,
+ nouveau_pci_name(pdev), pci_name(pdev),
nouveau_config, nouveau_debug, &device);
if (ret)
return ret;
@@ -300,22 +317,27 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
static void
-nouveau_get_hdmi_dev(struct drm_device *dev)
+nouveau_get_hdmi_dev(struct nouveau_drm *drm)
{
- struct nouveau_drm *drm = dev->dev_private;
- struct pci_dev *pdev = dev->pdev;
+ struct pci_dev *pdev = drm->dev->pdev;
+
+ if (!pdev) {
+ DRM_INFO("not a PCI device; no HDMI\n");
+ drm->hdmi_device = NULL;
+ return;
+ }
/* subfunction one is a hdmi audio device? */
drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
if (!drm->hdmi_device) {
- DRM_INFO("hdmi device not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
+ NV_DEBUG(drm, "hdmi device not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
return;
}
if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
- DRM_INFO("possible hdmi device not audio %d\n", drm->hdmi_device->class);
+ NV_DEBUG(drm, "possible hdmi device not audio %d\n", drm->hdmi_device->class);
pci_dev_put(drm->hdmi_device);
drm->hdmi_device = NULL;
return;
@@ -330,22 +352,24 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
struct nouveau_drm *drm;
int ret;
- ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
+ ret = nouveau_cli_create(nouveau_name(dev), "DRM", sizeof(*drm),
+ (void **)&drm);
if (ret)
return ret;
dev->dev_private = drm;
drm->dev = dev;
+ nouveau_client(drm)->debug = nouveau_dbgopt(nouveau_debug, "DRM");
INIT_LIST_HEAD(&drm->clients);
spin_lock_init(&drm->tile.lock);
- nouveau_get_hdmi_dev(dev);
+ nouveau_get_hdmi_dev(drm);
/* make sure AGP controller is in a consistent state before we
* (possibly) execute vbios init tables (see nouveau_agp.h)
*/
- if (drm_pci_device_is_agp(dev) && dev->agp) {
+ if (pdev && drm_pci_device_is_agp(dev) && dev->agp) {
/* dummy device object, doesn't init anything, but allows
* agp code access to registers
*/
@@ -486,13 +510,13 @@ nouveau_drm_remove(struct pci_dev *pdev)
}
static int
-nouveau_do_suspend(struct drm_device *dev)
+nouveau_do_suspend(struct drm_device *dev, bool runtime)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
int ret;
- if (dev->mode_config.num_crtc) {
+ if (dev->mode_config.num_crtc && !runtime) {
NV_INFO(drm, "suspending display...\n");
ret = nouveau_display_suspend(dev);
if (ret)
@@ -566,7 +590,7 @@ int nouveau_pmops_suspend(struct device *dev)
if (drm_dev->mode_config.num_crtc)
nouveau_fbcon_set_suspend(drm_dev, 1);
- ret = nouveau_do_suspend(drm_dev);
+ ret = nouveau_do_suspend(drm_dev, false);
if (ret)
return ret;
@@ -646,7 +670,7 @@ static int nouveau_pmops_freeze(struct device *dev)
if (drm_dev->mode_config.num_crtc)
nouveau_fbcon_set_suspend(drm_dev, 1);
- ret = nouveau_do_suspend(drm_dev);
+ ret = nouveau_do_suspend(drm_dev, false);
return ret;
}
@@ -671,7 +695,6 @@ static int nouveau_pmops_thaw(struct device *dev)
static int
nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
{
- struct pci_dev *pdev = dev->pdev;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
char name[32], tmpname[TASK_COMM_LEN];
@@ -679,13 +702,15 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
/* need to bring up power immediately if opening device */
ret = pm_runtime_get_sync(dev->dev);
- if (ret < 0)
+ if (ret < 0 && ret != -EACCES)
return ret;
get_task_comm(tmpname, current);
snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
- ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
+ ret = nouveau_cli_create(nouveau_name(dev), name, sizeof(*cli),
+ (void **)&cli);
+
if (ret)
goto out_suspend;
@@ -762,7 +787,7 @@ long nouveau_drm_ioctl(struct file *filp,
dev = file_priv->minor->dev;
ret = pm_runtime_get_sync(dev->dev);
- if (ret < 0)
+ if (ret < 0 && ret != -EACCES)
return ret;
ret = drm_ioctl(filp, cmd, arg);
@@ -882,7 +907,7 @@ static int nouveau_pmops_runtime_suspend(struct device *dev)
drm_kms_helper_poll_disable(drm_dev);
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
nouveau_switcheroo_optimus_dsm();
- ret = nouveau_do_suspend(drm_dev);
+ ret = nouveau_do_suspend(drm_dev, true);
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3cold);
@@ -908,8 +933,6 @@ static int nouveau_pmops_runtime_resume(struct device *dev)
pci_set_master(pdev);
ret = nouveau_do_resume(drm_dev);
- if (drm_dev->mode_config.num_crtc)
- nouveau_display_resume(drm_dev);
drm_kms_helper_poll_enable(drm_dev);
/* do magic */
nv_mask(device, 0x88488, (1 << 25), (1 << 25));
@@ -980,6 +1003,25 @@ nouveau_drm_pci_driver = {
.driver.pm = &nouveau_pm_ops,
};
+int nouveau_drm_platform_probe(struct platform_device *pdev)
+{
+ struct nouveau_device *device;
+ int ret;
+
+ ret = nouveau_device_create(pdev, NOUVEAU_BUS_PLATFORM,
+ nouveau_platform_name(pdev),
+ dev_name(&pdev->dev), nouveau_config,
+ nouveau_debug, &device);
+
+ ret = drm_platform_init(&driver, pdev);
+ if (ret) {
+ nouveau_object_ref(NULL, (struct nouveau_object **)&device);
+ return ret;
+ }
+
+ return ret;
+}
+
static int __init
nouveau_drm_init(void)
{
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h
index 23ca7a517246..7efbafaf7c1d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -161,10 +161,7 @@ int nouveau_pmops_resume(struct device *);
#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
#define NV_INFO(cli, fmt, args...) nv_info((cli), fmt, ##args)
-#define NV_DEBUG(cli, fmt, args...) do { \
- if (drm_debug & DRM_UT_DRIVER) \
- nv_info((cli), fmt, ##args); \
-} while (0)
+#define NV_DEBUG(cli, fmt, args...) nv_debug((cli), fmt, ##args)
extern int nouveau_modeset;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 7903e0ed3c75..64a42cfd3717 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -528,10 +528,10 @@ nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
struct nouveau_drm *drm = nouveau_drm(dev);
if (drm->fbcon) {
console_lock();
- if (state == 0)
+ if (state == 1)
nouveau_fbcon_save_disable_accel(dev);
fb_set_suspend(drm->fbcon->helper.fbdev, state);
- if (state == 1)
+ if (state == 0)
nouveau_fbcon_restore_accel(dev);
console_unlock();
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 27c3fd89e8ce..c90c0dc0afe8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -228,8 +228,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
struct nouveau_bo *nvbo = NULL;
int ret = 0;
- drm->ttm.bdev.dev_mapping = drm->dev->dev_mapping;
-
if (!pfb->memtype_valid(pfb, req->info.tile_flags)) {
NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags);
return -EINVAL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
index 4aff04fa483c..19fd767bab10 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
@@ -383,8 +383,9 @@ nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a,
long value;
int ret;
- if (strict_strtol(buf, 10, &value) == -EINVAL)
- return -EINVAL;
+ ret = kstrtol(buf, 10, &value);
+ if (ret)
+ return ret;
ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MODE, value);
if (ret)
@@ -587,18 +588,14 @@ nouveau_hwmon_init(struct drm_device *dev)
/* set the default attributes */
ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup);
- if (ret) {
- if (ret)
- goto error;
- }
+ if (ret)
+ goto error;
/* if the card has a working thermal sensor */
if (therm->temp_get(therm) >= 0) {
ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup);
- if (ret) {
- if (ret)
- goto error;
- }
+ if (ret)
+ goto error;
}
/* if the card has a pwm fan */
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
index 89201a17ce75..75dda2b07176 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sysfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
@@ -30,7 +30,7 @@
static inline struct drm_device *
drm_device(struct device *d)
{
- return pci_get_drvdata(to_pci_dev(d));
+ return dev_get_drvdata(d);
}
#define snappendf(p,r,f,a...) do { \
@@ -132,9 +132,10 @@ nouveau_sysfs_fini(struct drm_device *dev)
{
struct nouveau_sysfs *sysfs = nouveau_sysfs(dev);
struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_device *device = nv_device(drm->device);
if (sysfs->ctrl) {
- device_remove_file(&dev->pdev->dev, &dev_attr_pstate);
+ device_remove_file(nv_device_base(device), &dev_attr_pstate);
nouveau_object_del(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL);
}
@@ -146,6 +147,7 @@ int
nouveau_sysfs_init(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_device *device = nv_device(drm->device);
struct nouveau_sysfs *sysfs;
int ret;
@@ -156,7 +158,7 @@ nouveau_sysfs_init(struct drm_device *dev)
ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL,
NV_CONTROL_CLASS, NULL, 0, &sysfs->ctrl);
if (ret == 0)
- device_create_file(&dev->pdev->dev, &dev_attr_pstate);
+ device_create_file(nv_device_base(device), &dev_attr_pstate);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index d45d50da978f..ab0228f640a5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -354,21 +354,26 @@ int
nouveau_ttm_init(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
+ struct nouveau_device *device = nv_device(drm->device);
u32 bits;
int ret;
bits = nouveau_vmmgr(drm->device)->dma_bits;
- if ( drm->agp.stat == ENABLED ||
- !pci_dma_supported(dev->pdev, DMA_BIT_MASK(bits)))
- bits = 32;
-
- ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(bits));
- if (ret)
- return ret;
-
- ret = pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(bits));
- if (ret)
- pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(32));
+ if (nv_device_is_pci(device)) {
+ if (drm->agp.stat == ENABLED ||
+ !pci_dma_supported(dev->pdev, DMA_BIT_MASK(bits)))
+ bits = 32;
+
+ ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(bits));
+ if (ret)
+ return ret;
+
+ ret = pci_set_consistent_dma_mask(dev->pdev,
+ DMA_BIT_MASK(bits));
+ if (ret)
+ pci_set_consistent_dma_mask(dev->pdev,
+ DMA_BIT_MASK(32));
+ }
ret = nouveau_ttm_global_init(drm);
if (ret)
@@ -376,7 +381,9 @@ nouveau_ttm_init(struct nouveau_drm *drm)
ret = ttm_bo_device_init(&drm->ttm.bdev,
drm->ttm.bo_global_ref.ref.object,
- &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET,
+ &nouveau_bo_driver,
+ dev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
bits <= 32 ? true : false);
if (ret) {
NV_ERROR(drm, "error initialising bo driver, %d\n", ret);
@@ -394,8 +401,8 @@ nouveau_ttm_init(struct nouveau_drm *drm)
return ret;
}
- drm->ttm.mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 1),
- pci_resource_len(dev->pdev, 1));
+ drm->ttm.mtrr = arch_phys_wc_add(nv_device_resource_start(device, 1),
+ nv_device_resource_len(device, 1));
/* GART init */
if (drm->agp.stat != ENABLED) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 471347edc27e..fb84da3cb50d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -84,6 +84,11 @@ nouveau_vga_init(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
bool runtime = false;
+
+ /* only relevant for PCI devices */
+ if (!dev->pdev)
+ return;
+
vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
if (nouveau_runtime_pm == 1)
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 2dccafc6e9db..58af547b0b93 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -651,7 +651,7 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
nv_connector = nouveau_crtc_connector_get(nv_crtc);
connector = &nv_connector->base;
if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) {
- if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3)
+ if (nv_crtc->base.primary->fb->depth > connector->display_info.bpc * 3)
mode = DITHERING_MODE_DYNAMIC2X2;
} else {
mode = nv_connector->dithering_mode;
@@ -785,7 +785,8 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
if (update) {
nv50_display_flip_stop(crtc);
- nv50_display_flip_next(crtc, crtc->fb, NULL, 1);
+ nv50_display_flip_next(crtc, crtc->primary->fb,
+ NULL, 1);
}
}
@@ -1028,7 +1029,7 @@ nv50_crtc_commit(struct drm_crtc *crtc)
}
nv50_crtc_cursor_show_hide(nv_crtc, nv_crtc->cursor.visible, true);
- nv50_display_flip_next(crtc, crtc->fb, NULL, 1);
+ nv50_display_flip_next(crtc, crtc->primary->fb, NULL, 1);
}
static bool
@@ -1042,7 +1043,7 @@ nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
static int
nv50_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
{
- struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
+ struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->primary->fb);
struct nv50_head *head = nv50_head(crtc);
int ret;
@@ -1139,7 +1140,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
nv50_crtc_set_dither(nv_crtc, false);
nv50_crtc_set_scale(nv_crtc, false);
nv50_crtc_set_color_vibrance(nv_crtc, false);
- nv50_crtc_set_image(nv_crtc, crtc->fb, x, y, false);
+ nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, false);
return 0;
}
@@ -1151,7 +1152,7 @@ nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ret;
- if (!crtc->fb) {
+ if (!crtc->primary->fb) {
NV_DEBUG(drm, "No FB bound\n");
return 0;
}
@@ -1161,8 +1162,8 @@ nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
return ret;
nv50_display_flip_stop(crtc);
- nv50_crtc_set_image(nv_crtc, crtc->fb, x, y, true);
- nv50_display_flip_next(crtc, crtc->fb, NULL, 1);
+ nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, true);
+ nv50_display_flip_next(crtc, crtc->primary->fb, NULL, 1);
return 0;
}
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 4313bb0a49a6..355157e4f78d 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -245,7 +245,7 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc,
copy_timings_drm_to_omap(&omap_crtc->timings, mode);
omap_crtc->full_update = true;
- return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
+ return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x << 16, y << 16,
mode->hdisplay << 16, mode->vdisplay << 16,
@@ -273,7 +273,7 @@ static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_plane *plane = omap_crtc->plane;
struct drm_display_mode *mode = &crtc->mode;
- return omap_plane_mode_set(plane, crtc, crtc->fb,
+ return omap_plane_mode_set(plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x << 16, y << 16,
mode->hdisplay << 16, mode->vdisplay << 16,
@@ -308,14 +308,14 @@ static void page_flip_worker(struct work_struct *work)
struct drm_gem_object *bo;
mutex_lock(&crtc->mutex);
- omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
+ omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
crtc->x << 16, crtc->y << 16,
mode->hdisplay << 16, mode->vdisplay << 16,
vblank_cb, crtc);
mutex_unlock(&crtc->mutex);
- bo = omap_framebuffer_bo(crtc->fb, 0);
+ bo = omap_framebuffer_bo(crtc->primary->fb, 0);
drm_gem_object_unreference_unlocked(bo);
}
@@ -336,9 +336,10 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_plane *primary = crtc->primary;
struct drm_gem_object *bo;
- DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1,
+ DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1,
fb->base.id, event);
if (omap_crtc->old_fb) {
@@ -347,7 +348,7 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
}
omap_crtc->event = event;
- crtc->fb = fb;
+ primary->fb = fb;
/*
* Hold a reference temporarily until the crtc is updated
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index f466c4aaee94..d2b8c49bfb4a 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -306,13 +306,14 @@ struct drm_connector *omap_framebuffer_get_next_connector(
struct drm_connector *connector = from;
if (!from)
- return list_first_entry(connector_list, typeof(*from), head);
+ return list_first_entry_or_null(connector_list, typeof(*from),
+ head);
list_for_each_entry_from(connector, connector_list, head) {
if (connector != from) {
struct drm_encoder *encoder = connector->encoder;
struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
- if (crtc && crtc->fb == fb)
+ if (crtc && crtc->primary->fb == fb)
return connector;
}
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index 5aec3e81fe24..c8d972763889 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -153,24 +153,24 @@ static struct {
static void evict_entry(struct drm_gem_object *obj,
enum tiler_fmt fmt, struct usergart_entry *entry)
{
- if (obj->dev->dev_mapping) {
- struct omap_gem_object *omap_obj = to_omap_bo(obj);
- int n = usergart[fmt].height;
- size_t size = PAGE_SIZE * n;
- loff_t off = mmap_offset(obj) +
- (entry->obj_pgoff << PAGE_SHIFT);
- const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
- if (m > 1) {
- int i;
- /* if stride > than PAGE_SIZE then sparse mapping: */
- for (i = n; i > 0; i--) {
- unmap_mapping_range(obj->dev->dev_mapping,
- off, PAGE_SIZE, 1);
- off += PAGE_SIZE * m;
- }
- } else {
- unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
+ struct omap_gem_object *omap_obj = to_omap_bo(obj);
+ int n = usergart[fmt].height;
+ size_t size = PAGE_SIZE * n;
+ loff_t off = mmap_offset(obj) +
+ (entry->obj_pgoff << PAGE_SHIFT);
+ const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
+
+ if (m > 1) {
+ int i;
+ /* if stride > than PAGE_SIZE then sparse mapping: */
+ for (i = n; i > 0; i--) {
+ unmap_mapping_range(obj->dev->anon_inode->i_mapping,
+ off, PAGE_SIZE, 1);
+ off += PAGE_SIZE * m;
}
+ } else {
+ unmap_mapping_range(obj->dev->anon_inode->i_mapping,
+ off, size, 1);
}
entry->obj = NULL;
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 3e0f13d1bc84..4ec874da5668 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -16,4 +16,18 @@ config DRM_PANEL_SIMPLE
that it can be automatically turned off when the panel goes into a
low power state.
+config DRM_PANEL_LD9040
+ tristate "LD9040 RGB/SPI panel"
+ depends on DRM && DRM_PANEL
+ depends on OF
+ select SPI
+ select VIDEOMODE_HELPERS
+
+config DRM_PANEL_S6E8AA0
+ tristate "S6E8AA0 DSI video mode panel"
+ depends on DRM && DRM_PANEL
+ depends on OF
+ select DRM_MIPI_DSI
+ select VIDEOMODE_HELPERS
+
endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index af9dfa235b94..8b929212fad7 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1 +1,3 @@
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
+obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o
+obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o
diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c
new file mode 100644
index 000000000000..1f1f8371a199
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ld9040.c
@@ -0,0 +1,376 @@
+/*
+ * ld9040 AMOLED LCD drm_panel driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ * Derived from drivers/video/backlight/ld9040.c
+ *
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <video/mipi_display.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+/* Manufacturer Command Set */
+#define MCS_MANPWR 0xb0
+#define MCS_ELVSS_ON 0xb1
+#define MCS_USER_SETTING 0xf0
+#define MCS_DISPCTL 0xf2
+#define MCS_GTCON 0xf7
+#define MCS_PANEL_CONDITION 0xf8
+#define MCS_GAMMA_SET1 0xf9
+#define MCS_GAMMA_CTRL 0xfb
+
+/* array of gamma tables for gamma value 2.2 */
+static u8 const ld9040_gammas[25][22] = {
+ { 0xf9, 0x00, 0x13, 0xb2, 0xba, 0xd2, 0x00, 0x30, 0x00, 0xaf, 0xc0,
+ 0xb8, 0xcd, 0x00, 0x3d, 0x00, 0xa8, 0xb8, 0xb7, 0xcd, 0x00, 0x44 },
+ { 0xf9, 0x00, 0x13, 0xb9, 0xb9, 0xd0, 0x00, 0x3c, 0x00, 0xaf, 0xbf,
+ 0xb6, 0xcb, 0x00, 0x4b, 0x00, 0xa8, 0xb9, 0xb5, 0xcc, 0x00, 0x52 },
+ { 0xf9, 0x00, 0x13, 0xba, 0xb9, 0xcd, 0x00, 0x41, 0x00, 0xb0, 0xbe,
+ 0xb5, 0xc9, 0x00, 0x51, 0x00, 0xa9, 0xb9, 0xb5, 0xca, 0x00, 0x57 },
+ { 0xf9, 0x00, 0x13, 0xb9, 0xb8, 0xcd, 0x00, 0x46, 0x00, 0xb1, 0xbc,
+ 0xb5, 0xc8, 0x00, 0x56, 0x00, 0xaa, 0xb8, 0xb4, 0xc9, 0x00, 0x5d },
+ { 0xf9, 0x00, 0x13, 0xba, 0xb8, 0xcb, 0x00, 0x4b, 0x00, 0xb3, 0xbc,
+ 0xb4, 0xc7, 0x00, 0x5c, 0x00, 0xac, 0xb8, 0xb4, 0xc8, 0x00, 0x62 },
+ { 0xf9, 0x00, 0x13, 0xbb, 0xb7, 0xca, 0x00, 0x4f, 0x00, 0xb4, 0xbb,
+ 0xb3, 0xc7, 0x00, 0x60, 0x00, 0xad, 0xb8, 0xb4, 0xc7, 0x00, 0x67 },
+ { 0xf9, 0x00, 0x47, 0xba, 0xb6, 0xca, 0x00, 0x53, 0x00, 0xb5, 0xbb,
+ 0xb3, 0xc6, 0x00, 0x65, 0x00, 0xae, 0xb8, 0xb3, 0xc7, 0x00, 0x6c },
+ { 0xf9, 0x00, 0x71, 0xbb, 0xb5, 0xc8, 0x00, 0x57, 0x00, 0xb5, 0xbb,
+ 0xb0, 0xc5, 0x00, 0x6a, 0x00, 0xae, 0xb9, 0xb1, 0xc6, 0x00, 0x70 },
+ { 0xf9, 0x00, 0x7b, 0xbb, 0xb4, 0xc8, 0x00, 0x5b, 0x00, 0xb5, 0xba,
+ 0xb1, 0xc4, 0x00, 0x6e, 0x00, 0xae, 0xb9, 0xb0, 0xc5, 0x00, 0x75 },
+ { 0xf9, 0x00, 0x82, 0xba, 0xb4, 0xc7, 0x00, 0x5f, 0x00, 0xb5, 0xba,
+ 0xb0, 0xc3, 0x00, 0x72, 0x00, 0xae, 0xb8, 0xb0, 0xc3, 0x00, 0x7a },
+ { 0xf9, 0x00, 0x89, 0xba, 0xb3, 0xc8, 0x00, 0x62, 0x00, 0xb6, 0xba,
+ 0xaf, 0xc3, 0x00, 0x76, 0x00, 0xaf, 0xb7, 0xae, 0xc4, 0x00, 0x7e },
+ { 0xf9, 0x00, 0x8b, 0xb9, 0xb3, 0xc7, 0x00, 0x65, 0x00, 0xb7, 0xb8,
+ 0xaf, 0xc3, 0x00, 0x7a, 0x00, 0x80, 0xb6, 0xae, 0xc4, 0x00, 0x81 },
+ { 0xf9, 0x00, 0x93, 0xba, 0xb3, 0xc5, 0x00, 0x69, 0x00, 0xb8, 0xb9,
+ 0xae, 0xc1, 0x00, 0x7f, 0x00, 0xb0, 0xb6, 0xae, 0xc3, 0x00, 0x85 },
+ { 0xf9, 0x00, 0x97, 0xba, 0xb2, 0xc5, 0x00, 0x6c, 0x00, 0xb8, 0xb8,
+ 0xae, 0xc1, 0x00, 0x82, 0x00, 0xb0, 0xb6, 0xae, 0xc2, 0x00, 0x89 },
+ { 0xf9, 0x00, 0x9a, 0xba, 0xb1, 0xc4, 0x00, 0x6f, 0x00, 0xb8, 0xb8,
+ 0xad, 0xc0, 0x00, 0x86, 0x00, 0xb0, 0xb7, 0xad, 0xc0, 0x00, 0x8d },
+ { 0xf9, 0x00, 0x9c, 0xb9, 0xb0, 0xc4, 0x00, 0x72, 0x00, 0xb8, 0xb8,
+ 0xac, 0xbf, 0x00, 0x8a, 0x00, 0xb0, 0xb6, 0xac, 0xc0, 0x00, 0x91 },
+ { 0xf9, 0x00, 0x9e, 0xba, 0xb0, 0xc2, 0x00, 0x75, 0x00, 0xb9, 0xb8,
+ 0xab, 0xbe, 0x00, 0x8e, 0x00, 0xb0, 0xb6, 0xac, 0xbf, 0x00, 0x94 },
+ { 0xf9, 0x00, 0xa0, 0xb9, 0xaf, 0xc3, 0x00, 0x77, 0x00, 0xb9, 0xb7,
+ 0xab, 0xbe, 0x00, 0x90, 0x00, 0xb0, 0xb6, 0xab, 0xbf, 0x00, 0x97 },
+ { 0xf9, 0x00, 0xa2, 0xb9, 0xaf, 0xc2, 0x00, 0x7a, 0x00, 0xb9, 0xb7,
+ 0xaa, 0xbd, 0x00, 0x94, 0x00, 0xb0, 0xb5, 0xab, 0xbf, 0x00, 0x9a },
+ { 0xf9, 0x00, 0xa4, 0xb9, 0xaf, 0xc1, 0x00, 0x7d, 0x00, 0xb9, 0xb6,
+ 0xaa, 0xbb, 0x00, 0x97, 0x00, 0xb1, 0xb5, 0xaa, 0xbf, 0x00, 0x9d },
+ { 0xf9, 0x00, 0xa4, 0xb8, 0xb0, 0xbf, 0x00, 0x80, 0x00, 0xb8, 0xb6,
+ 0xaa, 0xbc, 0x00, 0x9a, 0x00, 0xb0, 0xb5, 0xab, 0xbd, 0x00, 0xa0 },
+ { 0xf9, 0x00, 0xa8, 0xb8, 0xae, 0xbe, 0x00, 0x84, 0x00, 0xb9, 0xb7,
+ 0xa8, 0xbc, 0x00, 0x9d, 0x00, 0xb2, 0xb5, 0xaa, 0xbc, 0x00, 0xa4 },
+ { 0xf9, 0x00, 0xa9, 0xb6, 0xad, 0xbf, 0x00, 0x86, 0x00, 0xb8, 0xb5,
+ 0xa8, 0xbc, 0x00, 0xa0, 0x00, 0xb3, 0xb3, 0xa9, 0xbc, 0x00, 0xa7 },
+ { 0xf9, 0x00, 0xa9, 0xb7, 0xae, 0xbd, 0x00, 0x89, 0x00, 0xb7, 0xb6,
+ 0xa8, 0xba, 0x00, 0xa4, 0x00, 0xb1, 0xb4, 0xaa, 0xbb, 0x00, 0xaa },
+ { 0xf9, 0x00, 0xa7, 0xb4, 0xae, 0xbf, 0x00, 0x91, 0x00, 0xb2, 0xb4,
+ 0xaa, 0xbb, 0x00, 0xac, 0x00, 0xb3, 0xb1, 0xaa, 0xbc, 0x00, 0xb3 },
+};
+
+struct ld9040 {
+ struct device *dev;
+ struct drm_panel panel;
+
+ struct regulator_bulk_data supplies[2];
+ struct gpio_desc *reset_gpio;
+ u32 power_on_delay;
+ u32 reset_delay;
+ struct videomode vm;
+ u32 width_mm;
+ u32 height_mm;
+
+ int brightness;
+
+ /* This field is tested by functions directly accessing bus before
+ * transfer, transfer is skipped if it is set. In case of transfer
+ * failure or unexpected response the field is set to error value.
+ * Such construct allows to eliminate many checks in higher level
+ * functions.
+ */
+ int error;
+};
+
+#define panel_to_ld9040(p) container_of(p, struct ld9040, panel)
+
+static int ld9040_clear_error(struct ld9040 *ctx)
+{
+ int ret = ctx->error;
+
+ ctx->error = 0;
+ return ret;
+}
+
+static int ld9040_spi_write_word(struct ld9040 *ctx, u16 data)
+{
+ struct spi_device *spi = to_spi_device(ctx->dev);
+ struct spi_transfer xfer = {
+ .len = 2,
+ .tx_buf = &data,
+ };
+ struct spi_message msg;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ return spi_sync(spi, &msg);
+}
+
+static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len)
+{
+ int ret = 0;
+
+ if (ctx->error < 0 || len == 0)
+ return;
+
+ dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", len, data);
+ ret = ld9040_spi_write_word(ctx, *data);
+
+ while (!ret && --len) {
+ ++data;
+ ret = ld9040_spi_write_word(ctx, *data | 0x100);
+ }
+
+ if (ret) {
+ dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len,
+ data);
+ ctx->error = ret;
+ }
+
+ usleep_range(300, 310);
+}
+
+#define ld9040_dcs_write_seq_static(ctx, seq...) \
+({\
+ static const u8 d[] = { seq };\
+ ld9040_dcs_write(ctx, d, ARRAY_SIZE(d));\
+})
+
+static void ld9040_brightness_set(struct ld9040 *ctx)
+{
+ ld9040_dcs_write(ctx, ld9040_gammas[ctx->brightness],
+ ARRAY_SIZE(ld9040_gammas[ctx->brightness]));
+
+ ld9040_dcs_write_seq_static(ctx, MCS_GAMMA_CTRL, 0x02, 0x5a);
+}
+
+static void ld9040_init(struct ld9040 *ctx)
+{
+ ld9040_dcs_write_seq_static(ctx, MCS_USER_SETTING, 0x5a, 0x5a);
+ ld9040_dcs_write_seq_static(ctx, MCS_PANEL_CONDITION,
+ 0x05, 0x65, 0x96, 0x71, 0x7d, 0x19, 0x3b, 0x0d,
+ 0x19, 0x7e, 0x0d, 0xe2, 0x00, 0x00, 0x7e, 0x7d,
+ 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02);
+ ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL,
+ 0x02, 0x08, 0x08, 0x10, 0x10);
+ ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04);
+ ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16);
+ ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00);
+ ld9040_brightness_set(ctx);
+ ld9040_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
+ ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
+}
+
+static int ld9040_power_on(struct ld9040 *ctx)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ msleep(ctx->power_on_delay);
+ gpiod_set_value(ctx->reset_gpio, 0);
+ msleep(ctx->reset_delay);
+ gpiod_set_value(ctx->reset_gpio, 1);
+ msleep(ctx->reset_delay);
+
+ return 0;
+}
+
+static int ld9040_power_off(struct ld9040 *ctx)
+{
+ return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int ld9040_disable(struct drm_panel *panel)
+{
+ struct ld9040 *ctx = panel_to_ld9040(panel);
+
+ msleep(120);
+ ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
+ ld9040_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
+ msleep(40);
+
+ ld9040_clear_error(ctx);
+
+ return ld9040_power_off(ctx);
+}
+
+static int ld9040_enable(struct drm_panel *panel)
+{
+ struct ld9040 *ctx = panel_to_ld9040(panel);
+ int ret;
+
+ ret = ld9040_power_on(ctx);
+ if (ret < 0)
+ return ret;
+
+ ld9040_init(ctx);
+
+ ret = ld9040_clear_error(ctx);
+
+ if (ret < 0)
+ ld9040_disable(panel);
+
+ return ret;
+}
+
+static int ld9040_get_modes(struct drm_panel *panel)
+{
+ struct drm_connector *connector = panel->connector;
+ struct ld9040 *ctx = panel_to_ld9040(panel);
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ if (!mode) {
+ DRM_ERROR("failed to create a new display mode\n");
+ return 0;
+ }
+
+ drm_display_mode_from_videomode(&ctx->vm, mode);
+ mode->width_mm = ctx->width_mm;
+ mode->height_mm = ctx->height_mm;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs ld9040_drm_funcs = {
+ .disable = ld9040_disable,
+ .enable = ld9040_enable,
+ .get_modes = ld9040_get_modes,
+};
+
+static int ld9040_parse_dt(struct ld9040 *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ ret = of_get_videomode(np, &ctx->vm, 0);
+ if (ret < 0)
+ return ret;
+
+ of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay);
+ of_property_read_u32(np, "reset-delay", &ctx->reset_delay);
+ of_property_read_u32(np, "panel-width-mm", &ctx->width_mm);
+ of_property_read_u32(np, "panel-height-mm", &ctx->height_mm);
+
+ return 0;
+}
+
+static int ld9040_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ld9040 *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(struct ld9040), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, ctx);
+
+ ctx->dev = dev;
+ ctx->brightness = ARRAY_SIZE(ld9040_gammas) - 1;
+
+ ret = ld9040_parse_dt(ctx);
+ if (ret < 0)
+ return ret;
+
+ ctx->supplies[0].supply = "vdd3";
+ ctx->supplies[1].supply = "vci";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset");
+ if (IS_ERR(ctx->reset_gpio)) {
+ dev_err(dev, "cannot get reset-gpios %ld\n",
+ PTR_ERR(ctx->reset_gpio));
+ return PTR_ERR(ctx->reset_gpio);
+ }
+ ret = gpiod_direction_output(ctx->reset_gpio, 1);
+ if (ret < 0) {
+ dev_err(dev, "cannot configure reset-gpios %d\n", ret);
+ return ret;
+ }
+
+ spi->bits_per_word = 9;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(dev, "spi setup failed.\n");
+ return ret;
+ }
+
+ drm_panel_init(&ctx->panel);
+ ctx->panel.dev = dev;
+ ctx->panel.funcs = &ld9040_drm_funcs;
+
+ return drm_panel_add(&ctx->panel);
+}
+
+static int ld9040_remove(struct spi_device *spi)
+{
+ struct ld9040 *ctx = spi_get_drvdata(spi);
+
+ ld9040_power_off(ctx);
+ drm_panel_remove(&ctx->panel);
+
+ return 0;
+}
+
+static struct of_device_id ld9040_of_match[] = {
+ { .compatible = "samsung,ld9040" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ld9040_of_match);
+
+static struct spi_driver ld9040_driver = {
+ .probe = ld9040_probe,
+ .remove = ld9040_remove,
+ .driver = {
+ .name = "ld9040",
+ .owner = THIS_MODULE,
+ .of_match_table = ld9040_of_match,
+ },
+};
+module_spi_driver(ld9040_driver);
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("ld9040 LCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
new file mode 100644
index 000000000000..35941d2412b8
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
@@ -0,0 +1,1069 @@
+/*
+ * MIPI-DSI based s6e8aa0 AMOLED LCD 5.3 inch panel driver.
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ * Joongmock Shin <jmock.shin@samsung.com>
+ * Eunchul Kim <chulspro.kim@samsung.com>
+ * Tomasz Figa <t.figa@samsung.com>
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#define LDI_MTP_LENGTH 24
+#define GAMMA_LEVEL_NUM 25
+#define GAMMA_TABLE_LEN 26
+
+#define PANELCTL_SS_MASK (1 << 5)
+#define PANELCTL_SS_1_800 (0 << 5)
+#define PANELCTL_SS_800_1 (1 << 5)
+#define PANELCTL_GTCON_MASK (7 << 2)
+#define PANELCTL_GTCON_110 (6 << 2)
+#define PANELCTL_GTCON_111 (7 << 2)
+
+#define PANELCTL_CLK1_CON_MASK (7 << 3)
+#define PANELCTL_CLK1_000 (0 << 3)
+#define PANELCTL_CLK1_001 (1 << 3)
+#define PANELCTL_CLK2_CON_MASK (7 << 0)
+#define PANELCTL_CLK2_000 (0 << 0)
+#define PANELCTL_CLK2_001 (1 << 0)
+
+#define PANELCTL_INT1_CON_MASK (7 << 3)
+#define PANELCTL_INT1_000 (0 << 3)
+#define PANELCTL_INT1_001 (1 << 3)
+#define PANELCTL_INT2_CON_MASK (7 << 0)
+#define PANELCTL_INT2_000 (0 << 0)
+#define PANELCTL_INT2_001 (1 << 0)
+
+#define PANELCTL_BICTL_CON_MASK (7 << 3)
+#define PANELCTL_BICTL_000 (0 << 3)
+#define PANELCTL_BICTL_001 (1 << 3)
+#define PANELCTL_BICTLB_CON_MASK (7 << 0)
+#define PANELCTL_BICTLB_000 (0 << 0)
+#define PANELCTL_BICTLB_001 (1 << 0)
+
+#define PANELCTL_EM_CLK1_CON_MASK (7 << 3)
+#define PANELCTL_EM_CLK1_110 (6 << 3)
+#define PANELCTL_EM_CLK1_111 (7 << 3)
+#define PANELCTL_EM_CLK1B_CON_MASK (7 << 0)
+#define PANELCTL_EM_CLK1B_110 (6 << 0)
+#define PANELCTL_EM_CLK1B_111 (7 << 0)
+
+#define PANELCTL_EM_CLK2_CON_MASK (7 << 3)
+#define PANELCTL_EM_CLK2_110 (6 << 3)
+#define PANELCTL_EM_CLK2_111 (7 << 3)
+#define PANELCTL_EM_CLK2B_CON_MASK (7 << 0)
+#define PANELCTL_EM_CLK2B_110 (6 << 0)
+#define PANELCTL_EM_CLK2B_111 (7 << 0)
+
+#define PANELCTL_EM_INT1_CON_MASK (7 << 3)
+#define PANELCTL_EM_INT1_000 (0 << 3)
+#define PANELCTL_EM_INT1_001 (1 << 3)
+#define PANELCTL_EM_INT2_CON_MASK (7 << 0)
+#define PANELCTL_EM_INT2_000 (0 << 0)
+#define PANELCTL_EM_INT2_001 (1 << 0)
+
+#define AID_DISABLE (0x4)
+#define AID_1 (0x5)
+#define AID_2 (0x6)
+#define AID_3 (0x7)
+
+typedef u8 s6e8aa0_gamma_table[GAMMA_TABLE_LEN];
+
+struct s6e8aa0_variant {
+ u8 version;
+ const s6e8aa0_gamma_table *gamma_tables;
+};
+
+struct s6e8aa0 {
+ struct device *dev;
+ struct drm_panel panel;
+
+ struct regulator_bulk_data supplies[2];
+ struct gpio_desc *reset_gpio;
+ u32 power_on_delay;
+ u32 reset_delay;
+ u32 init_delay;
+ bool flip_horizontal;
+ bool flip_vertical;
+ struct videomode vm;
+ u32 width_mm;
+ u32 height_mm;
+
+ u8 version;
+ u8 id;
+ const struct s6e8aa0_variant *variant;
+ int brightness;
+
+ /* This field is tested by functions directly accessing DSI bus before
+ * transfer, transfer is skipped if it is set. In case of transfer
+ * failure or unexpected response the field is set to error value.
+ * Such construct allows to eliminate many checks in higher level
+ * functions.
+ */
+ int error;
+};
+
+#define panel_to_s6e8aa0(p) container_of(p, struct s6e8aa0, panel)
+
+static int s6e8aa0_clear_error(struct s6e8aa0 *ctx)
+{
+ int ret = ctx->error;
+
+ ctx->error = 0;
+ return ret;
+}
+
+static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ int ret;
+
+ if (ctx->error < 0)
+ return;
+
+ ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len);
+ if (ret < 0) {
+ dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len,
+ data);
+ ctx->error = ret;
+ }
+}
+
+static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ int ret;
+
+ if (ctx->error < 0)
+ return ctx->error;
+
+ ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len);
+ if (ret < 0) {
+ dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd);
+ ctx->error = ret;
+ }
+
+ return ret;
+}
+
+#define s6e8aa0_dcs_write_seq(ctx, seq...) \
+({\
+ const u8 d[] = { seq };\
+ BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too big for stack");\
+ s6e8aa0_dcs_write(ctx, d, ARRAY_SIZE(d));\
+})
+
+#define s6e8aa0_dcs_write_seq_static(ctx, seq...) \
+({\
+ static const u8 d[] = { seq };\
+ s6e8aa0_dcs_write(ctx, d, ARRAY_SIZE(d));\
+})
+
+static void s6e8aa0_apply_level_1_key(struct s6e8aa0 *ctx)
+{
+ s6e8aa0_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
+}
+
+static void s6e8aa0_panel_cond_set_v142(struct s6e8aa0 *ctx)
+{
+ static const u8 aids[] = {
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x60, 0x80, 0xA0
+ };
+ u8 aid = aids[ctx->id >> 5];
+ u8 cfg = 0x3d;
+ u8 clk_con = 0xc8;
+ u8 int_con = 0x08;
+ u8 bictl_con = 0x48;
+ u8 em_clk1_con = 0xff;
+ u8 em_clk2_con = 0xff;
+ u8 em_int_con = 0xc8;
+
+ if (ctx->flip_vertical) {
+ /* GTCON */
+ cfg &= ~(PANELCTL_GTCON_MASK);
+ cfg |= (PANELCTL_GTCON_110);
+ }
+
+ if (ctx->flip_horizontal) {
+ /* SS */
+ cfg &= ~(PANELCTL_SS_MASK);
+ cfg |= (PANELCTL_SS_1_800);
+ }
+
+ if (ctx->flip_horizontal || ctx->flip_vertical) {
+ /* CLK1,2_CON */
+ clk_con &= ~(PANELCTL_CLK1_CON_MASK |
+ PANELCTL_CLK2_CON_MASK);
+ clk_con |= (PANELCTL_CLK1_000 | PANELCTL_CLK2_001);
+
+ /* INT1,2_CON */
+ int_con &= ~(PANELCTL_INT1_CON_MASK |
+ PANELCTL_INT2_CON_MASK);
+ int_con |= (PANELCTL_INT1_000 | PANELCTL_INT2_001);
+
+ /* BICTL,B_CON */
+ bictl_con &= ~(PANELCTL_BICTL_CON_MASK |
+ PANELCTL_BICTLB_CON_MASK);
+ bictl_con |= (PANELCTL_BICTL_000 |
+ PANELCTL_BICTLB_001);
+
+ /* EM_CLK1,1B_CON */
+ em_clk1_con &= ~(PANELCTL_EM_CLK1_CON_MASK |
+ PANELCTL_EM_CLK1B_CON_MASK);
+ em_clk1_con |= (PANELCTL_EM_CLK1_110 |
+ PANELCTL_EM_CLK1B_110);
+
+ /* EM_CLK2,2B_CON */
+ em_clk2_con &= ~(PANELCTL_EM_CLK2_CON_MASK |
+ PANELCTL_EM_CLK2B_CON_MASK);
+ em_clk2_con |= (PANELCTL_EM_CLK2_110 |
+ PANELCTL_EM_CLK2B_110);
+
+ /* EM_INT1,2_CON */
+ em_int_con &= ~(PANELCTL_EM_INT1_CON_MASK |
+ PANELCTL_EM_INT2_CON_MASK);
+ em_int_con |= (PANELCTL_EM_INT1_000 |
+ PANELCTL_EM_INT2_001);
+ }
+
+ s6e8aa0_dcs_write_seq(ctx,
+ 0xf8, cfg, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00,
+ 0x3c, 0x78, 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00,
+ 0x00, 0x20, aid, 0x08, 0x6e, 0x00, 0x00, 0x00,
+ 0x02, 0x07, 0x07, 0x23, 0x23, 0xc0, clk_con, int_con,
+ bictl_con, 0xc1, 0x00, 0xc1, em_clk1_con, em_clk2_con,
+ em_int_con);
+}
+
+static void s6e8aa0_panel_cond_set(struct s6e8aa0 *ctx)
+{
+ if (ctx->version < 142)
+ s6e8aa0_dcs_write_seq_static(ctx,
+ 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x94, 0x00,
+ 0x3c, 0x78, 0x10, 0x27, 0x08, 0x6e, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x07, 0x23, 0x6e, 0xc0, 0xc1, 0x01,
+ 0x81, 0xc1, 0x00, 0xc3, 0xf6, 0xf6, 0xc1
+ );
+ else
+ s6e8aa0_panel_cond_set_v142(ctx);
+}
+
+static void s6e8aa0_display_condition_set(struct s6e8aa0 *ctx)
+{
+ s6e8aa0_dcs_write_seq_static(ctx, 0xf2, 0x80, 0x03, 0x0d);
+}
+
+static void s6e8aa0_etc_source_control(struct s6e8aa0 *ctx)
+{
+ s6e8aa0_dcs_write_seq_static(ctx, 0xf6, 0x00, 0x02, 0x00);
+}
+
+static void s6e8aa0_etc_pentile_control(struct s6e8aa0 *ctx)
+{
+ static const u8 pent32[] = {
+ 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xc0, 0x44, 0x44, 0xc0, 0x00
+ };
+
+ static const u8 pent142[] = {
+ 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, 0x00
+ };
+
+ if (ctx->version < 142)
+ s6e8aa0_dcs_write(ctx, pent32, ARRAY_SIZE(pent32));
+ else
+ s6e8aa0_dcs_write(ctx, pent142, ARRAY_SIZE(pent142));
+}
+
+static void s6e8aa0_etc_power_control(struct s6e8aa0 *ctx)
+{
+ static const u8 pwr142[] = {
+ 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x1e, 0x33, 0x02
+ };
+
+ static const u8 pwr32[] = {
+ 0xf4, 0xcf, 0x0a, 0x15, 0x10, 0x19, 0x33, 0x02
+ };
+
+ if (ctx->version < 142)
+ s6e8aa0_dcs_write(ctx, pwr32, ARRAY_SIZE(pwr32));
+ else
+ s6e8aa0_dcs_write(ctx, pwr142, ARRAY_SIZE(pwr142));
+}
+
+static void s6e8aa0_etc_elvss_control(struct s6e8aa0 *ctx)
+{
+ u8 id = ctx->id ? 0 : 0x95;
+
+ s6e8aa0_dcs_write_seq(ctx, 0xb1, 0x04, id);
+}
+
+static void s6e8aa0_elvss_nvm_set_v142(struct s6e8aa0 *ctx)
+{
+ u8 br;
+
+ switch (ctx->brightness) {
+ case 0 ... 6: /* 30cd ~ 100cd */
+ br = 0xdf;
+ break;
+ case 7 ... 11: /* 120cd ~ 150cd */
+ br = 0xdd;
+ break;
+ case 12 ... 15: /* 180cd ~ 210cd */
+ default:
+ br = 0xd9;
+ break;
+ case 16 ... 24: /* 240cd ~ 300cd */
+ br = 0xd0;
+ break;
+ }
+
+ s6e8aa0_dcs_write_seq(ctx, 0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e,
+ 0xc4, 0x0f, 0x40, 0x41, br, 0x00, 0x60, 0x19);
+}
+
+static void s6e8aa0_elvss_nvm_set(struct s6e8aa0 *ctx)
+{
+ if (ctx->version < 142)
+ s6e8aa0_dcs_write_seq_static(ctx,
+ 0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, 0xc4, 0x07,
+ 0x40, 0x41, 0xc1, 0x00, 0x60, 0x19);
+ else
+ s6e8aa0_elvss_nvm_set_v142(ctx);
+};
+
+static void s6e8aa0_apply_level_2_key(struct s6e8aa0 *ctx)
+{
+ s6e8aa0_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
+}
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v142[GAMMA_LEVEL_NUM] = {
+ {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x62, 0x55, 0x55,
+ 0xaf, 0xb1, 0xb1, 0xbd, 0xce, 0xb7, 0x9a, 0xb1,
+ 0x90, 0xb2, 0xc4, 0xae, 0x00, 0x60, 0x00, 0x40,
+ 0x00, 0x70,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x74, 0x68, 0x69,
+ 0xb8, 0xc1, 0xb7, 0xbd, 0xcd, 0xb8, 0x93, 0xab,
+ 0x88, 0xb4, 0xc4, 0xb1, 0x00, 0x6b, 0x00, 0x4d,
+ 0x00, 0x7d,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x95, 0x8a, 0x89,
+ 0xb4, 0xc6, 0xb2, 0xc5, 0xd2, 0xbf, 0x90, 0xa8,
+ 0x85, 0xb5, 0xc4, 0xb3, 0x00, 0x7b, 0x00, 0x5d,
+ 0x00, 0x8f,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9f, 0x98, 0x92,
+ 0xb3, 0xc4, 0xb0, 0xbc, 0xcc, 0xb4, 0x91, 0xa6,
+ 0x87, 0xb5, 0xc5, 0xb4, 0x00, 0x87, 0x00, 0x6a,
+ 0x00, 0x9e,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x99, 0x93, 0x8b,
+ 0xb2, 0xc2, 0xb0, 0xbd, 0xce, 0xb4, 0x90, 0xa6,
+ 0x87, 0xb3, 0xc3, 0xb2, 0x00, 0x8d, 0x00, 0x70,
+ 0x00, 0xa4,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xa5, 0x99,
+ 0xb2, 0xc2, 0xb0, 0xbb, 0xcd, 0xb1, 0x93, 0xa7,
+ 0x8a, 0xb2, 0xc1, 0xb0, 0x00, 0x92, 0x00, 0x75,
+ 0x00, 0xaa,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xa0, 0x93,
+ 0xb6, 0xc4, 0xb4, 0xb5, 0xc8, 0xaa, 0x94, 0xa9,
+ 0x8c, 0xb2, 0xc0, 0xb0, 0x00, 0x97, 0x00, 0x7a,
+ 0x00, 0xaf,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xa7, 0x96,
+ 0xb3, 0xc2, 0xb0, 0xba, 0xcb, 0xb0, 0x94, 0xa8,
+ 0x8c, 0xb0, 0xbf, 0xaf, 0x00, 0x9f, 0x00, 0x83,
+ 0x00, 0xb9,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9d, 0xa2, 0x90,
+ 0xb6, 0xc5, 0xb3, 0xb8, 0xc9, 0xae, 0x94, 0xa8,
+ 0x8d, 0xaf, 0xbd, 0xad, 0x00, 0xa4, 0x00, 0x88,
+ 0x00, 0xbf,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xac, 0x97,
+ 0xb4, 0xc4, 0xb1, 0xbb, 0xcb, 0xb2, 0x93, 0xa7,
+ 0x8d, 0xae, 0xbc, 0xad, 0x00, 0xa7, 0x00, 0x8c,
+ 0x00, 0xc3,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa2, 0xa9, 0x93,
+ 0xb6, 0xc5, 0xb2, 0xba, 0xc9, 0xb0, 0x93, 0xa7,
+ 0x8d, 0xae, 0xbb, 0xac, 0x00, 0xab, 0x00, 0x90,
+ 0x00, 0xc8,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9e, 0xa6, 0x8f,
+ 0xb7, 0xc6, 0xb3, 0xb8, 0xc8, 0xb0, 0x93, 0xa6,
+ 0x8c, 0xae, 0xbb, 0xad, 0x00, 0xae, 0x00, 0x93,
+ 0x00, 0xcc,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb4, 0x9c,
+ 0xb3, 0xc3, 0xaf, 0xb7, 0xc7, 0xaf, 0x93, 0xa6,
+ 0x8c, 0xaf, 0xbc, 0xad, 0x00, 0xb1, 0x00, 0x97,
+ 0x00, 0xcf,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xb1, 0x98,
+ 0xb1, 0xc2, 0xab, 0xba, 0xc9, 0xb2, 0x93, 0xa6,
+ 0x8d, 0xae, 0xba, 0xab, 0x00, 0xb5, 0x00, 0x9b,
+ 0x00, 0xd4,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xae, 0x94,
+ 0xb2, 0xc3, 0xac, 0xbb, 0xca, 0xb4, 0x91, 0xa4,
+ 0x8a, 0xae, 0xba, 0xac, 0x00, 0xb8, 0x00, 0x9e,
+ 0x00, 0xd8,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb7, 0x9c,
+ 0xae, 0xc0, 0xa9, 0xba, 0xc9, 0xb3, 0x92, 0xa5,
+ 0x8b, 0xad, 0xb9, 0xab, 0x00, 0xbb, 0x00, 0xa1,
+ 0x00, 0xdc,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb4, 0x97,
+ 0xb0, 0xc1, 0xaa, 0xb9, 0xc8, 0xb2, 0x92, 0xa5,
+ 0x8c, 0xae, 0xb9, 0xab, 0x00, 0xbe, 0x00, 0xa4,
+ 0x00, 0xdf,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94,
+ 0xb0, 0xc2, 0xab, 0xbb, 0xc9, 0xb3, 0x91, 0xa4,
+ 0x8b, 0xad, 0xb8, 0xaa, 0x00, 0xc1, 0x00, 0xa8,
+ 0x00, 0xe2,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94,
+ 0xae, 0xbf, 0xa8, 0xb9, 0xc8, 0xb3, 0x92, 0xa4,
+ 0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xc4, 0x00, 0xab,
+ 0x00, 0xe6,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb6, 0x98,
+ 0xaf, 0xc0, 0xa8, 0xb8, 0xc7, 0xb2, 0x93, 0xa5,
+ 0x8d, 0xad, 0xb7, 0xa9, 0x00, 0xc7, 0x00, 0xae,
+ 0x00, 0xe9,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95,
+ 0xaf, 0xc1, 0xa9, 0xb9, 0xc8, 0xb3, 0x92, 0xa4,
+ 0x8b, 0xad, 0xb7, 0xaa, 0x00, 0xc9, 0x00, 0xb0,
+ 0x00, 0xec,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95,
+ 0xac, 0xbe, 0xa6, 0xbb, 0xc9, 0xb4, 0x90, 0xa3,
+ 0x8a, 0xad, 0xb7, 0xa9, 0x00, 0xcc, 0x00, 0xb4,
+ 0x00, 0xf0,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xb0, 0x91,
+ 0xae, 0xc0, 0xa6, 0xba, 0xc8, 0xb4, 0x91, 0xa4,
+ 0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xcf, 0x00, 0xb7,
+ 0x00, 0xf3,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb8, 0x98,
+ 0xab, 0xbd, 0xa4, 0xbb, 0xc9, 0xb5, 0x91, 0xa3,
+ 0x8b, 0xac, 0xb6, 0xa8, 0x00, 0xd1, 0x00, 0xb9,
+ 0x00, 0xf6,
+ }, {
+ 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb5, 0x95,
+ 0xa9, 0xbc, 0xa1, 0xbb, 0xc9, 0xb5, 0x91, 0xa3,
+ 0x8a, 0xad, 0xb6, 0xa8, 0x00, 0xd6, 0x00, 0xbf,
+ 0x00, 0xfc,
+ },
+};
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v96[GAMMA_LEVEL_NUM] = {
+ {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xdf, 0x1f, 0xd7, 0xdc, 0xb7, 0xe1, 0xc0, 0xaf,
+ 0xc4, 0xd2, 0xd0, 0xcf, 0x00, 0x4d, 0x00, 0x40,
+ 0x00, 0x5f,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xd5, 0x35, 0xcf, 0xdc, 0xc1, 0xe1, 0xbf, 0xb3,
+ 0xc1, 0xd2, 0xd1, 0xce, 0x00, 0x53, 0x00, 0x46,
+ 0x00, 0x67,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xd2, 0x64, 0xcf, 0xdb, 0xc6, 0xe1, 0xbd, 0xb3,
+ 0xbd, 0xd2, 0xd2, 0xce, 0x00, 0x59, 0x00, 0x4b,
+ 0x00, 0x6e,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xd0, 0x7c, 0xcf, 0xdb, 0xc9, 0xe0, 0xbc, 0xb4,
+ 0xbb, 0xcf, 0xd1, 0xcc, 0x00, 0x5f, 0x00, 0x50,
+ 0x00, 0x75,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xd0, 0x8e, 0xd1, 0xdb, 0xcc, 0xdf, 0xbb, 0xb6,
+ 0xb9, 0xd0, 0xd1, 0xcd, 0x00, 0x63, 0x00, 0x54,
+ 0x00, 0x7a,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xd1, 0x9e, 0xd5, 0xda, 0xcd, 0xdd, 0xbb, 0xb7,
+ 0xb9, 0xce, 0xce, 0xc9, 0x00, 0x68, 0x00, 0x59,
+ 0x00, 0x81,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+ 0xd0, 0xa5, 0xd6, 0xda, 0xcf, 0xdd, 0xbb, 0xb7,
+ 0xb8, 0xcc, 0xcd, 0xc7, 0x00, 0x6c, 0x00, 0x5c,
+ 0x00, 0x86,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xfe,
+ 0xd0, 0xae, 0xd7, 0xd9, 0xd0, 0xdb, 0xb9, 0xb6,
+ 0xb5, 0xca, 0xcc, 0xc5, 0x00, 0x74, 0x00, 0x63,
+ 0x00, 0x90,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf9,
+ 0xcf, 0xb0, 0xd6, 0xd9, 0xd1, 0xdb, 0xb9, 0xb6,
+ 0xb4, 0xca, 0xcb, 0xc5, 0x00, 0x77, 0x00, 0x66,
+ 0x00, 0x94,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf7,
+ 0xcf, 0xb3, 0xd7, 0xd8, 0xd1, 0xd9, 0xb7, 0xb6,
+ 0xb3, 0xc9, 0xca, 0xc3, 0x00, 0x7b, 0x00, 0x69,
+ 0x00, 0x99,
+
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfd, 0x2f, 0xf7,
+ 0xdf, 0xb5, 0xd6, 0xd8, 0xd1, 0xd8, 0xb6, 0xb5,
+ 0xb2, 0xca, 0xcb, 0xc4, 0x00, 0x7e, 0x00, 0x6c,
+ 0x00, 0x9d,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfa, 0x2f, 0xf5,
+ 0xce, 0xb6, 0xd5, 0xd7, 0xd2, 0xd8, 0xb6, 0xb4,
+ 0xb0, 0xc7, 0xc9, 0xc1, 0x00, 0x84, 0x00, 0x71,
+ 0x00, 0xa5,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf7, 0x2f, 0xf2,
+ 0xce, 0xb9, 0xd5, 0xd8, 0xd2, 0xd8, 0xb4, 0xb4,
+ 0xaf, 0xc7, 0xc9, 0xc1, 0x00, 0x87, 0x00, 0x73,
+ 0x00, 0xa8,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf5, 0x2f, 0xf0,
+ 0xdf, 0xba, 0xd5, 0xd7, 0xd2, 0xd7, 0xb4, 0xb4,
+ 0xaf, 0xc5, 0xc7, 0xbf, 0x00, 0x8a, 0x00, 0x76,
+ 0x00, 0xac,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf2, 0x2f, 0xed,
+ 0xcE, 0xbb, 0xd4, 0xd6, 0xd2, 0xd6, 0xb5, 0xb4,
+ 0xaF, 0xc5, 0xc7, 0xbf, 0x00, 0x8c, 0x00, 0x78,
+ 0x00, 0xaf,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x2f, 0xeb,
+ 0xcd, 0xbb, 0xd2, 0xd7, 0xd3, 0xd6, 0xb3, 0xb4,
+ 0xae, 0xc5, 0xc6, 0xbe, 0x00, 0x91, 0x00, 0x7d,
+ 0x00, 0xb6,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xee, 0x2f, 0xea,
+ 0xce, 0xbd, 0xd4, 0xd6, 0xd2, 0xd5, 0xb2, 0xb3,
+ 0xad, 0xc3, 0xc4, 0xbb, 0x00, 0x94, 0x00, 0x7f,
+ 0x00, 0xba,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xec, 0x2f, 0xe8,
+ 0xce, 0xbe, 0xd3, 0xd6, 0xd3, 0xd5, 0xb2, 0xb2,
+ 0xac, 0xc3, 0xc5, 0xbc, 0x00, 0x96, 0x00, 0x81,
+ 0x00, 0xbd,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xeb, 0x2f, 0xe7,
+ 0xce, 0xbf, 0xd3, 0xd6, 0xd2, 0xd5, 0xb1, 0xb2,
+ 0xab, 0xc2, 0xc4, 0xbb, 0x00, 0x99, 0x00, 0x83,
+ 0x00, 0xc0,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x5f, 0xe9,
+ 0xca, 0xbf, 0xd3, 0xd5, 0xd2, 0xd4, 0xb2, 0xb2,
+ 0xab, 0xc1, 0xc4, 0xba, 0x00, 0x9b, 0x00, 0x85,
+ 0x00, 0xc3,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xea, 0x5f, 0xe8,
+ 0xee, 0xbf, 0xd2, 0xd5, 0xd2, 0xd4, 0xb1, 0xb2,
+ 0xab, 0xc1, 0xc2, 0xb9, 0x00, 0x9D, 0x00, 0x87,
+ 0x00, 0xc6,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe9, 0x5f, 0xe7,
+ 0xcd, 0xbf, 0xd2, 0xd6, 0xd2, 0xd4, 0xb1, 0xb2,
+ 0xab, 0xbe, 0xc0, 0xb7, 0x00, 0xa1, 0x00, 0x8a,
+ 0x00, 0xca,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x61, 0xe6,
+ 0xcd, 0xbf, 0xd1, 0xd6, 0xd3, 0xd4, 0xaf, 0xb0,
+ 0xa9, 0xbe, 0xc1, 0xb7, 0x00, 0xa3, 0x00, 0x8b,
+ 0x00, 0xce,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x62, 0xe5,
+ 0xcc, 0xc0, 0xd0, 0xd6, 0xd2, 0xd4, 0xaf, 0xb1,
+ 0xa9, 0xbd, 0xc0, 0xb6, 0x00, 0xa5, 0x00, 0x8d,
+ 0x00, 0xd0,
+ }, {
+ 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe7, 0x7f, 0xe3,
+ 0xcc, 0xc1, 0xd0, 0xd5, 0xd3, 0xd3, 0xae, 0xaf,
+ 0xa8, 0xbe, 0xc0, 0xb7, 0x00, 0xa8, 0x00, 0x90,
+ 0x00, 0xd3,
+ }
+};
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v32[GAMMA_LEVEL_NUM] = {
+ {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0x72, 0x5e, 0x6b,
+ 0xa1, 0xa7, 0x9a, 0xb4, 0xcb, 0xb8, 0x92, 0xac,
+ 0x97, 0xb4, 0xc3, 0xb5, 0x00, 0x4e, 0x00, 0x37,
+ 0x00, 0x58,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0x85, 0x71, 0x7d,
+ 0xa6, 0xb6, 0xa1, 0xb5, 0xca, 0xba, 0x93, 0xac,
+ 0x98, 0xb2, 0xc0, 0xaf, 0x00, 0x59, 0x00, 0x43,
+ 0x00, 0x64,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa4, 0x94, 0x9e,
+ 0xa0, 0xbb, 0x9c, 0xc3, 0xd2, 0xc6, 0x93, 0xaa,
+ 0x95, 0xb7, 0xc2, 0xb4, 0x00, 0x65, 0x00, 0x50,
+ 0x00, 0x74,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa1, 0xa6,
+ 0xa0, 0xb9, 0x9b, 0xc3, 0xd1, 0xc8, 0x90, 0xa6,
+ 0x90, 0xbb, 0xc3, 0xb7, 0x00, 0x6f, 0x00, 0x5b,
+ 0x00, 0x80,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa6, 0x9d, 0x9f,
+ 0x9f, 0xb8, 0x9a, 0xc7, 0xd5, 0xcc, 0x90, 0xa5,
+ 0x8f, 0xb8, 0xc1, 0xb6, 0x00, 0x74, 0x00, 0x60,
+ 0x00, 0x85,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb3, 0xae, 0xae,
+ 0x9e, 0xb7, 0x9a, 0xc8, 0xd6, 0xce, 0x91, 0xa6,
+ 0x90, 0xb6, 0xc0, 0xb3, 0x00, 0x78, 0x00, 0x65,
+ 0x00, 0x8a,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa9, 0xa8,
+ 0xa3, 0xb9, 0x9e, 0xc4, 0xd3, 0xcb, 0x94, 0xa6,
+ 0x90, 0xb6, 0xbf, 0xb3, 0x00, 0x7c, 0x00, 0x69,
+ 0x00, 0x8e,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xaf, 0xaf, 0xa9,
+ 0xa5, 0xbc, 0xa2, 0xc7, 0xd5, 0xcd, 0x93, 0xa5,
+ 0x8f, 0xb4, 0xbd, 0xb1, 0x00, 0x83, 0x00, 0x70,
+ 0x00, 0x96,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xab, 0xa3,
+ 0xaa, 0xbf, 0xa7, 0xc5, 0xd3, 0xcb, 0x93, 0xa5,
+ 0x8f, 0xb2, 0xbb, 0xb0, 0x00, 0x86, 0x00, 0x74,
+ 0x00, 0x9b,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xb5, 0xab,
+ 0xab, 0xc0, 0xa9, 0xc7, 0xd4, 0xcc, 0x94, 0xa4,
+ 0x8f, 0xb1, 0xbb, 0xaf, 0x00, 0x8a, 0x00, 0x77,
+ 0x00, 0x9e,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb2, 0xa7,
+ 0xae, 0xc2, 0xab, 0xc5, 0xd3, 0xca, 0x93, 0xa4,
+ 0x8f, 0xb1, 0xba, 0xae, 0x00, 0x8d, 0x00, 0x7b,
+ 0x00, 0xa2,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xaf, 0xa3,
+ 0xb0, 0xc3, 0xae, 0xc4, 0xd1, 0xc8, 0x93, 0xa4,
+ 0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x8f, 0x00, 0x7d,
+ 0x00, 0xa5,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbd, 0xaf,
+ 0xae, 0xc1, 0xab, 0xc2, 0xd0, 0xc6, 0x94, 0xa4,
+ 0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x92, 0x00, 0x80,
+ 0x00, 0xa8,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xb9, 0xac,
+ 0xad, 0xc1, 0xab, 0xc4, 0xd1, 0xc7, 0x95, 0xa4,
+ 0x90, 0xb0, 0xb9, 0xad, 0x00, 0x95, 0x00, 0x84,
+ 0x00, 0xac,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb6, 0xa7,
+ 0xaf, 0xc2, 0xae, 0xc5, 0xd1, 0xc7, 0x93, 0xa3,
+ 0x8e, 0xb0, 0xb9, 0xad, 0x00, 0x98, 0x00, 0x86,
+ 0x00, 0xaf,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbf, 0xaf,
+ 0xad, 0xc1, 0xab, 0xc3, 0xd0, 0xc6, 0x94, 0xa3,
+ 0x8f, 0xaf, 0xb8, 0xac, 0x00, 0x9a, 0x00, 0x89,
+ 0x00, 0xb2,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xbc, 0xac,
+ 0xaf, 0xc2, 0xad, 0xc2, 0xcf, 0xc4, 0x94, 0xa3,
+ 0x90, 0xaf, 0xb8, 0xad, 0x00, 0x9c, 0x00, 0x8b,
+ 0x00, 0xb5,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7,
+ 0xb1, 0xc4, 0xaf, 0xc3, 0xcf, 0xc5, 0x94, 0xa3,
+ 0x8f, 0xae, 0xb7, 0xac, 0x00, 0x9f, 0x00, 0x8e,
+ 0x00, 0xb8,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7,
+ 0xaf, 0xc2, 0xad, 0xc1, 0xce, 0xc3, 0x95, 0xa3,
+ 0x90, 0xad, 0xb6, 0xab, 0x00, 0xa2, 0x00, 0x91,
+ 0x00, 0xbb,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xbe, 0xac,
+ 0xb1, 0xc4, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa4,
+ 0x91, 0xad, 0xb6, 0xab, 0x00, 0xa4, 0x00, 0x93,
+ 0x00, 0xbd,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8,
+ 0xb3, 0xc5, 0xb2, 0xc1, 0xcd, 0xc2, 0x95, 0xa3,
+ 0x90, 0xad, 0xb6, 0xab, 0x00, 0xa6, 0x00, 0x95,
+ 0x00, 0xc0,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8,
+ 0xb0, 0xc3, 0xaf, 0xc2, 0xce, 0xc2, 0x94, 0xa2,
+ 0x90, 0xac, 0xb6, 0xab, 0x00, 0xa8, 0x00, 0x98,
+ 0x00, 0xc3,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xb8, 0xa5,
+ 0xb3, 0xc5, 0xb2, 0xc1, 0xcc, 0xc0, 0x95, 0xa2,
+ 0x90, 0xad, 0xb6, 0xab, 0x00, 0xaa, 0x00, 0x9a,
+ 0x00, 0xc5,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xc0, 0xac,
+ 0xb0, 0xc3, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa2,
+ 0x90, 0xac, 0xb5, 0xa9, 0x00, 0xac, 0x00, 0x9c,
+ 0x00, 0xc8,
+ }, {
+ 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbd, 0xa8,
+ 0xaf, 0xc2, 0xaf, 0xc1, 0xcc, 0xc0, 0x95, 0xa2,
+ 0x90, 0xac, 0xb5, 0xaa, 0x00, 0xb1, 0x00, 0xa1,
+ 0x00, 0xcc,
+ },
+};
+
+static const struct s6e8aa0_variant s6e8aa0_variants[] = {
+ {
+ .version = 32,
+ .gamma_tables = s6e8aa0_gamma_tables_v32,
+ }, {
+ .version = 96,
+ .gamma_tables = s6e8aa0_gamma_tables_v96,
+ }, {
+ .version = 142,
+ .gamma_tables = s6e8aa0_gamma_tables_v142,
+ }, {
+ .version = 210,
+ .gamma_tables = s6e8aa0_gamma_tables_v142,
+ }
+};
+
+static void s6e8aa0_brightness_set(struct s6e8aa0 *ctx)
+{
+ const u8 *gamma;
+
+ if (ctx->error)
+ return;
+
+ gamma = ctx->variant->gamma_tables[ctx->brightness];
+
+ if (ctx->version >= 142)
+ s6e8aa0_elvss_nvm_set(ctx);
+
+ s6e8aa0_dcs_write(ctx, gamma, GAMMA_TABLE_LEN);
+
+ /* update gamma table. */
+ s6e8aa0_dcs_write_seq_static(ctx, 0xf7, 0x03);
+}
+
+static void s6e8aa0_panel_init(struct s6e8aa0 *ctx)
+{
+ s6e8aa0_apply_level_1_key(ctx);
+ s6e8aa0_apply_level_2_key(ctx);
+ msleep(20);
+
+ s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(40);
+
+ s6e8aa0_panel_cond_set(ctx);
+ s6e8aa0_display_condition_set(ctx);
+ s6e8aa0_brightness_set(ctx);
+ s6e8aa0_etc_source_control(ctx);
+ s6e8aa0_etc_pentile_control(ctx);
+ s6e8aa0_elvss_nvm_set(ctx);
+ s6e8aa0_etc_power_control(ctx);
+ s6e8aa0_etc_elvss_control(ctx);
+ msleep(ctx->init_delay);
+}
+
+static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx,
+ int size)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+ u8 buf[] = {size, 0};
+ struct mipi_dsi_msg msg = {
+ .channel = dsi->channel,
+ .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
+ .tx_len = sizeof(buf),
+ .tx_buf = buf
+ };
+ int ret;
+
+ if (ctx->error < 0)
+ return;
+
+ if (!ops || !ops->transfer)
+ ret = -EIO;
+ else
+ ret = ops->transfer(dsi->host, &msg);
+
+ if (ret < 0) {
+ dev_err(ctx->dev,
+ "error %d setting maximum return packet size to %d\n",
+ ret, size);
+ ctx->error = ret;
+ }
+}
+
+static void s6e8aa0_read_mtp_id(struct s6e8aa0 *ctx)
+{
+ u8 id[3];
+ int ret, i;
+
+ ret = s6e8aa0_dcs_read(ctx, 0xd1, id, ARRAY_SIZE(id));
+ if (ret < ARRAY_SIZE(id) || id[0] == 0x00) {
+ dev_err(ctx->dev, "read id failed\n");
+ ctx->error = -EIO;
+ return;
+ }
+
+ dev_info(ctx->dev, "ID: 0x%2x, 0x%2x, 0x%2x\n", id[0], id[1], id[2]);
+
+ for (i = 0; i < ARRAY_SIZE(s6e8aa0_variants); ++i) {
+ if (id[1] == s6e8aa0_variants[i].version)
+ break;
+ }
+ if (i >= ARRAY_SIZE(s6e8aa0_variants)) {
+ dev_err(ctx->dev, "unsupported display version %d\n", id[1]);
+ ctx->error = -EINVAL;
+ }
+
+ ctx->variant = &s6e8aa0_variants[i];
+ ctx->version = id[1];
+ ctx->id = id[2];
+}
+
+static void s6e8aa0_set_sequence(struct s6e8aa0 *ctx)
+{
+ s6e8aa0_set_maximum_return_packet_size(ctx, 3);
+ s6e8aa0_read_mtp_id(ctx);
+ s6e8aa0_panel_init(ctx);
+ s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
+}
+
+static int s6e8aa0_power_on(struct s6e8aa0 *ctx)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ msleep(ctx->power_on_delay);
+
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+ gpiod_set_value(ctx->reset_gpio, 1);
+
+ msleep(ctx->reset_delay);
+
+ return 0;
+}
+
+static int s6e8aa0_power_off(struct s6e8aa0 *ctx)
+{
+ return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int s6e8aa0_disable(struct drm_panel *panel)
+{
+ struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
+
+ s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
+ s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
+ msleep(40);
+
+ s6e8aa0_clear_error(ctx);
+
+ return s6e8aa0_power_off(ctx);
+}
+
+static int s6e8aa0_enable(struct drm_panel *panel)
+{
+ struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
+ int ret;
+
+ ret = s6e8aa0_power_on(ctx);
+ if (ret < 0)
+ return ret;
+
+ s6e8aa0_set_sequence(ctx);
+ ret = ctx->error;
+
+ if (ret < 0)
+ s6e8aa0_disable(panel);
+
+ return ret;
+}
+
+static int s6e8aa0_get_modes(struct drm_panel *panel)
+{
+ struct drm_connector *connector = panel->connector;
+ struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_create(connector->dev);
+ if (!mode) {
+ DRM_ERROR("failed to create a new display mode\n");
+ return 0;
+ }
+
+ drm_display_mode_from_videomode(&ctx->vm, mode);
+ mode->width_mm = ctx->width_mm;
+ mode->height_mm = ctx->height_mm;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs s6e8aa0_drm_funcs = {
+ .disable = s6e8aa0_disable,
+ .enable = s6e8aa0_enable,
+ .get_modes = s6e8aa0_get_modes,
+};
+
+static int s6e8aa0_parse_dt(struct s6e8aa0 *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ ret = of_get_videomode(np, &ctx->vm, 0);
+ if (ret < 0)
+ return ret;
+
+ of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay);
+ of_property_read_u32(np, "reset-delay", &ctx->reset_delay);
+ of_property_read_u32(np, "init-delay", &ctx->init_delay);
+ of_property_read_u32(np, "panel-width-mm", &ctx->width_mm);
+ of_property_read_u32(np, "panel-height-mm", &ctx->height_mm);
+
+ ctx->flip_horizontal = of_property_read_bool(np, "flip-horizontal");
+ ctx->flip_vertical = of_property_read_bool(np, "flip-vertical");
+
+ return 0;
+}
+
+static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct s6e8aa0 *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(struct s6e8aa0), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ ctx->dev = dev;
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST
+ | MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP
+ | MIPI_DSI_MODE_VIDEO_HSA | MIPI_DSI_MODE_EOT_PACKET
+ | MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_AUTO_VERT;
+
+ ret = s6e8aa0_parse_dt(ctx);
+ if (ret < 0)
+ return ret;
+
+ ctx->supplies[0].supply = "vdd3";
+ ctx->supplies[1].supply = "vci";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+ ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset");
+ if (IS_ERR(ctx->reset_gpio)) {
+ dev_err(dev, "cannot get reset-gpios %ld\n",
+ PTR_ERR(ctx->reset_gpio));
+ return PTR_ERR(ctx->reset_gpio);
+ }
+ ret = gpiod_direction_output(ctx->reset_gpio, 1);
+ if (ret < 0) {
+ dev_err(dev, "cannot configure reset-gpios %d\n", ret);
+ return ret;
+ }
+
+ ctx->brightness = GAMMA_LEVEL_NUM - 1;
+
+ drm_panel_init(&ctx->panel);
+ ctx->panel.dev = dev;
+ ctx->panel.funcs = &s6e8aa0_drm_funcs;
+
+ ret = drm_panel_add(&ctx->panel);
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0)
+ drm_panel_remove(&ctx->panel);
+
+ return ret;
+}
+
+static int s6e8aa0_remove(struct mipi_dsi_device *dsi)
+{
+ struct s6e8aa0 *ctx = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_panel_remove(&ctx->panel);
+
+ return 0;
+}
+
+static struct of_device_id s6e8aa0_of_match[] = {
+ { .compatible = "samsung,s6e8aa0" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s6e8aa0_of_match);
+
+static struct mipi_dsi_driver s6e8aa0_driver = {
+ .probe = s6e8aa0_probe,
+ .remove = s6e8aa0_remove,
+ .driver = {
+ .name = "panel_s6e8aa0",
+ .owner = THIS_MODULE,
+ .of_match_table = s6e8aa0_of_match,
+ },
+};
+module_mipi_dsi_driver(s6e8aa0_driver);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>");
+MODULE_AUTHOR("Eunchul Kim <chulspro.kim@samsung.com>");
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e8aa0 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 59d52ca2c67f..309f29e9234a 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -22,9 +22,8 @@
*/
#include <linux/backlight.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
@@ -44,9 +43,6 @@ struct panel_desc {
} size;
};
-/* TODO: convert to gpiod_*() API once it's been merged */
-#define GPIO_ACTIVE_LOW (1 << 0)
-
struct panel_simple {
struct drm_panel base;
bool enabled;
@@ -57,8 +53,7 @@ struct panel_simple {
struct regulator *supply;
struct i2c_adapter *ddc;
- unsigned long enable_gpio_flags;
- int enable_gpio;
+ struct gpio_desc *enable_gpio;
};
static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
@@ -110,12 +105,8 @@ static int panel_simple_disable(struct drm_panel *panel)
backlight_update_status(p->backlight);
}
- if (gpio_is_valid(p->enable_gpio)) {
- if (p->enable_gpio_flags & GPIO_ACTIVE_LOW)
- gpio_set_value(p->enable_gpio, 1);
- else
- gpio_set_value(p->enable_gpio, 0);
- }
+ if (p->enable_gpio)
+ gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply);
p->enabled = false;
@@ -137,12 +128,8 @@ static int panel_simple_enable(struct drm_panel *panel)
return err;
}
- if (gpio_is_valid(p->enable_gpio)) {
- if (p->enable_gpio_flags & GPIO_ACTIVE_LOW)
- gpio_set_value(p->enable_gpio, 0);
- else
- gpio_set_value(p->enable_gpio, 1);
- }
+ if (p->enable_gpio)
+ gpiod_set_value_cansleep(p->enable_gpio, 1);
if (p->backlight) {
p->backlight->props.power = FB_BLANK_UNBLANK;
@@ -185,7 +172,6 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
{
struct device_node *backlight, *ddc;
struct panel_simple *panel;
- enum of_gpio_flags flags;
int err;
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
@@ -199,29 +185,20 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
if (IS_ERR(panel->supply))
return PTR_ERR(panel->supply);
- panel->enable_gpio = of_get_named_gpio_flags(dev->of_node,
- "enable-gpios", 0,
- &flags);
- if (gpio_is_valid(panel->enable_gpio)) {
- unsigned int value;
-
- if (flags & OF_GPIO_ACTIVE_LOW)
- panel->enable_gpio_flags |= GPIO_ACTIVE_LOW;
-
- err = gpio_request(panel->enable_gpio, "enable");
- if (err < 0) {
- dev_err(dev, "failed to request GPIO#%u: %d\n",
- panel->enable_gpio, err);
+ panel->enable_gpio = devm_gpiod_get(dev, "enable");
+ if (IS_ERR(panel->enable_gpio)) {
+ err = PTR_ERR(panel->enable_gpio);
+ if (err != -ENOENT) {
+ dev_err(dev, "failed to request GPIO: %d\n", err);
return err;
}
- value = (panel->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
-
- err = gpio_direction_output(panel->enable_gpio, value);
+ panel->enable_gpio = NULL;
+ } else {
+ err = gpiod_direction_output(panel->enable_gpio, 0);
if (err < 0) {
- dev_err(dev, "failed to setup GPIO%u: %d\n",
- panel->enable_gpio, err);
- goto free_gpio;
+ dev_err(dev, "failed to setup GPIO: %d\n", err);
+ return err;
}
}
@@ -230,10 +207,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
panel->backlight = of_find_backlight_by_node(backlight);
of_node_put(backlight);
- if (!panel->backlight) {
- err = -EPROBE_DEFER;
- goto free_gpio;
- }
+ if (!panel->backlight)
+ return -EPROBE_DEFER;
}
ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
@@ -265,9 +240,6 @@ free_ddc:
free_backlight:
if (panel->backlight)
put_device(&panel->backlight->dev);
-free_gpio:
- if (gpio_is_valid(panel->enable_gpio))
- gpio_free(panel->enable_gpio);
return err;
}
@@ -287,11 +259,6 @@ static int panel_simple_remove(struct device *dev)
if (panel->backlight)
put_device(&panel->backlight->dev);
- if (gpio_is_valid(panel->enable_gpio))
- gpio_free(panel->enable_gpio);
-
- regulator_disable(panel->supply);
-
return 0;
}
@@ -361,6 +328,28 @@ static const struct panel_desc chunghwa_claa101wb01 = {
},
};
+static const struct drm_display_mode lg_lp129qe_mode = {
+ .clock = 285250,
+ .hdisplay = 2560,
+ .hsync_start = 2560 + 48,
+ .hsync_end = 2560 + 48 + 32,
+ .htotal = 2560 + 48 + 32 + 80,
+ .vdisplay = 1700,
+ .vsync_start = 1700 + 3,
+ .vsync_end = 1700 + 3 + 10,
+ .vtotal = 1700 + 3 + 10 + 36,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc lg_lp129qe = {
+ .modes = &lg_lp129qe_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 272,
+ .height = 181,
+ },
+};
+
static const struct drm_display_mode samsung_ltn101nt05_mode = {
.clock = 54030,
.hdisplay = 1024,
@@ -394,6 +383,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "chunghwa,claa101wb01",
.data = &chunghwa_claa101wb01
}, {
+ .compatible = "lg,lp129qe",
+ .data = &lg_lp129qe,
+ }, {
.compatible = "samsung,ltn101nt05",
.data = &samsung_ltn101nt05,
}, {
@@ -433,10 +425,65 @@ static struct platform_driver panel_simple_platform_driver = {
struct panel_desc_dsi {
struct panel_desc desc;
+ unsigned long flags;
enum mipi_dsi_pixel_format format;
unsigned int lanes;
};
+static const struct drm_display_mode lg_ld070wx3_sl01_mode = {
+ .clock = 71000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 32,
+ .hsync_end = 800 + 32 + 1,
+ .htotal = 800 + 32 + 1 + 57,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 28,
+ .vsync_end = 1280 + 28 + 1,
+ .vtotal = 1280 + 28 + 1 + 14,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
+ .desc = {
+ .modes = &lg_ld070wx3_sl01_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 94,
+ .height = 151,
+ },
+ },
+ .flags = MIPI_DSI_MODE_VIDEO,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+};
+
+static const struct drm_display_mode lg_lh500wx1_sd03_mode = {
+ .clock = 67000,
+ .hdisplay = 720,
+ .hsync_start = 720 + 12,
+ .hsync_end = 720 + 12 + 4,
+ .htotal = 720 + 12 + 4 + 112,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 8,
+ .vsync_end = 1280 + 8 + 4,
+ .vtotal = 1280 + 8 + 4 + 12,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc_dsi lg_lh500wx1_sd03 = {
+ .desc = {
+ .modes = &lg_lh500wx1_sd03_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 62,
+ .height = 110,
+ },
+ },
+ .flags = MIPI_DSI_MODE_VIDEO,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+};
+
static const struct drm_display_mode panasonic_vvx10f004b00_mode = {
.clock = 157200,
.hdisplay = 1920,
@@ -459,12 +506,19 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
.height = 136,
},
},
+ .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 4,
};
static const struct of_device_id dsi_of_match[] = {
{
+ .compatible = "lg,ld070wx3-sl01",
+ .data = &lg_ld070wx3_sl01
+ }, {
+ .compatible = "lg,lh500wx1-sd03",
+ .data = &lg_lh500wx1_sd03
+ }, {
.compatible = "panasonic,vvx10f004b00",
.data = &panasonic_vvx10f004b00
}, {
@@ -489,6 +543,7 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
if (err < 0)
return err;
+ dsi->mode_flags = desc->flags;
dsi->format = desc->format;
dsi->lanes = desc->lanes;
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 798bde2e5881..41bdd174657e 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -527,7 +527,7 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
bool recreate_primary = false;
int ret;
int surf_id;
- if (!crtc->fb) {
+ if (!crtc->primary->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
@@ -536,7 +536,7 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
qfb = to_qxl_framebuffer(old_fb);
old_bo = gem_to_qxl_bo(qfb->obj);
}
- qfb = to_qxl_framebuffer(crtc->fb);
+ qfb = to_qxl_framebuffer(crtc->primary->fb);
bo = gem_to_qxl_bo(qfb->obj);
if (!m)
/* and do we care? */
@@ -609,14 +609,14 @@ static void qxl_crtc_disable(struct drm_crtc *crtc)
struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct qxl_device *qdev = dev->dev_private;
- if (crtc->fb) {
- struct qxl_framebuffer *qfb = to_qxl_framebuffer(crtc->fb);
+ if (crtc->primary->fb) {
+ struct qxl_framebuffer *qfb = to_qxl_framebuffer(crtc->primary->fb);
struct qxl_bo *bo = gem_to_qxl_bo(qfb->obj);
int ret;
ret = qxl_bo_reserve(bo, false);
qxl_bo_unpin(bo);
qxl_bo_unreserve(bo);
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
}
qxl_monitors_config_set(qdev, qcrtc->index, 0, 0, 0, 0, 0);
diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c
index 8691c76c5ef0..b95f144f0b49 100644
--- a/drivers/gpu/drm/qxl/qxl_object.c
+++ b/drivers/gpu/drm/qxl/qxl_object.c
@@ -82,8 +82,6 @@ int qxl_bo_create(struct qxl_device *qdev,
enum ttm_bo_type type;
int r;
- if (unlikely(qdev->mman.bdev.dev_mapping == NULL))
- qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping;
if (kernel)
type = ttm_bo_type_kernel;
else
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index 821ab7b9409b..14e776f1d14e 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -349,7 +349,7 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release)
qxl_fence_add_release_locked(&qbo->fence, release->id);
ttm_bo_add_to_lru(bo);
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
entry->reserved = false;
}
spin_unlock(&bdev->fence_lock);
diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c
index c7e7e6590c2b..d52c27527b9a 100644
--- a/drivers/gpu/drm/qxl/qxl_ttm.c
+++ b/drivers/gpu/drm/qxl/qxl_ttm.c
@@ -433,6 +433,7 @@ static int qxl_sync_obj_flush(void *sync_obj)
static void qxl_sync_obj_unref(void **sync_obj)
{
+ *sync_obj = NULL;
}
static void *qxl_sync_obj_ref(void *sync_obj)
@@ -493,7 +494,9 @@ int qxl_ttm_init(struct qxl_device *qdev)
/* No others user of address space so set it to 0 */
r = ttm_bo_device_init(&qdev->mman.bdev,
qdev->mman.bo_global_ref.ref.object,
- &qxl_bo_driver, DRM_FILE_PAGE_OFFSET, 0);
+ &qxl_bo_driver,
+ qdev->ddev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET, 0);
if (r) {
DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
return r;
@@ -518,8 +521,6 @@ int qxl_ttm_init(struct qxl_device *qdev)
((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024));
DRM_INFO("qxl: %uM of Surface memory size\n",
(unsigned)qdev->surfaceram_size / (1024 * 1024));
- if (unlikely(qdev->mman.bdev.dev_mapping == NULL))
- qdev->mman.bdev.dev_mapping = qdev->ddev->dev_mapping;
r = qxl_ttm_debugfs_init(qdev);
if (r) {
DRM_ERROR("Failed to init debugfs\n");
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 306364a1ecda..09433534dc47 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
- ci_dpm.o dce6_afmt.o
+ ci_dpm.o dce6_afmt.o radeon_vm.o
# add async DMA block
radeon-y += \
@@ -99,6 +99,12 @@ radeon-y += \
uvd_v3_1.o \
uvd_v4_2.o
+# add VCE block
+radeon-y += \
+ radeon_vce.o \
+ vce_v1_0.o \
+ vce_v2_0.o \
+
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
radeon-$(CONFIG_ACPI) += radeon_acpi.o
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index daa4dd375ab1..fb187c78978f 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -1106,7 +1106,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
int r;
/* no fb bound */
- if (!atomic && !crtc->fb) {
+ if (!atomic && !crtc->primary->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
@@ -1116,8 +1116,8 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
target_fb = fb;
}
else {
- radeon_fb = to_radeon_framebuffer(crtc->fb);
- target_fb = crtc->fb;
+ radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
+ target_fb = crtc->primary->fb;
}
/* If atomic, assume fb object is pinned & idle & fenced and
@@ -1316,7 +1316,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
/* set pageflip to happen anywhere in vblank interval */
WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0);
- if (!atomic && fb && fb != crtc->fb) {
+ if (!atomic && fb && fb != crtc->primary->fb) {
radeon_fb = to_radeon_framebuffer(fb);
rbo = gem_to_radeon_bo(radeon_fb->obj);
r = radeon_bo_reserve(rbo, false);
@@ -1350,7 +1350,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
int r;
/* no fb bound */
- if (!atomic && !crtc->fb) {
+ if (!atomic && !crtc->primary->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
@@ -1360,8 +1360,8 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
target_fb = fb;
}
else {
- radeon_fb = to_radeon_framebuffer(crtc->fb);
- target_fb = crtc->fb;
+ radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
+ target_fb = crtc->primary->fb;
}
obj = radeon_fb->obj;
@@ -1485,7 +1485,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
/* set pageflip to happen anywhere in vblank interval */
WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0);
- if (!atomic && fb && fb != crtc->fb) {
+ if (!atomic && fb && fb != crtc->primary->fb) {
radeon_fb = to_radeon_framebuffer(fb);
rbo = gem_to_radeon_bo(radeon_fb->obj);
r = radeon_bo_reserve(rbo, false);
@@ -1972,12 +1972,12 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
int i;
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
- if (crtc->fb) {
+ if (crtc->primary->fb) {
int r;
struct radeon_framebuffer *radeon_fb;
struct radeon_bo *rbo;
- radeon_fb = to_radeon_framebuffer(crtc->fb);
+ radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
rbo = gem_to_radeon_bo(radeon_fb->obj);
r = radeon_bo_reserve(rbo, false);
if (unlikely(r))
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 4ad7643fce5f..8b0ab170cef9 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -142,101 +142,69 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
return recv_bytes;
}
-static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector,
- u16 address, u8 *send, u8 send_bytes, u8 delay)
-{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- int ret;
- u8 msg[20];
- int msg_bytes = send_bytes + 4;
- u8 ack;
- unsigned retry;
-
- if (send_bytes > 16)
- return -1;
+#define HEADER_SIZE 4
- msg[0] = address;
- msg[1] = address >> 8;
- msg[2] = DP_AUX_NATIVE_WRITE << 4;
- msg[3] = (msg_bytes << 4) | (send_bytes - 1);
- memcpy(&msg[4], send, send_bytes);
-
- for (retry = 0; retry < 7; retry++) {
- ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
- msg, msg_bytes, NULL, 0, delay, &ack);
- if (ret == -EBUSY)
- continue;
- else if (ret < 0)
- return ret;
- ack >>= 4;
- if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK)
- return send_bytes;
- else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER)
- usleep_range(400, 500);
- else
- return -EIO;
- }
-
- return -EIO;
-}
-
-static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector,
- u16 address, u8 *recv, int recv_bytes, u8 delay)
+static ssize_t
+radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- u8 msg[4];
- int msg_bytes = 4;
- u8 ack;
+ struct radeon_i2c_chan *chan =
+ container_of(aux, struct radeon_i2c_chan, aux);
int ret;
- unsigned retry;
-
- msg[0] = address;
- msg[1] = address >> 8;
- msg[2] = DP_AUX_NATIVE_READ << 4;
- msg[3] = (msg_bytes << 4) | (recv_bytes - 1);
-
- for (retry = 0; retry < 7; retry++) {
- ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
- msg, msg_bytes, recv, recv_bytes, delay, &ack);
- if (ret == -EBUSY)
- continue;
- else if (ret < 0)
- return ret;
- ack >>= 4;
- if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK)
- return ret;
- else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER)
- usleep_range(400, 500);
- else if (ret == 0)
- return -EPROTO;
- else
- return -EIO;
+ u8 tx_buf[20];
+ size_t tx_size;
+ u8 ack, delay = 0;
+
+ if (WARN_ON(msg->size > 16))
+ return -E2BIG;
+
+ tx_buf[0] = msg->address & 0xff;
+ tx_buf[1] = msg->address >> 8;
+ tx_buf[2] = msg->request << 4;
+ tx_buf[3] = msg->size - 1;
+
+ switch (msg->request & ~DP_AUX_I2C_MOT) {
+ case DP_AUX_NATIVE_WRITE:
+ case DP_AUX_I2C_WRITE:
+ tx_size = HEADER_SIZE + msg->size;
+ tx_buf[3] |= tx_size << 4;
+ memcpy(tx_buf + HEADER_SIZE, msg->buffer, msg->size);
+ ret = radeon_process_aux_ch(chan,
+ tx_buf, tx_size, NULL, 0, delay, &ack);
+ if (ret >= 0)
+ /* Return payload size. */
+ ret = msg->size;
+ break;
+ case DP_AUX_NATIVE_READ:
+ case DP_AUX_I2C_READ:
+ tx_size = HEADER_SIZE;
+ tx_buf[3] |= tx_size << 4;
+ ret = radeon_process_aux_ch(chan,
+ tx_buf, tx_size, msg->buffer, msg->size, delay, &ack);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
- return -EIO;
-}
+ if (ret > 0)
+ msg->reply = ack >> 4;
-static void radeon_write_dpcd_reg(struct radeon_connector *radeon_connector,
- u16 reg, u8 val)
-{
- radeon_dp_aux_native_write(radeon_connector, reg, &val, 1, 0);
+ return ret;
}
-static u8 radeon_read_dpcd_reg(struct radeon_connector *radeon_connector,
- u16 reg)
+void radeon_dp_aux_init(struct radeon_connector *radeon_connector)
{
- u8 val = 0;
-
- radeon_dp_aux_native_read(radeon_connector, reg, &val, 1, 0);
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- return val;
+ dig_connector->dp_i2c_bus->aux.dev = radeon_connector->base.kdev;
+ dig_connector->dp_i2c_bus->aux.transfer = radeon_dp_aux_transfer;
}
int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
u8 write_byte, u8 *read_byte)
{
struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
- struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
+ struct radeon_i2c_chan *auxch = i2c_get_adapdata(adapter);
u16 address = algo_data->address;
u8 msg[5];
u8 reply[2];
@@ -246,34 +214,30 @@ int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
int ret;
u8 ack;
- /* Set up the command byte */
- if (mode & MODE_I2C_READ)
- msg[2] = DP_AUX_I2C_READ << 4;
- else
- msg[2] = DP_AUX_I2C_WRITE << 4;
-
- if (!(mode & MODE_I2C_STOP))
- msg[2] |= DP_AUX_I2C_MOT << 4;
-
+ /* Set up the address */
msg[0] = address;
msg[1] = address >> 8;
- switch (mode) {
- case MODE_I2C_WRITE:
+ /* Set up the command byte */
+ if (mode & MODE_I2C_READ) {
+ msg[2] = DP_AUX_I2C_READ << 4;
+ msg_bytes = 4;
+ msg[3] = msg_bytes << 4;
+ } else {
+ msg[2] = DP_AUX_I2C_WRITE << 4;
msg_bytes = 5;
msg[3] = msg_bytes << 4;
msg[4] = write_byte;
- break;
- case MODE_I2C_READ:
- msg_bytes = 4;
- msg[3] = msg_bytes << 4;
- break;
- default:
- msg_bytes = 4;
- msg[3] = 3 << 4;
- break;
}
+ /* special handling for start/stop */
+ if (mode & (MODE_I2C_START | MODE_I2C_STOP))
+ msg[3] = 3 << 4;
+
+ /* Set MOT bit for all but stop */
+ if ((mode & MODE_I2C_STOP) == 0)
+ msg[2] |= DP_AUX_I2C_MOT << 4;
+
for (retry = 0; retry < 7; retry++) {
ret = radeon_process_aux_ch(auxch,
msg, msg_bytes, reply, reply_bytes, 0, &ack);
@@ -472,11 +436,11 @@ static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector)
if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
return;
- if (radeon_dp_aux_native_read(radeon_connector, DP_SINK_OUI, buf, 3, 0))
+ if (drm_dp_dpcd_read(&dig_connector->dp_i2c_bus->aux, DP_SINK_OUI, buf, 3))
DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
- if (radeon_dp_aux_native_read(radeon_connector, DP_BRANCH_OUI, buf, 3, 0))
+ if (drm_dp_dpcd_read(&dig_connector->dp_i2c_bus->aux, DP_BRANCH_OUI, buf, 3))
DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
}
@@ -487,8 +451,8 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
u8 msg[DP_DPCD_SIZE];
int ret, i;
- ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg,
- DP_DPCD_SIZE, 0);
+ ret = drm_dp_dpcd_read(&dig_connector->dp_i2c_bus->aux, DP_DPCD_REV, msg,
+ DP_DPCD_SIZE);
if (ret > 0) {
memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
DRM_DEBUG_KMS("DPCD: ");
@@ -510,6 +474,7 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector;
int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
u16 dp_bridge = radeon_connector_encoder_get_dp_bridge_encoder_id(connector);
u8 tmp;
@@ -517,9 +482,15 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
if (!ASIC_IS_DCE4(rdev))
return panel_mode;
+ if (!radeon_connector->con_priv)
+ return panel_mode;
+
+ dig_connector = radeon_connector->con_priv;
+
if (dp_bridge != ENCODER_OBJECT_ID_NONE) {
/* DP bridge chips */
- tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
+ drm_dp_dpcd_readb(&dig_connector->dp_i2c_bus->aux,
+ DP_EDP_CONFIGURATION_CAP, &tmp);
if (tmp & 1)
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) ||
@@ -529,7 +500,8 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
} else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
/* eDP */
- tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
+ drm_dp_dpcd_readb(&dig_connector->dp_i2c_bus->aux,
+ DP_EDP_CONFIGURATION_CAP, &tmp);
if (tmp & 1)
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
}
@@ -577,37 +549,42 @@ int radeon_dp_mode_valid_helper(struct drm_connector *connector,
return MODE_OK;
}
-static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
- u8 link_status[DP_LINK_STATUS_SIZE])
-{
- int ret;
- ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS,
- link_status, DP_LINK_STATUS_SIZE, 100);
- if (ret <= 0) {
- return false;
- }
-
- DRM_DEBUG_KMS("link status %6ph\n", link_status);
- return true;
-}
-
bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
{
u8 link_status[DP_LINK_STATUS_SIZE];
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
- if (!radeon_dp_get_link_status(radeon_connector, link_status))
+ if (drm_dp_dpcd_read_link_status(&dig->dp_i2c_bus->aux, link_status) <= 0)
return false;
if (drm_dp_channel_eq_ok(link_status, dig->dp_lane_count))
return false;
return true;
}
+void radeon_dp_set_rx_power_state(struct drm_connector *connector,
+ u8 power_state)
+{
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector;
+
+ if (!radeon_connector->con_priv)
+ return;
+
+ dig_connector = radeon_connector->con_priv;
+
+ /* power up/down the sink */
+ if (dig_connector->dpcd[0] >= 0x11) {
+ drm_dp_dpcd_writeb(&dig_connector->dp_i2c_bus->aux,
+ DP_SET_POWER, power_state);
+ usleep_range(1000, 2000);
+ }
+}
+
+
struct radeon_dp_link_train_info {
struct radeon_device *rdev;
struct drm_encoder *encoder;
struct drm_connector *connector;
- struct radeon_connector *radeon_connector;
int enc_id;
int dp_clock;
int dp_lane_count;
@@ -617,6 +594,7 @@ struct radeon_dp_link_train_info {
u8 link_status[DP_LINK_STATUS_SIZE];
u8 tries;
bool use_dpencoder;
+ struct drm_dp_aux *aux;
};
static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info)
@@ -627,8 +605,8 @@ static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info)
0, dp_info->train_set[0]); /* sets all lanes at once */
/* set the vs/emph on the sink */
- radeon_dp_aux_native_write(dp_info->radeon_connector, DP_TRAINING_LANE0_SET,
- dp_info->train_set, dp_info->dp_lane_count, 0);
+ drm_dp_dpcd_write(dp_info->aux, DP_TRAINING_LANE0_SET,
+ dp_info->train_set, dp_info->dp_lane_count);
}
static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp)
@@ -663,7 +641,7 @@ static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp)
}
/* enable training pattern on the sink */
- radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp);
+ drm_dp_dpcd_writeb(dp_info->aux, DP_TRAINING_PATTERN_SET, tp);
}
static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
@@ -673,34 +651,30 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
u8 tmp;
/* power up the sink */
- if (dp_info->dpcd[0] >= 0x11) {
- radeon_write_dpcd_reg(dp_info->radeon_connector,
- DP_SET_POWER, DP_SET_POWER_D0);
- usleep_range(1000, 2000);
- }
+ radeon_dp_set_rx_power_state(dp_info->connector, DP_SET_POWER_D0);
/* possibly enable downspread on the sink */
if (dp_info->dpcd[3] & 0x1)
- radeon_write_dpcd_reg(dp_info->radeon_connector,
- DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
+ drm_dp_dpcd_writeb(dp_info->aux,
+ DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
else
- radeon_write_dpcd_reg(dp_info->radeon_connector,
- DP_DOWNSPREAD_CTRL, 0);
+ drm_dp_dpcd_writeb(dp_info->aux,
+ 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);
+ drm_dp_dpcd_writeb(dp_info->aux, DP_EDP_CONFIGURATION_SET, 1);
}
/* set the lane count on the sink */
tmp = dp_info->dp_lane_count;
if (drm_dp_enhanced_frame_cap(dp_info->dpcd))
tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
- radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);
+ drm_dp_dpcd_writeb(dp_info->aux, DP_LANE_COUNT_SET, tmp);
/* set the link rate on the sink */
tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock);
- radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);
+ drm_dp_dpcd_writeb(dp_info->aux, DP_LINK_BW_SET, tmp);
/* start training on the source */
if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
@@ -711,9 +685,9 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
dp_info->dp_clock, dp_info->enc_id, 0);
/* disable the training pattern on the sink */
- radeon_write_dpcd_reg(dp_info->radeon_connector,
- DP_TRAINING_PATTERN_SET,
- DP_TRAINING_PATTERN_DISABLE);
+ drm_dp_dpcd_writeb(dp_info->aux,
+ DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
return 0;
}
@@ -723,9 +697,9 @@ static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info
udelay(400);
/* disable the training pattern on the sink */
- radeon_write_dpcd_reg(dp_info->radeon_connector,
- DP_TRAINING_PATTERN_SET,
- DP_TRAINING_PATTERN_DISABLE);
+ drm_dp_dpcd_writeb(dp_info->aux,
+ DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
/* disable the training pattern on the source */
if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
@@ -757,7 +731,8 @@ static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info)
while (1) {
drm_dp_link_train_clock_recovery_delay(dp_info->dpcd);
- if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) {
+ if (drm_dp_dpcd_read_link_status(dp_info->aux,
+ dp_info->link_status) <= 0) {
DRM_ERROR("displayport link status failed\n");
break;
}
@@ -819,7 +794,8 @@ static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info)
while (1) {
drm_dp_link_train_channel_eq_delay(dp_info->dpcd);
- if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) {
+ if (drm_dp_dpcd_read_link_status(dp_info->aux,
+ dp_info->link_status) <= 0) {
DRM_ERROR("displayport link status failed\n");
break;
}
@@ -902,7 +878,7 @@ void radeon_dp_link_train(struct drm_encoder *encoder,
else
dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A;
- tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT);
+ drm_dp_dpcd_readb(&dig_connector->dp_i2c_bus->aux, DP_MAX_LANE_COUNT, &tmp);
if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED))
dp_info.tp3_supported = true;
else
@@ -912,9 +888,9 @@ void radeon_dp_link_train(struct drm_encoder *encoder,
dp_info.rdev = rdev;
dp_info.encoder = encoder;
dp_info.connector = connector;
- dp_info.radeon_connector = radeon_connector;
dp_info.dp_lane_count = dig_connector->dp_lane_count;
dp_info.dp_clock = dig_connector->dp_clock;
+ dp_info.aux = &dig_connector->dp_i2c_bus->aux;
if (radeon_dp_link_train_init(&dp_info))
goto done;
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index 607dc14d195e..e6eb5097597f 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -1633,10 +1633,16 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
struct radeon_connector *radeon_connector = NULL;
struct radeon_connector_atom_dig *radeon_dig_connector = NULL;
+ bool travis_quirk = false;
if (connector) {
radeon_connector = to_radeon_connector(connector);
radeon_dig_connector = radeon_connector->con_priv;
+ if ((radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_TRAVIS) &&
+ (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) &&
+ !ASIC_IS_DCE5(rdev))
+ travis_quirk = true;
}
switch (mode) {
@@ -1657,17 +1663,13 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
atombios_external_encoder_setup(encoder, ext_encoder,
EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
}
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
} else if (ASIC_IS_DCE4(rdev)) {
/* setup and enable the encoder */
atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
- /* enable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
} else {
/* setup and enable the encoder and transmitter */
atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
}
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
@@ -1675,68 +1677,56 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
ATOM_TRANSMITTER_ACTION_POWER_ON);
radeon_dig_connector->edp_on = true;
}
+ }
+ /* enable the transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
+ /* DP_SET_POWER_D0 is set in radeon_dp_link_train */
radeon_dp_link_train(encoder, connector);
if (ASIC_IS_DCE4(rdev))
atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
}
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0);
+ atombios_dig_transmitter_setup(encoder,
+ ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0);
+ if (ext_encoder)
+ atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
if (ASIC_IS_DCE4(rdev)) {
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector)
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
+ }
+ if (ext_encoder)
+ atombios_external_encoder_setup(encoder, ext_encoder, ATOM_DISABLE);
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ atombios_dig_transmitter_setup(encoder,
+ ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0);
+
+ if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) &&
+ connector && !travis_quirk)
+ radeon_dp_set_rx_power_state(connector, DP_SET_POWER_D3);
+ if (ASIC_IS_DCE4(rdev)) {
/* disable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ atombios_dig_transmitter_setup(encoder,
+ ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
} else {
/* disable the encoder and transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ atombios_dig_transmitter_setup(encoder,
+ ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 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);
+ if (travis_quirk)
+ radeon_dp_set_rx_power_state(connector, DP_SET_POWER_D3);
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_OFF);
radeon_dig_connector->edp_on = false;
}
}
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0);
- break;
- }
-}
-
-static void
-radeon_atom_encoder_dpms_ext(struct drm_encoder *encoder,
- struct drm_encoder *ext_encoder,
- int mode)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- default:
- if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) {
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT);
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING_OFF);
- } else
- atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) {
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING);
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_DISABLE_OUTPUT);
- } else
- atombios_external_encoder_setup(encoder, ext_encoder, ATOM_DISABLE);
break;
}
}
@@ -1747,7 +1737,6 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n",
radeon_encoder->encoder_id, mode, radeon_encoder->devices,
@@ -1807,9 +1796,6 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
return;
}
- if (ext_encoder)
- radeon_atom_encoder_dpms_ext(encoder, ext_encoder, mode);
-
radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
}
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c
index ea103ccdf4bd..f81d7ca134db 100644
--- a/drivers/gpu/drm/radeon/btc_dpm.c
+++ b/drivers/gpu/drm/radeon/btc_dpm.c
@@ -2601,6 +2601,10 @@ int btc_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = rv7xx_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c
index 8d49104ca6c2..cad89a977527 100644
--- a/drivers/gpu/drm/radeon/ci_dpm.c
+++ b/drivers/gpu/drm/radeon/ci_dpm.c
@@ -172,6 +172,8 @@ extern void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
extern void cik_enter_rlc_safe_mode(struct radeon_device *rdev);
extern void cik_exit_rlc_safe_mode(struct radeon_device *rdev);
extern int ci_mc_load_microcode(struct radeon_device *rdev);
+extern void cik_update_cg(struct radeon_device *rdev,
+ u32 block, bool enable);
static int ci_get_std_voltage_value_sidd(struct radeon_device *rdev,
struct atom_voltage_table_entry *voltage_table,
@@ -746,6 +748,14 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
int i;
+ if (rps->vce_active) {
+ rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
+ rps->ecclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].ecclk;
+ } else {
+ rps->evclk = 0;
+ rps->ecclk = 0;
+ }
+
if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
ci_dpm_vblank_too_short(rdev))
disable_mclk_switching = true;
@@ -804,6 +814,13 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
sclk = ps->performance_levels[0].sclk;
}
+ if (rps->vce_active) {
+ if (sclk < rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].sclk)
+ sclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].sclk;
+ if (mclk < rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].mclk)
+ mclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].mclk;
+ }
+
ps->performance_levels[0].sclk = sclk;
ps->performance_levels[0].mclk = mclk;
@@ -3468,7 +3485,6 @@ static int ci_enable_uvd_dpm(struct radeon_device *rdev, bool enable)
0 : -EINVAL;
}
-#if 0
static int ci_enable_vce_dpm(struct radeon_device *rdev, bool enable)
{
struct ci_power_info *pi = ci_get_pi(rdev);
@@ -3501,6 +3517,7 @@ static int ci_enable_vce_dpm(struct radeon_device *rdev, bool enable)
0 : -EINVAL;
}
+#if 0
static int ci_enable_samu_dpm(struct radeon_device *rdev, bool enable)
{
struct ci_power_info *pi = ci_get_pi(rdev);
@@ -3587,7 +3604,6 @@ static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate)
return ci_enable_uvd_dpm(rdev, !gate);
}
-#if 0
static u8 ci_get_vce_boot_level(struct radeon_device *rdev)
{
u8 i;
@@ -3608,15 +3624,15 @@ static int ci_update_vce_dpm(struct radeon_device *rdev,
struct radeon_ps *radeon_current_state)
{
struct ci_power_info *pi = ci_get_pi(rdev);
- bool new_vce_clock_non_zero = (radeon_new_state->evclk != 0);
- bool old_vce_clock_non_zero = (radeon_current_state->evclk != 0);
int ret = 0;
u32 tmp;
- if (new_vce_clock_non_zero != old_vce_clock_non_zero) {
- if (new_vce_clock_non_zero) {
- pi->smc_state_table.VceBootLevel = ci_get_vce_boot_level(rdev);
+ if (radeon_current_state->evclk != radeon_new_state->evclk) {
+ if (radeon_new_state->evclk) {
+ /* turn the clocks on when encoding */
+ cik_update_cg(rdev, RADEON_CG_BLOCK_VCE, false);
+ pi->smc_state_table.VceBootLevel = ci_get_vce_boot_level(rdev);
tmp = RREG32_SMC(DPM_TABLE_475);
tmp &= ~VceBootLevel_MASK;
tmp |= VceBootLevel(pi->smc_state_table.VceBootLevel);
@@ -3624,12 +3640,16 @@ static int ci_update_vce_dpm(struct radeon_device *rdev,
ret = ci_enable_vce_dpm(rdev, true);
} else {
+ /* turn the clocks off when not encoding */
+ cik_update_cg(rdev, RADEON_CG_BLOCK_VCE, true);
+
ret = ci_enable_vce_dpm(rdev, false);
}
}
return ret;
}
+#if 0
static int ci_update_samu_dpm(struct radeon_device *rdev, bool gate)
{
return ci_enable_samu_dpm(rdev, gate);
@@ -4752,13 +4772,13 @@ int ci_dpm_set_power_state(struct radeon_device *rdev)
DRM_ERROR("ci_generate_dpm_level_enable_mask failed\n");
return ret;
}
-#if 0
+
ret = ci_update_vce_dpm(rdev, new_ps, old_ps);
if (ret) {
DRM_ERROR("ci_update_vce_dpm failed\n");
return ret;
}
-#endif
+
ret = ci_update_sclk_t(rdev);
if (ret) {
DRM_ERROR("ci_update_sclk_t failed\n");
@@ -4959,9 +4979,6 @@ static int ci_parse_power_table(struct radeon_device *rdev)
if (!rdev->pm.dpm.ps)
return -ENOMEM;
power_state_offset = (u8 *)state_array->states;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < state_array->ucNumEntries; i++) {
u8 *idx;
power_state = (union pplib_power_state *)power_state_offset;
@@ -4998,6 +5015,21 @@ static int ci_parse_power_table(struct radeon_device *rdev)
power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
}
rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+
+ /* fill in the vce power states */
+ for (i = 0; i < RADEON_MAX_VCE_LEVELS; i++) {
+ u32 sclk, mclk;
+ clock_array_index = rdev->pm.dpm.vce_states[i].clk_idx;
+ clock_info = (union pplib_clock_info *)
+ &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+ sclk = le16_to_cpu(clock_info->ci.usEngineClockLow);
+ sclk |= clock_info->ci.ucEngineClockHigh << 16;
+ mclk = le16_to_cpu(clock_info->ci.usMemoryClockLow);
+ mclk |= clock_info->ci.ucMemoryClockHigh << 16;
+ rdev->pm.dpm.vce_states[i].sclk = sclk;
+ rdev->pm.dpm.vce_states[i].mclk = mclk;
+ }
+
return 0;
}
@@ -5077,17 +5109,25 @@ int ci_dpm_init(struct radeon_device *rdev)
ci_dpm_fini(rdev);
return ret;
}
- ret = ci_parse_power_table(rdev);
+
+ ret = r600_get_platform_caps(rdev);
if (ret) {
ci_dpm_fini(rdev);
return ret;
}
+
ret = r600_parse_extended_power_table(rdev);
if (ret) {
ci_dpm_fini(rdev);
return ret;
}
+ ret = ci_parse_power_table(rdev);
+ if (ret) {
+ ci_dpm_fini(rdev);
+ return ret;
+ }
+
pi->dll_default_on = false;
pi->sram_end = SMC_RAM_END;
@@ -5120,6 +5160,7 @@ int ci_dpm_init(struct radeon_device *rdev)
pi->caps_sclk_throttle_low_notification = false;
pi->caps_uvd_dpm = true;
+ pi->caps_vce_dpm = true;
ci_get_leakage_voltages(rdev);
ci_patch_dependency_tables_with_leakage(rdev);
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index bbb17841a9e5..745143c2358f 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -75,6 +75,7 @@ extern void si_init_uvd_internal_cg(struct radeon_device *rdev);
extern int cik_sdma_resume(struct radeon_device *rdev);
extern void cik_sdma_enable(struct radeon_device *rdev, bool enable);
extern void cik_sdma_fini(struct radeon_device *rdev);
+extern void vce_v2_0_enable_mgcg(struct radeon_device *rdev, bool enable);
static void cik_rlc_stop(struct radeon_device *rdev);
static void cik_pcie_gen3_enable(struct radeon_device *rdev);
static void cik_program_aspm(struct radeon_device *rdev);
@@ -1095,7 +1096,7 @@ static const u32 spectre_golden_registers[] =
0x8a14, 0xf000003f, 0x00000007,
0x8b24, 0xffffffff, 0x00ffffff,
0x28350, 0x3f3f3fff, 0x00000082,
- 0x28355, 0x0000003f, 0x00000000,
+ 0x28354, 0x0000003f, 0x00000000,
0x3e78, 0x00000001, 0x00000002,
0x913c, 0xffff03df, 0x00000004,
0xc768, 0x00000008, 0x00000008,
@@ -2028,6 +2029,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 5:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING));
break;
case 6:
@@ -2048,6 +2050,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 9:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING));
break;
case 10:
@@ -2070,6 +2073,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 13:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING));
break;
case 14:
@@ -2092,6 +2096,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 27:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P16_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING));
break;
case 28:
@@ -2246,6 +2251,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 5:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING));
break;
case 6:
@@ -2266,6 +2272,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 9:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING));
break;
case 10:
@@ -2288,6 +2295,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 13:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING));
break;
case 14:
@@ -2310,6 +2318,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 27:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING));
break;
case 28:
@@ -2466,6 +2475,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 5:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING));
break;
case 6:
@@ -2486,6 +2496,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 9:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING));
break;
case 10:
@@ -2508,6 +2519,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 13:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING));
break;
case 14:
@@ -2530,6 +2542,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 27:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_16x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING));
break;
case 28:
@@ -2592,6 +2605,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 5:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING));
break;
case 6:
@@ -2612,6 +2626,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 9:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING));
break;
case 10:
@@ -2634,6 +2649,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 13:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING));
break;
case 14:
@@ -2656,6 +2672,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 27:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING));
break;
case 28:
@@ -2812,6 +2829,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 5:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P2) |
MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING));
break;
case 6:
@@ -2827,11 +2845,13 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
TILE_SPLIT(split_equal_to_row_size));
break;
case 8:
- gb_tile_moden = ARRAY_MODE(ARRAY_LINEAR_ALIGNED);
+ gb_tile_moden = ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+ PIPE_CONFIG(ADDR_SURF_P2);
break;
case 9:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
- MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING));
+ MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P2));
break;
case 10:
gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
@@ -2853,6 +2873,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 13:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ PIPE_CONFIG(ADDR_SURF_P2) |
MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING));
break;
case 14:
@@ -2875,7 +2896,8 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
break;
case 27:
gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
- MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING));
+ MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P2));
break;
case 28:
gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) |
@@ -4030,8 +4052,6 @@ static int cik_cp_gfx_resume(struct radeon_device *rdev)
WREG32(CP_RB0_BASE, rb_addr);
WREG32(CP_RB0_BASE_HI, upper_32_bits(rb_addr));
- ring->rptr = RREG32(CP_RB0_RPTR);
-
/* start the ring */
cik_cp_gfx_start(rdev);
rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true;
@@ -4589,8 +4609,7 @@ static int cik_cp_compute_resume(struct radeon_device *rdev)
rdev->ring[idx].wptr = 0;
mqd->queue_state.cp_hqd_pq_wptr = rdev->ring[idx].wptr;
WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr);
- rdev->ring[idx].rptr = RREG32(CP_HQD_PQ_RPTR);
- mqd->queue_state.cp_hqd_pq_rptr = rdev->ring[idx].rptr;
+ mqd->queue_state.cp_hqd_pq_rptr = RREG32(CP_HQD_PQ_RPTR);
/* set the vmid for the queue */
mqd->queue_state.cp_hqd_vmid = 0;
@@ -5120,11 +5139,9 @@ bool cik_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
if (!(reset_mask & (RADEON_RESET_GFX |
RADEON_RESET_COMPUTE |
RADEON_RESET_CP))) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force CP activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
@@ -6144,6 +6161,10 @@ void cik_update_cg(struct radeon_device *rdev,
cik_enable_hdp_mgcg(rdev, enable);
cik_enable_hdp_ls(rdev, enable);
}
+
+ if (block & RADEON_CG_BLOCK_VCE) {
+ vce_v2_0_enable_mgcg(rdev, enable);
+ }
}
static void cik_init_cg(struct radeon_device *rdev)
@@ -6521,8 +6542,8 @@ void cik_get_csb_buffer(struct radeon_device *rdev, volatile u32 *buffer)
buffer[count++] = cpu_to_le32(0x00000000);
break;
case CHIP_HAWAII:
- buffer[count++] = 0x3a00161a;
- buffer[count++] = 0x0000002e;
+ buffer[count++] = cpu_to_le32(0x3a00161a);
+ buffer[count++] = cpu_to_le32(0x0000002e);
break;
default:
buffer[count++] = cpu_to_le32(0x00000000);
@@ -7493,6 +7514,20 @@ restart_ih:
/* reset addr and status */
WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1);
break;
+ case 167: /* VCE */
+ DRM_DEBUG("IH: VCE int: 0x%08x\n", src_data);
+ switch (src_data) {
+ case 0:
+ radeon_fence_process(rdev, TN_RING_TYPE_VCE1_INDEX);
+ break;
+ case 1:
+ radeon_fence_process(rdev, TN_RING_TYPE_VCE2_INDEX);
+ break;
+ default:
+ DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
case 176: /* GFX RB CP_INT */
case 177: /* GFX IB CP_INT */
radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX);
@@ -7792,6 +7827,22 @@ static int cik_startup(struct radeon_device *rdev)
if (r)
rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0;
+ r = radeon_vce_resume(rdev);
+ if (!r) {
+ r = vce_v2_0_resume(rdev);
+ if (!r)
+ r = radeon_fence_driver_start_ring(rdev,
+ TN_RING_TYPE_VCE1_INDEX);
+ if (!r)
+ r = radeon_fence_driver_start_ring(rdev,
+ TN_RING_TYPE_VCE2_INDEX);
+ }
+ if (r) {
+ dev_err(rdev->dev, "VCE init error (%d).\n", r);
+ rdev->ring[TN_RING_TYPE_VCE1_INDEX].ring_size = 0;
+ rdev->ring[TN_RING_TYPE_VCE2_INDEX].ring_size = 0;
+ }
+
/* Enable IRQ */
if (!rdev->irq.installed) {
r = radeon_irq_kms_init(rdev);
@@ -7867,6 +7918,23 @@ static int cik_startup(struct radeon_device *rdev)
DRM_ERROR("radeon: failed initializing UVD (%d).\n", r);
}
+ r = -ENOENT;
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
+ if (ring->ring_size)
+ r = radeon_ring_init(rdev, ring, ring->ring_size, 0,
+ VCE_CMD_NO_OP);
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
+ if (ring->ring_size)
+ r = radeon_ring_init(rdev, ring, ring->ring_size, 0,
+ VCE_CMD_NO_OP);
+
+ if (!r)
+ r = vce_v1_0_init(rdev);
+ else if (r != -ENOENT)
+ DRM_ERROR("radeon: failed initializing VCE (%d).\n", r);
+
r = radeon_ib_pool_init(rdev);
if (r) {
dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
@@ -7938,6 +8006,7 @@ int cik_suspend(struct radeon_device *rdev)
cik_sdma_enable(rdev, false);
uvd_v1_0_fini(rdev);
radeon_uvd_suspend(rdev);
+ radeon_vce_suspend(rdev);
cik_fini_pg(rdev);
cik_fini_cg(rdev);
cik_irq_suspend(rdev);
@@ -8070,6 +8139,17 @@ int cik_init(struct radeon_device *rdev)
r600_ring_init(rdev, ring, 4096);
}
+ r = radeon_vce_init(rdev);
+ if (!r) {
+ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
+ ring->ring_obj = NULL;
+ r600_ring_init(rdev, ring, 4096);
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
+ ring->ring_obj = NULL;
+ r600_ring_init(rdev, ring, 4096);
+ }
+
rdev->ih.ring_obj = NULL;
r600_ih_ring_init(rdev, 64 * 1024);
@@ -8131,6 +8211,7 @@ void cik_fini(struct radeon_device *rdev)
radeon_irq_kms_fini(rdev);
uvd_v1_0_fini(rdev);
radeon_uvd_fini(rdev);
+ radeon_vce_fini(rdev);
cik_pcie_gart_fini(rdev);
r600_vram_scratch_fini(rdev);
radeon_gem_fini(rdev);
@@ -8869,6 +8950,41 @@ int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
return r;
}
+int cik_set_vce_clocks(struct radeon_device *rdev, u32 evclk, u32 ecclk)
+{
+ int r, i;
+ struct atom_clock_dividers dividers;
+ u32 tmp;
+
+ r = radeon_atom_get_clock_dividers(rdev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+ ecclk, false, &dividers);
+ if (r)
+ return r;
+
+ for (i = 0; i < 100; i++) {
+ if (RREG32_SMC(CG_ECLK_STATUS) & ECLK_STATUS)
+ break;
+ mdelay(10);
+ }
+ if (i == 100)
+ return -ETIMEDOUT;
+
+ tmp = RREG32_SMC(CG_ECLK_CNTL);
+ tmp &= ~(ECLK_DIR_CNTL_EN|ECLK_DIVIDER_MASK);
+ tmp |= dividers.post_divider;
+ WREG32_SMC(CG_ECLK_CNTL, tmp);
+
+ for (i = 0; i < 100; i++) {
+ if (RREG32_SMC(CG_ECLK_STATUS) & ECLK_STATUS)
+ break;
+ mdelay(10);
+ }
+ if (i == 100)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
static void cik_pcie_gen3_enable(struct radeon_device *rdev)
{
struct pci_dev *root = rdev->pdev->bus->self;
diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c
index 94626ea90fa5..89b4afa5041c 100644
--- a/drivers/gpu/drm/radeon/cik_sdma.c
+++ b/drivers/gpu/drm/radeon/cik_sdma.c
@@ -369,8 +369,6 @@ static int cik_sdma_gfx_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(SDMA0_GFX_RB_WPTR + reg_offset, ring->wptr << 2);
- ring->rptr = RREG32(SDMA0_GFX_RB_RPTR + reg_offset) >> 2;
-
/* enable DMA RB */
WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl | SDMA_RB_ENABLE);
@@ -713,11 +711,9 @@ bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
mask = RADEON_RESET_DMA1;
if (!(reset_mask & mask)) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force ring activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h
index 98bae9d7b74d..213873270d5f 100644
--- a/drivers/gpu/drm/radeon/cikd.h
+++ b/drivers/gpu/drm/radeon/cikd.h
@@ -203,6 +203,12 @@
#define CTF_TEMP_MASK 0x0003fe00
#define CTF_TEMP_SHIFT 9
+#define CG_ECLK_CNTL 0xC05000AC
+# define ECLK_DIVIDER_MASK 0x7f
+# define ECLK_DIR_CNTL_EN (1 << 8)
+#define CG_ECLK_STATUS 0xC05000B0
+# define ECLK_STATUS (1 << 0)
+
#define CG_SPLL_FUNC_CNTL 0xC0500140
#define SPLL_RESET (1 << 0)
#define SPLL_PWRON (1 << 1)
@@ -2010,4 +2016,47 @@
/* UVD CTX indirect */
#define UVD_CGC_MEM_CTRL 0xC0
+/* VCE */
+
+#define VCE_VCPU_CACHE_OFFSET0 0x20024
+#define VCE_VCPU_CACHE_SIZE0 0x20028
+#define VCE_VCPU_CACHE_OFFSET1 0x2002c
+#define VCE_VCPU_CACHE_SIZE1 0x20030
+#define VCE_VCPU_CACHE_OFFSET2 0x20034
+#define VCE_VCPU_CACHE_SIZE2 0x20038
+#define VCE_RB_RPTR2 0x20178
+#define VCE_RB_WPTR2 0x2017c
+#define VCE_RB_RPTR 0x2018c
+#define VCE_RB_WPTR 0x20190
+#define VCE_CLOCK_GATING_A 0x202f8
+# define CGC_CLK_GATE_DLY_TIMER_MASK (0xf << 0)
+# define CGC_CLK_GATE_DLY_TIMER(x) ((x) << 0)
+# define CGC_CLK_GATER_OFF_DLY_TIMER_MASK (0xff << 4)
+# define CGC_CLK_GATER_OFF_DLY_TIMER(x) ((x) << 4)
+# define CGC_UENC_WAIT_AWAKE (1 << 18)
+#define VCE_CLOCK_GATING_B 0x202fc
+#define VCE_CGTT_CLK_OVERRIDE 0x207a0
+#define VCE_UENC_CLOCK_GATING 0x207bc
+# define CLOCK_ON_DELAY_MASK (0xf << 0)
+# define CLOCK_ON_DELAY(x) ((x) << 0)
+# define CLOCK_OFF_DELAY_MASK (0xff << 4)
+# define CLOCK_OFF_DELAY(x) ((x) << 4)
+#define VCE_UENC_REG_CLOCK_GATING 0x207c0
+#define VCE_SYS_INT_EN 0x21300
+# define VCE_SYS_INT_TRAP_INTERRUPT_EN (1 << 3)
+#define VCE_LMI_CTRL2 0x21474
+#define VCE_LMI_CTRL 0x21498
+#define VCE_LMI_VM_CTRL 0x214a0
+#define VCE_LMI_SWAP_CNTL 0x214b4
+#define VCE_LMI_SWAP_CNTL1 0x214b8
+#define VCE_LMI_CACHE_CTRL 0x214f4
+
+#define VCE_CMD_NO_OP 0x00000000
+#define VCE_CMD_END 0x00000001
+#define VCE_CMD_IB 0x00000002
+#define VCE_CMD_FENCE 0x00000003
+#define VCE_CMD_TRAP 0x00000004
+#define VCE_CMD_IB_AUTO 0x00000005
+#define VCE_CMD_SEMAPHORE 0x00000006
+
#endif
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c
index cf783fc0ef21..5a9a5f4d7888 100644
--- a/drivers/gpu/drm/radeon/cypress_dpm.c
+++ b/drivers/gpu/drm/radeon/cypress_dpm.c
@@ -2036,6 +2036,10 @@ int cypress_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = rv7xx_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 27b0ff16082e..b406546440da 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -2990,8 +2990,6 @@ static int evergreen_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB_BASE, ring->gpu_addr >> 8);
WREG32(CP_DEBUG, (1 << 27) | (1 << 28));
- ring->rptr = RREG32(CP_RB_RPTR);
-
evergreen_cp_start(rdev);
ring->ready = true;
r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring);
@@ -3952,11 +3950,9 @@ bool evergreen_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *rin
if (!(reset_mask & (RADEON_RESET_GFX |
RADEON_RESET_COMPUTE |
RADEON_RESET_CP))) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force CP activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index c7cac07f139b..5c8b358f9fba 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -1165,7 +1165,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case DB_DEPTH_CONTROL:
track->db_depth_control = radeon_get_ib_value(p, idx);
@@ -1196,12 +1196,12 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
ib[idx] &= ~Z_ARRAY_MODE(0xf);
track->db_z_info &= ~Z_ARRAY_MODE(0xf);
- ib[idx] |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
- track->db_z_info |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ ib[idx] |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
+ track->db_z_info |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
+ if (reloc->tiling_flags & RADEON_TILING_MACRO) {
unsigned bankw, bankh, mtaspect, tile_split;
- evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ evergreen_tiling_fields(reloc->tiling_flags,
&bankw, &bankh, &mtaspect,
&tile_split);
ib[idx] |= DB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
@@ -1237,7 +1237,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->db_z_read_offset = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->db_z_read_bo = reloc->robj;
track->db_dirty = true;
break;
@@ -1249,7 +1249,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->db_z_write_offset = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->db_z_write_bo = reloc->robj;
track->db_dirty = true;
break;
@@ -1261,7 +1261,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->db_s_read_offset = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->db_s_read_bo = reloc->robj;
track->db_dirty = true;
break;
@@ -1273,7 +1273,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->db_s_write_offset = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->db_s_write_bo = reloc->robj;
track->db_dirty = true;
break;
@@ -1297,7 +1297,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16;
track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->vgt_strmout_bo[tmp] = reloc->robj;
track->streamout_dirty = true;
break;
@@ -1317,7 +1317,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
case CB_TARGET_MASK:
track->cb_target_mask = radeon_get_ib_value(p, idx);
track->cb_dirty = true;
@@ -1381,8 +1381,8 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
- track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
+ ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
+ track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
}
track->cb_dirty = true;
break;
@@ -1399,8 +1399,8 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
- track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
+ ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
+ track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
}
track->cb_dirty = true;
break;
@@ -1461,10 +1461,10 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ if (reloc->tiling_flags & RADEON_TILING_MACRO) {
unsigned bankw, bankh, mtaspect, tile_split;
- evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ evergreen_tiling_fields(reloc->tiling_flags,
&bankw, &bankh, &mtaspect,
&tile_split);
ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
@@ -1489,10 +1489,10 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ if (reloc->tiling_flags & RADEON_TILING_MACRO) {
unsigned bankw, bankh, mtaspect, tile_split;
- evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ evergreen_tiling_fields(reloc->tiling_flags,
&bankw, &bankh, &mtaspect,
&tile_split);
ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
@@ -1520,7 +1520,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->cb_color_fmask_bo[tmp] = reloc->robj;
break;
case CB_COLOR0_CMASK:
@@ -1537,7 +1537,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->cb_color_cmask_bo[tmp] = reloc->robj;
break;
case CB_COLOR0_FMASK_SLICE:
@@ -1578,7 +1578,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = (reg - CB_COLOR0_BASE) / 0x3c;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->cb_color_bo[tmp] = reloc->robj;
track->cb_dirty = true;
break;
@@ -1594,7 +1594,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = ((reg - CB_COLOR8_BASE) / 0x1c) + 8;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->cb_color_bo[tmp] = reloc->robj;
track->cb_dirty = true;
break;
@@ -1606,7 +1606,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->htile_offset = radeon_get_ib_value(p, idx);
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->htile_bo = reloc->robj;
track->db_dirty = true;
break;
@@ -1723,7 +1723,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case SX_MEMORY_EXPORT_BASE:
if (p->rdev->family >= CHIP_CAYMAN) {
@@ -1737,7 +1737,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case CAYMAN_SX_SCATTER_EXPORT_BASE:
if (p->rdev->family < CHIP_CAYMAN) {
@@ -1751,7 +1751,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case SX_MISC:
track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0;
@@ -1836,7 +1836,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(idx_value & 0xfffffff0) +
((u64)(tmp & 0xff) << 32);
@@ -1882,7 +1882,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
idx_value +
((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
@@ -1909,7 +1909,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
idx_value +
((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
@@ -1937,7 +1937,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
radeon_get_ib_value(p, idx+1) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -2027,7 +2027,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DISPATCH_INDIRECT\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
+ ib[idx+0] = idx_value + (u32)(reloc->gpu_offset & 0xffffffff);
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
@@ -2049,7 +2049,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -2106,7 +2106,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
tmp = radeon_get_ib_value(p, idx) +
((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
- offset = reloc->lobj.gpu_offset + tmp;
+ offset = reloc->gpu_offset + tmp;
if ((tmp + size) > radeon_bo_size(reloc->robj)) {
dev_warn(p->dev, "CP DMA src buffer too small (%llu %lu)\n",
@@ -2144,7 +2144,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
tmp = radeon_get_ib_value(p, idx+2) +
((u64)(radeon_get_ib_value(p, idx+3) & 0xff) << 32);
- offset = reloc->lobj.gpu_offset + tmp;
+ offset = reloc->gpu_offset + tmp;
if ((tmp + size) > radeon_bo_size(reloc->robj)) {
dev_warn(p->dev, "CP DMA dst buffer too small (%llu %lu)\n",
@@ -2174,7 +2174,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SURFACE_SYNC\n");
return -EINVAL;
}
- ib[idx+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx+2] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
}
break;
case PACKET3_EVENT_WRITE:
@@ -2190,7 +2190,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -2212,7 +2212,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -2234,7 +2234,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -2302,11 +2302,11 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
ib[idx+1+(i*8)+1] |=
- TEX_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ TEX_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
+ if (reloc->tiling_flags & RADEON_TILING_MACRO) {
unsigned bankw, bankh, mtaspect, tile_split;
- evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ evergreen_tiling_fields(reloc->tiling_flags,
&bankw, &bankh, &mtaspect,
&tile_split);
ib[idx+1+(i*8)+6] |= TEX_TILE_SPLIT(tile_split);
@@ -2318,7 +2318,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
}
texture = reloc->robj;
- toffset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ toffset = (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
/* tex mip base */
tex_dim = ib[idx+1+(i*8)+0] & 0x7;
@@ -2337,7 +2337,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE (tex)\n");
return -EINVAL;
}
- moffset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ moffset = (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
mipmap = reloc->robj;
}
@@ -2364,7 +2364,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj) - offset;
}
- offset64 = reloc->lobj.gpu_offset + offset;
+ offset64 = reloc->gpu_offset + offset;
ib[idx+1+(i*8)+0] = offset64;
ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
(upper_32_bits(offset64) & 0xff);
@@ -2445,7 +2445,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+1] = offset;
ib[idx+2] = upper_32_bits(offset) & 0xff;
}
@@ -2464,7 +2464,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+3] = offset;
ib[idx+4] = upper_32_bits(offset) & 0xff;
}
@@ -2493,7 +2493,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 8, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+0] = offset;
ib[idx+1] = upper_32_bits(offset) & 0xff;
break;
@@ -2518,7 +2518,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+1] = offset;
ib[idx+2] = upper_32_bits(offset) & 0xff;
} else {
@@ -2542,7 +2542,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+3] = offset;
ib[idx+4] = upper_32_bits(offset) & 0xff;
} else {
@@ -2717,7 +2717,7 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset <<= 8;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
p->idx += count + 7;
break;
/* linear */
@@ -2725,8 +2725,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+2] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
p->idx += count + 3;
break;
default:
@@ -2768,10 +2768,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+2] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+3] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
+ ib[idx+4] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 5;
break;
/* Copy L2T/T2L */
@@ -2781,22 +2781,22 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
/* tiled src, linear dst */
src_offset = radeon_get_ib_value(p, idx+1);
src_offset <<= 8;
- ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(src_reloc->gpu_offset >> 8);
dst_offset = radeon_get_ib_value(p, idx + 7);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32;
- ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+7] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+8] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
} else {
/* linear src, tiled dst */
src_offset = radeon_get_ib_value(p, idx+7);
src_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32;
- ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+7] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+8] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset <<= 8;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
}
if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) {
dev_warn(p->dev, "DMA L2T, src buffer too small (%llu %lu)\n",
@@ -2827,10 +2827,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset + count, radeon_bo_size(dst_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xffffffff);
+ ib[idx+2] += (u32)(src_reloc->gpu_offset & 0xffffffff);
+ ib[idx+3] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
+ ib[idx+4] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 5;
break;
/* Copy L2L, partial */
@@ -2840,10 +2840,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
DRM_ERROR("L2L Partial is cayman only !\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+4] += (u32)(dst_reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+5] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(src_reloc->gpu_offset & 0xffffffff);
+ ib[idx+2] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
+ ib[idx+4] += (u32)(dst_reloc->gpu_offset & 0xffffffff);
+ ib[idx+5] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
p->idx += 9;
break;
@@ -2876,12 +2876,12 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+3] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+4] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+5] += upper_32_bits(dst2_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+2] += (u32)(dst2_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+3] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+4] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
+ ib[idx+5] += upper_32_bits(dst2_reloc->gpu_offset) & 0xff;
+ ib[idx+6] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 7;
break;
/* Copy L2T Frame to Field */
@@ -2916,10 +2916,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
- ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8);
- ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
+ ib[idx+2] += (u32)(dst2_reloc->gpu_offset >> 8);
+ ib[idx+8] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+9] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 10;
break;
/* Copy L2T/T2L, partial */
@@ -2932,16 +2932,16 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
/* detile bit */
if (radeon_get_ib_value(p, idx + 2) & (1 << 31)) {
/* tiled src, linear dst */
- ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(src_reloc->gpu_offset >> 8);
- ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+7] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+8] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
} else {
/* linear src, tiled dst */
- ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+7] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+8] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
}
p->idx += 12;
break;
@@ -2978,10 +2978,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
- ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8);
- ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
+ ib[idx+2] += (u32)(dst2_reloc->gpu_offset >> 8);
+ ib[idx+8] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+9] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 10;
break;
/* Copy L2T/T2L (tile units) */
@@ -2992,22 +2992,22 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
/* tiled src, linear dst */
src_offset = radeon_get_ib_value(p, idx+1);
src_offset <<= 8;
- ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(src_reloc->gpu_offset >> 8);
dst_offset = radeon_get_ib_value(p, idx+7);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32;
- ib[idx+7] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+8] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+7] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+8] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
} else {
/* linear src, tiled dst */
src_offset = radeon_get_ib_value(p, idx+7);
src_offset |= ((u64)(radeon_get_ib_value(p, idx+8) & 0xff)) << 32;
- ib[idx+7] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+8] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+7] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+8] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset <<= 8;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
}
if ((src_offset + (count * 4)) > radeon_bo_size(src_reloc->robj)) {
dev_warn(p->dev, "DMA L2T, T2L src buffer too small (%llu %lu)\n",
@@ -3028,8 +3028,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
DRM_ERROR("L2T, T2L Partial is cayman only !\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8);
- ib[idx+4] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(src_reloc->gpu_offset >> 8);
+ ib[idx+4] += (u32)(dst_reloc->gpu_offset >> 8);
p->idx += 13;
break;
/* Copy L2T broadcast (tile units) */
@@ -3065,10 +3065,10 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst2_offset + (count * 4), radeon_bo_size(dst2_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
- ib[idx+2] += (u32)(dst2_reloc->lobj.gpu_offset >> 8);
- ib[idx+8] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+9] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
+ ib[idx+2] += (u32)(dst2_reloc->gpu_offset >> 8);
+ ib[idx+8] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+9] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 10;
break;
default:
@@ -3089,8 +3089,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset, radeon_bo_size(dst_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) << 16) & 0x00ff0000;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+3] += (upper_32_bits(dst_reloc->gpu_offset) << 16) & 0x00ff0000;
p->idx += 4;
break;
case DMA_PACKET_NOP:
diff --git a/drivers/gpu/drm/radeon/evergreen_dma.c b/drivers/gpu/drm/radeon/evergreen_dma.c
index a37b54436382..287fe966d7de 100644
--- a/drivers/gpu/drm/radeon/evergreen_dma.c
+++ b/drivers/gpu/drm/radeon/evergreen_dma.c
@@ -174,11 +174,9 @@ bool evergreen_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *rin
u32 reset_mask = evergreen_gpu_check_soft_reset(rdev);
if (!(reset_mask & RADEON_RESET_DMA)) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force ring activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c
index 351db361239d..16ec9d56a234 100644
--- a/drivers/gpu/drm/radeon/kv_dpm.c
+++ b/drivers/gpu/drm/radeon/kv_dpm.c
@@ -1338,13 +1338,11 @@ static int kv_enable_uvd_dpm(struct radeon_device *rdev, bool enable)
PPSMC_MSG_UVDDPM_Enable : PPSMC_MSG_UVDDPM_Disable);
}
-#if 0
static int kv_enable_vce_dpm(struct radeon_device *rdev, bool enable)
{
return kv_notify_message_to_smu(rdev, enable ?
PPSMC_MSG_VCEDPM_Enable : PPSMC_MSG_VCEDPM_Disable);
}
-#endif
static int kv_enable_samu_dpm(struct radeon_device *rdev, bool enable)
{
@@ -1389,7 +1387,6 @@ static int kv_update_uvd_dpm(struct radeon_device *rdev, bool gate)
return kv_enable_uvd_dpm(rdev, !gate);
}
-#if 0
static u8 kv_get_vce_boot_level(struct radeon_device *rdev)
{
u8 i;
@@ -1414,6 +1411,9 @@ static int kv_update_vce_dpm(struct radeon_device *rdev,
int ret;
if (radeon_new_state->evclk > 0 && radeon_current_state->evclk == 0) {
+ kv_dpm_powergate_vce(rdev, false);
+ /* turn the clocks on when encoding */
+ cik_update_cg(rdev, RADEON_CG_BLOCK_VCE, false);
if (pi->caps_stable_p_state)
pi->vce_boot_level = table->count - 1;
else
@@ -1436,11 +1436,13 @@ static int kv_update_vce_dpm(struct radeon_device *rdev,
kv_enable_vce_dpm(rdev, true);
} else if (radeon_new_state->evclk == 0 && radeon_current_state->evclk > 0) {
kv_enable_vce_dpm(rdev, false);
+ /* turn the clocks off when not encoding */
+ cik_update_cg(rdev, RADEON_CG_BLOCK_VCE, true);
+ kv_dpm_powergate_vce(rdev, true);
}
return 0;
}
-#endif
static int kv_update_samu_dpm(struct radeon_device *rdev, bool gate)
{
@@ -1575,11 +1577,16 @@ static void kv_dpm_powergate_vce(struct radeon_device *rdev, bool gate)
pi->vce_power_gated = gate;
if (gate) {
- if (pi->caps_vce_pg)
+ if (pi->caps_vce_pg) {
+ /* XXX do we need a vce_v1_0_stop() ? */
kv_notify_message_to_smu(rdev, PPSMC_MSG_VCEPowerOFF);
+ }
} else {
- if (pi->caps_vce_pg)
+ if (pi->caps_vce_pg) {
kv_notify_message_to_smu(rdev, PPSMC_MSG_VCEPowerON);
+ vce_v2_0_resume(rdev);
+ vce_v1_0_start(rdev);
+ }
}
}
@@ -1768,7 +1775,7 @@ int kv_dpm_set_power_state(struct radeon_device *rdev)
{
struct kv_power_info *pi = kv_get_pi(rdev);
struct radeon_ps *new_ps = &pi->requested_rps;
- /*struct radeon_ps *old_ps = &pi->current_rps;*/
+ struct radeon_ps *old_ps = &pi->current_rps;
int ret;
if (pi->bapm_enable) {
@@ -1798,13 +1805,12 @@ int kv_dpm_set_power_state(struct radeon_device *rdev)
kv_set_enabled_levels(rdev);
kv_force_lowest_valid(rdev);
kv_unforce_levels(rdev);
-#if 0
+
ret = kv_update_vce_dpm(rdev, new_ps, old_ps);
if (ret) {
DRM_ERROR("kv_update_vce_dpm failed\n");
return ret;
}
-#endif
kv_update_sclk_t(rdev);
}
} else {
@@ -1823,13 +1829,11 @@ int kv_dpm_set_power_state(struct radeon_device *rdev)
kv_program_nbps_index_settings(rdev, new_ps);
kv_freeze_sclk_dpm(rdev, false);
kv_set_enabled_levels(rdev);
-#if 0
ret = kv_update_vce_dpm(rdev, new_ps, old_ps);
if (ret) {
DRM_ERROR("kv_update_vce_dpm failed\n");
return ret;
}
-#endif
kv_update_acp_boot_level(rdev);
kv_update_sclk_t(rdev);
kv_enable_nb_dpm(rdev);
@@ -2037,6 +2041,14 @@ static void kv_apply_state_adjust_rules(struct radeon_device *rdev,
struct radeon_clock_and_voltage_limits *max_limits =
&rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+ if (new_rps->vce_active) {
+ new_rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
+ new_rps->ecclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].ecclk;
+ } else {
+ new_rps->evclk = 0;
+ new_rps->ecclk = 0;
+ }
+
mclk = max_limits->mclk;
sclk = min_sclk;
@@ -2056,6 +2068,11 @@ static void kv_apply_state_adjust_rules(struct radeon_device *rdev,
sclk = stable_p_state_sclk;
}
+ if (new_rps->vce_active) {
+ if (sclk < rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].sclk)
+ sclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].sclk;
+ }
+
ps->need_dfs_bypass = true;
for (i = 0; i < ps->num_levels; i++) {
@@ -2092,7 +2109,8 @@ static void kv_apply_state_adjust_rules(struct radeon_device *rdev,
}
}
- pi->video_start = new_rps->dclk || new_rps->vclk;
+ pi->video_start = new_rps->dclk || new_rps->vclk ||
+ new_rps->evclk || new_rps->ecclk;
if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
@@ -2538,9 +2556,6 @@ static int kv_parse_power_table(struct radeon_device *rdev)
if (!rdev->pm.dpm.ps)
return -ENOMEM;
power_state_offset = (u8 *)state_array->states;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < state_array->ucNumEntries; i++) {
u8 *idx;
power_state = (union pplib_power_state *)power_state_offset;
@@ -2577,6 +2592,19 @@ static int kv_parse_power_table(struct radeon_device *rdev)
power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
}
rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+
+ /* fill in the vce power states */
+ for (i = 0; i < RADEON_MAX_VCE_LEVELS; i++) {
+ u32 sclk;
+ clock_array_index = rdev->pm.dpm.vce_states[i].clk_idx;
+ clock_info = (union pplib_clock_info *)
+ &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+ sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
+ sclk |= clock_info->sumo.ucEngineClockHigh << 16;
+ rdev->pm.dpm.vce_states[i].sclk = sclk;
+ rdev->pm.dpm.vce_states[i].mclk = 0;
+ }
+
return 0;
}
@@ -2590,6 +2618,10 @@ int kv_dpm_init(struct radeon_device *rdev)
return -ENOMEM;
rdev->pm.dpm.priv = pi;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = r600_parse_extended_power_table(rdev);
if (ret)
return ret;
@@ -2623,7 +2655,7 @@ int kv_dpm_init(struct radeon_device *rdev)
pi->caps_fps = false; /* true? */
pi->caps_uvd_pg = true;
pi->caps_uvd_dpm = true;
- pi->caps_vce_pg = false;
+ pi->caps_vce_pg = false; /* XXX true */
pi->caps_samu_pg = false;
pi->caps_acp_pg = false;
pi->caps_stable_p_state = false;
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index bf6300cfd62d..d246e043421a 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1642,8 +1642,8 @@ static int cayman_cp_resume(struct radeon_device *rdev)
ring = &rdev->ring[ridx[i]];
WREG32_P(cp_rb_cntl[i], RB_RPTR_WR_ENA, ~RB_RPTR_WR_ENA);
- ring->rptr = ring->wptr = 0;
- WREG32(cp_rb_rptr[i], ring->rptr);
+ ring->wptr = 0;
+ WREG32(cp_rb_rptr[i], 0);
WREG32(cp_rb_wptr[i], ring->wptr);
mdelay(1);
@@ -1917,11 +1917,9 @@ bool cayman_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
if (!(reset_mask & (RADEON_RESET_GFX |
RADEON_RESET_COMPUTE |
RADEON_RESET_CP))) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force CP activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/ni_dma.c b/drivers/gpu/drm/radeon/ni_dma.c
index 7cf96b15377f..6378e0276691 100644
--- a/drivers/gpu/drm/radeon/ni_dma.c
+++ b/drivers/gpu/drm/radeon/ni_dma.c
@@ -248,8 +248,6 @@ int cayman_dma_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(DMA_RB_WPTR + reg_offset, ring->wptr << 2);
- ring->rptr = RREG32(DMA_RB_RPTR + reg_offset) >> 2;
-
WREG32(DMA_RB_CNTL + reg_offset, rb_cntl | DMA_RB_ENABLE);
ring->ready = true;
@@ -302,11 +300,9 @@ bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
mask = RADEON_RESET_DMA1;
if (!(reset_mask & mask)) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force ring activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c
index ca814276b075..004c931606c4 100644
--- a/drivers/gpu/drm/radeon/ni_dpm.c
+++ b/drivers/gpu/drm/radeon/ni_dpm.c
@@ -4025,9 +4025,6 @@ static int ni_parse_power_table(struct radeon_device *rdev)
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
@@ -4089,6 +4086,10 @@ int ni_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = ni_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 3cc78bb66042..b6c32640df20 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -1193,7 +1193,6 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
WREG32(RADEON_CP_RB_CNTL, tmp);
udelay(10);
- ring->rptr = RREG32(RADEON_CP_RB_RPTR);
/* Set cp mode to bus mastering & enable cp*/
WREG32(RADEON_CP_CSQ_MODE,
REG_SET(RADEON_INDIRECT2_START, indirect2_start) |
@@ -1275,12 +1274,12 @@ int r100_reloc_pitch_offset(struct radeon_cs_parser *p,
value = radeon_get_ib_value(p, idx);
tmp = value & 0x003fffff;
- tmp += (((u32)reloc->lobj.gpu_offset) >> 10);
+ tmp += (((u32)reloc->gpu_offset) >> 10);
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= RADEON_DST_TILE_MACRO;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
+ if (reloc->tiling_flags & RADEON_TILING_MICRO) {
if (reg == RADEON_SRC_PITCH_OFFSET) {
DRM_ERROR("Cannot src blit from microtiled surface\n");
radeon_cs_dump_packet(p, pkt);
@@ -1326,7 +1325,7 @@ int r100_packet3_load_vbpntr(struct radeon_cs_parser *p,
return r;
}
idx_value = radeon_get_ib_value(p, idx);
- ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
+ ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->gpu_offset);
track->arrays[i + 0].esize = idx_value >> 8;
track->arrays[i + 0].robj = reloc->robj;
@@ -1338,7 +1337,7 @@ int r100_packet3_load_vbpntr(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset);
+ ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->gpu_offset);
track->arrays[i + 1].robj = reloc->robj;
track->arrays[i + 1].esize = idx_value >> 24;
track->arrays[i + 1].esize &= 0x7F;
@@ -1352,7 +1351,7 @@ int r100_packet3_load_vbpntr(struct radeon_cs_parser *p,
return r;
}
idx_value = radeon_get_ib_value(p, idx);
- ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
+ ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->gpu_offset);
track->arrays[i + 0].robj = reloc->robj;
track->arrays[i + 0].esize = idx_value >> 8;
track->arrays[i + 0].esize &= 0x7F;
@@ -1595,7 +1594,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
track->zb.robj = reloc->robj;
track->zb.offset = idx_value;
track->zb_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case RADEON_RB3D_COLOROFFSET:
r = radeon_cs_packet_next_reloc(p, &reloc, 0);
@@ -1608,7 +1607,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
track->cb[0].robj = reloc->robj;
track->cb[0].offset = idx_value;
track->cb_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case RADEON_PP_TXOFFSET_0:
case RADEON_PP_TXOFFSET_1:
@@ -1622,16 +1621,16 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
return r;
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= RADEON_TXO_MACRO_TILE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= RADEON_TXO_MICRO_TILE_X2;
tmp = idx_value & ~(0x7 << 2);
tmp |= tile_flags;
- ib[idx] = tmp + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = tmp + ((u32)reloc->gpu_offset);
} else
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
track->textures[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -1649,7 +1648,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
return r;
}
track->textures[0].cube_info[i].offset = idx_value;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
track->textures[0].cube_info[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -1667,7 +1666,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
return r;
}
track->textures[1].cube_info[i].offset = idx_value;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
track->textures[1].cube_info[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -1685,7 +1684,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
return r;
}
track->textures[2].cube_info[i].offset = idx_value;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
track->textures[2].cube_info[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -1703,9 +1702,9 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
return r;
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= RADEON_COLOR_TILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= RADEON_COLOR_MICROTILE_ENABLE;
tmp = idx_value & ~(0x7 << 16);
@@ -1773,7 +1772,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case RADEON_PP_CNTL:
{
@@ -1933,7 +1932,7 @@ static int r100_packet3_check(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx+1] = radeon_get_ib_value(p, idx+1) + ((u32)reloc->lobj.gpu_offset);
+ ib[idx+1] = radeon_get_ib_value(p, idx+1) + ((u32)reloc->gpu_offset);
r = r100_cs_track_check_pkt3_indx_buffer(p, pkt, reloc->robj);
if (r) {
return r;
@@ -1947,7 +1946,7 @@ static int r100_packet3_check(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx] = radeon_get_ib_value(p, idx) + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = radeon_get_ib_value(p, idx) + ((u32)reloc->gpu_offset);
track->num_arrays = 1;
track->vtx_size = r100_get_vtx_size(radeon_get_ib_value(p, idx + 2));
@@ -2523,11 +2522,9 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
rbbm_status = RREG32(R_000E40_RBBM_STATUS);
if (!G_000E40_GUI_ACTIVE(rbbm_status)) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force CP activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
@@ -3223,12 +3220,12 @@ void r100_bandwidth_update(struct radeon_device *rdev)
if (rdev->mode_info.crtcs[0]->base.enabled) {
mode1 = &rdev->mode_info.crtcs[0]->base.mode;
- pixel_bytes1 = rdev->mode_info.crtcs[0]->base.fb->bits_per_pixel / 8;
+ pixel_bytes1 = rdev->mode_info.crtcs[0]->base.primary->fb->bits_per_pixel / 8;
}
if (!(rdev->flags & RADEON_SINGLE_CRTC)) {
if (rdev->mode_info.crtcs[1]->base.enabled) {
mode2 = &rdev->mode_info.crtcs[1]->base.mode;
- pixel_bytes2 = rdev->mode_info.crtcs[1]->base.fb->bits_per_pixel / 8;
+ pixel_bytes2 = rdev->mode_info.crtcs[1]->base.primary->fb->bits_per_pixel / 8;
}
}
diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
index b3807edb1936..58f0473aa73f 100644
--- a/drivers/gpu/drm/radeon/r200.c
+++ b/drivers/gpu/drm/radeon/r200.c
@@ -185,7 +185,7 @@ int r200_packet0_check(struct radeon_cs_parser *p,
track->zb.robj = reloc->robj;
track->zb.offset = idx_value;
track->zb_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case RADEON_RB3D_COLOROFFSET:
r = radeon_cs_packet_next_reloc(p, &reloc, 0);
@@ -198,7 +198,7 @@ int r200_packet0_check(struct radeon_cs_parser *p,
track->cb[0].robj = reloc->robj;
track->cb[0].offset = idx_value;
track->cb_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case R200_PP_TXOFFSET_0:
case R200_PP_TXOFFSET_1:
@@ -215,16 +215,16 @@ int r200_packet0_check(struct radeon_cs_parser *p,
return r;
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= R200_TXO_MACRO_TILE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= R200_TXO_MICRO_TILE;
tmp = idx_value & ~(0x7 << 2);
tmp |= tile_flags;
- ib[idx] = tmp + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = tmp + ((u32)reloc->gpu_offset);
} else
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
track->textures[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -268,7 +268,7 @@ int r200_packet0_check(struct radeon_cs_parser *p,
return r;
}
track->textures[i].cube_info[face - 1].offset = idx_value;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
track->textures[i].cube_info[face - 1].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -287,9 +287,9 @@ int r200_packet0_check(struct radeon_cs_parser *p,
}
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= RADEON_COLOR_TILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= RADEON_COLOR_MICROTILE_ENABLE;
tmp = idx_value & ~(0x7 << 16);
@@ -362,7 +362,7 @@ int r200_packet0_check(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case RADEON_PP_CNTL:
{
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 0b658b34b33a..206caf9700b7 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -640,7 +640,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
track->cb[i].robj = reloc->robj;
track->cb[i].offset = idx_value;
track->cb_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case R300_ZB_DEPTHOFFSET:
r = radeon_cs_packet_next_reloc(p, &reloc, 0);
@@ -653,7 +653,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
track->zb.robj = reloc->robj;
track->zb.offset = idx_value;
track->zb_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case R300_TX_OFFSET_0:
case R300_TX_OFFSET_0+4:
@@ -682,16 +682,16 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
if (p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS) {
ib[idx] = (idx_value & 31) | /* keep the 1st 5 bits */
- ((idx_value & ~31) + (u32)reloc->lobj.gpu_offset);
+ ((idx_value & ~31) + (u32)reloc->gpu_offset);
} else {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= R300_TXO_MACRO_TILE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= R300_TXO_MICRO_TILE;
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+ else if (reloc->tiling_flags & RADEON_TILING_MICRO_SQUARE)
tile_flags |= R300_TXO_MICRO_TILE_SQUARE;
- tmp = idx_value + ((u32)reloc->lobj.gpu_offset);
+ tmp = idx_value + ((u32)reloc->gpu_offset);
tmp |= tile_flags;
ib[idx] = tmp;
}
@@ -753,11 +753,11 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
return r;
}
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= R300_COLOR_TILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= R300_COLOR_MICROTILE_ENABLE;
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+ else if (reloc->tiling_flags & RADEON_TILING_MICRO_SQUARE)
tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE;
tmp = idx_value & ~(0x7 << 16);
@@ -838,11 +838,11 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
return r;
}
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
tile_flags |= R300_DEPTHMACROTILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ if (reloc->tiling_flags & RADEON_TILING_MICRO)
tile_flags |= R300_DEPTHMICROTILE_TILED;
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+ else if (reloc->tiling_flags & RADEON_TILING_MICRO_SQUARE)
tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE;
tmp = idx_value & ~(0x7 << 16);
@@ -1052,7 +1052,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case 0x4e0c:
/* RB3D_COLOR_CHANNEL_MASK */
@@ -1097,7 +1097,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
track->aa.robj = reloc->robj;
track->aa.offset = idx_value;
track->aa_dirty = true;
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ ib[idx] = idx_value + ((u32)reloc->gpu_offset);
break;
case R300_RB3D_AARESOLVE_PITCH:
track->aa.pitch = idx_value & 0x3FFE;
@@ -1162,7 +1162,7 @@ static int r300_packet3_check(struct radeon_cs_parser *p,
radeon_cs_dump_packet(p, pkt);
return r;
}
- ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset);
+ ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->gpu_offset);
r = r100_cs_track_check_pkt3_indx_buffer(p, pkt, reloc->robj);
if (r) {
return r;
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 647ef4079217..6e887d004eba 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -1748,11 +1748,9 @@ bool r600_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
if (!(reset_mask & (RADEON_RESET_GFX |
RADEON_RESET_COMPUTE |
RADEON_RESET_CP))) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force CP activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
@@ -2604,8 +2602,6 @@ int r600_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB_BASE, ring->gpu_addr >> 8);
WREG32(CP_DEBUG, (1 << 27) | (1 << 28));
- ring->rptr = RREG32(CP_RB_RPTR);
-
r600_cp_start(rdev);
ring->ready = true;
r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring);
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 2812c7d1ae6f..12511bb5fd6f 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -1022,7 +1022,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case SQ_CONFIG:
track->sq_config = radeon_get_ib_value(p, idx);
@@ -1043,7 +1043,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_depth_info = radeon_get_ib_value(p, idx);
ib[idx] &= C_028010_ARRAY_MODE;
track->db_depth_info &= C_028010_ARRAY_MODE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ if (reloc->tiling_flags & RADEON_TILING_MACRO) {
ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_2D_TILED_THIN1);
track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_2D_TILED_THIN1);
} else {
@@ -1084,9 +1084,9 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16;
track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->vgt_strmout_bo[tmp] = reloc->robj;
- track->vgt_strmout_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->vgt_strmout_bo_mc[tmp] = reloc->gpu_offset;
track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_SIZE_0:
@@ -1105,7 +1105,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case R_028238_CB_TARGET_MASK:
track->cb_target_mask = radeon_get_ib_value(p, idx);
@@ -1142,10 +1142,10 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4;
track->cb_color_info[tmp] = radeon_get_ib_value(p, idx);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ if (reloc->tiling_flags & RADEON_TILING_MACRO) {
ib[idx] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_2D_TILED_THIN1);
track->cb_color_info[tmp] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_2D_TILED_THIN1);
- } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
+ } else if (reloc->tiling_flags & RADEON_TILING_MICRO) {
ib[idx] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_1D_TILED_THIN1);
track->cb_color_info[tmp] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_1D_TILED_THIN1);
}
@@ -1214,7 +1214,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
track->cb_color_frag_bo[tmp] = reloc->robj;
track->cb_color_frag_offset[tmp] = (u64)ib[idx] << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
}
if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
track->cb_dirty = true;
@@ -1245,7 +1245,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
track->cb_color_tile_bo[tmp] = reloc->robj;
track->cb_color_tile_offset[tmp] = (u64)ib[idx] << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
}
if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
track->cb_dirty = true;
@@ -1281,10 +1281,10 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = (reg - CB_COLOR0_BASE) / 4;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
- track->cb_color_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->cb_color_bo_mc[tmp] = reloc->gpu_offset;
track->cb_dirty = true;
break;
case DB_DEPTH_BASE:
@@ -1295,9 +1295,9 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->db_offset = radeon_get_ib_value(p, idx) << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->db_bo = reloc->robj;
- track->db_bo_mc = reloc->lobj.gpu_offset;
+ track->db_bo_mc = reloc->gpu_offset;
track->db_dirty = true;
break;
case DB_HTILE_DATA_BASE:
@@ -1308,7 +1308,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return -EINVAL;
}
track->htile_offset = radeon_get_ib_value(p, idx) << 8;
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
track->htile_bo = reloc->robj;
track->db_dirty = true;
break;
@@ -1377,7 +1377,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case SX_MEMORY_EXPORT_BASE:
r = radeon_cs_packet_next_reloc(p, &reloc, r600_nomm);
@@ -1386,7 +1386,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
break;
case SX_MISC:
track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0;
@@ -1672,7 +1672,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(idx_value & 0xfffffff0) +
((u64)(tmp & 0xff) << 32);
@@ -1713,7 +1713,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
idx_value +
((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
@@ -1765,7 +1765,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffff0) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -1805,7 +1805,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
tmp = radeon_get_ib_value(p, idx) +
((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
- offset = reloc->lobj.gpu_offset + tmp;
+ offset = reloc->gpu_offset + tmp;
if ((tmp + size) > radeon_bo_size(reloc->robj)) {
dev_warn(p->dev, "CP DMA src buffer too small (%llu %lu)\n",
@@ -1835,7 +1835,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
tmp = radeon_get_ib_value(p, idx+2) +
((u64)(radeon_get_ib_value(p, idx+3) & 0xff) << 32);
- offset = reloc->lobj.gpu_offset + tmp;
+ offset = reloc->gpu_offset + tmp;
if ((tmp + size) > radeon_bo_size(reloc->robj)) {
dev_warn(p->dev, "CP DMA dst buffer too small (%llu %lu)\n",
@@ -1861,7 +1861,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SURFACE_SYNC\n");
return -EINVAL;
}
- ib[idx+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx+2] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
}
break;
case PACKET3_EVENT_WRITE:
@@ -1877,7 +1877,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -1899,7 +1899,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- offset = reloc->lobj.gpu_offset +
+ offset = reloc->gpu_offset +
(radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
@@ -1964,11 +1964,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE\n");
return -EINVAL;
}
- base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ base_offset = (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ if (reloc->tiling_flags & RADEON_TILING_MACRO)
ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
- else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ else if (reloc->tiling_flags & RADEON_TILING_MICRO)
ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
}
texture = reloc->robj;
@@ -1978,13 +1978,13 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE\n");
return -EINVAL;
}
- mip_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ mip_offset = (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
mipmap = reloc->robj;
r = r600_check_texture_resource(p, idx+(i*7)+1,
texture, mipmap,
base_offset + radeon_get_ib_value(p, idx+1+(i*7)+2),
mip_offset + radeon_get_ib_value(p, idx+1+(i*7)+3),
- reloc->lobj.tiling_flags);
+ reloc->tiling_flags);
if (r)
return r;
ib[idx+1+(i*7)+2] += base_offset;
@@ -2008,7 +2008,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj) - offset;
}
- offset64 = reloc->lobj.gpu_offset + offset;
+ offset64 = reloc->gpu_offset + offset;
ib[idx+1+(i*8)+0] = offset64;
ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
(upper_32_bits(offset64) & 0xff);
@@ -2118,7 +2118,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ ib[idx+1] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
}
break;
case PACKET3_SURFACE_BASE_UPDATE:
@@ -2151,7 +2151,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+1] = offset;
ib[idx+2] = upper_32_bits(offset) & 0xff;
}
@@ -2170,7 +2170,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+3] = offset;
ib[idx+4] = upper_32_bits(offset) & 0xff;
}
@@ -2199,7 +2199,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 8, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+0] = offset;
ib[idx+1] = upper_32_bits(offset) & 0xff;
break;
@@ -2224,7 +2224,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+1] = offset;
ib[idx+2] = upper_32_bits(offset) & 0xff;
} else {
@@ -2248,7 +2248,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- offset += reloc->lobj.gpu_offset;
+ offset += reloc->gpu_offset;
ib[idx+3] = offset;
ib[idx+4] = upper_32_bits(offset) & 0xff;
} else {
@@ -2505,14 +2505,14 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset <<= 8;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
p->idx += count + 5;
} else {
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+2] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+2] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
p->idx += count + 3;
}
if ((dst_offset + (count * 4)) > radeon_bo_size(dst_reloc->robj)) {
@@ -2539,22 +2539,22 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
/* tiled src, linear dst */
src_offset = radeon_get_ib_value(p, idx+1);
src_offset <<= 8;
- ib[idx+1] += (u32)(src_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(src_reloc->gpu_offset >> 8);
dst_offset = radeon_get_ib_value(p, idx+5);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32;
- ib[idx+5] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+6] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+5] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+6] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
} else {
/* linear src, tiled dst */
src_offset = radeon_get_ib_value(p, idx+5);
src_offset |= ((u64)(radeon_get_ib_value(p, idx+6) & 0xff)) << 32;
- ib[idx+5] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+6] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+5] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+6] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset <<= 8;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8);
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);
}
p->idx += 7;
} else {
@@ -2564,10 +2564,10 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff)) << 32;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+3] += upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+2] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+3] += upper_32_bits(dst_reloc->gpu_offset) & 0xff;
+ ib[idx+4] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
p->idx += 5;
} else {
src_offset = radeon_get_ib_value(p, idx+2);
@@ -2575,10 +2575,10 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset = radeon_get_ib_value(p, idx+1);
dst_offset |= ((u64)(radeon_get_ib_value(p, idx+3) & 0xff0000)) << 16;
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+2] += (u32)(src_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+3] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff;
- ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) & 0xff) << 16;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+2] += (u32)(src_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+3] += upper_32_bits(src_reloc->gpu_offset) & 0xff;
+ ib[idx+3] += (upper_32_bits(dst_reloc->gpu_offset) & 0xff) << 16;
p->idx += 4;
}
}
@@ -2610,8 +2610,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p)
dst_offset + (count * 4), radeon_bo_size(dst_reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset & 0xfffffffc);
- ib[idx+3] += (upper_32_bits(dst_reloc->lobj.gpu_offset) << 16) & 0x00ff0000;
+ ib[idx+1] += (u32)(dst_reloc->gpu_offset & 0xfffffffc);
+ ib[idx+3] += (upper_32_bits(dst_reloc->gpu_offset) << 16) & 0x00ff0000;
p->idx += 4;
break;
case DMA_PACKET_NOP:
diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c
index b2d4c91e6272..53fcb28f5578 100644
--- a/drivers/gpu/drm/radeon/r600_dma.c
+++ b/drivers/gpu/drm/radeon/r600_dma.c
@@ -176,8 +176,6 @@ int r600_dma_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(DMA_RB_WPTR, ring->wptr << 2);
- ring->rptr = RREG32(DMA_RB_RPTR) >> 2;
-
WREG32(DMA_RB_CNTL, rb_cntl | DMA_RB_ENABLE);
ring->ready = true;
@@ -221,11 +219,9 @@ bool r600_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
u32 reset_mask = r600_gpu_check_soft_reset(rdev);
if (!(reset_mask & RADEON_RESET_DMA)) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force ring activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index e4cc9b314ce9..cbf7e3269f84 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -834,6 +834,26 @@ static int r600_parse_clk_voltage_dep_table(struct radeon_clock_voltage_dependen
return 0;
}
+int r600_get_platform_caps(struct radeon_device *rdev)
+{
+ struct radeon_mode_info *mode_info = &rdev->mode_info;
+ union power_info *power_info;
+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+ u16 data_offset;
+ u8 frev, crev;
+
+ if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+ &frev, &crev, &data_offset))
+ return -EINVAL;
+ power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+ rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+ rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+ rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+
+ return 0;
+}
+
/* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */
#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12
#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14
@@ -1043,7 +1063,15 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(ext_hdr->usVCETableOffset) + 1 +
1 + array->ucNumEntries * sizeof(VCEClockInfo));
+ ATOM_PPLIB_VCE_State_Table *states =
+ (ATOM_PPLIB_VCE_State_Table *)
+ (mode_info->atom_context->bios + data_offset +
+ le16_to_cpu(ext_hdr->usVCETableOffset) + 1 +
+ 1 + (array->ucNumEntries * sizeof (VCEClockInfo)) +
+ 1 + (limits->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record)));
ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *entry;
+ ATOM_PPLIB_VCE_State_Record *state_entry;
+ VCEClockInfo *vce_clk;
u32 size = limits->numEntries *
sizeof(struct radeon_vce_clock_voltage_dependency_entry);
rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries =
@@ -1055,8 +1083,9 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.count =
limits->numEntries;
entry = &limits->entries[0];
+ state_entry = &states->entries[0];
for (i = 0; i < limits->numEntries; i++) {
- VCEClockInfo *vce_clk = (VCEClockInfo *)
+ vce_clk = (VCEClockInfo *)
((u8 *)&array->entries[0] +
(entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo)));
rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].evclk =
@@ -1068,6 +1097,23 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
entry = (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *)
((u8 *)entry + sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record));
}
+ for (i = 0; i < states->numEntries; i++) {
+ if (i >= RADEON_MAX_VCE_LEVELS)
+ break;
+ vce_clk = (VCEClockInfo *)
+ ((u8 *)&array->entries[0] +
+ (state_entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo)));
+ rdev->pm.dpm.vce_states[i].evclk =
+ le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16);
+ rdev->pm.dpm.vce_states[i].ecclk =
+ le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16);
+ rdev->pm.dpm.vce_states[i].clk_idx =
+ state_entry->ucClockInfoIndex & 0x3f;
+ rdev->pm.dpm.vce_states[i].pstate =
+ (state_entry->ucClockInfoIndex & 0xc0) >> 6;
+ state_entry = (ATOM_PPLIB_VCE_State_Record *)
+ ((u8 *)state_entry + sizeof(ATOM_PPLIB_VCE_State_Record));
+ }
}
if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) &&
ext_hdr->usUVDTableOffset) {
diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h
index 07eab2b04e81..46b9d2a03018 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.h
+++ b/drivers/gpu/drm/radeon/r600_dpm.h
@@ -215,6 +215,8 @@ void r600_stop_dpm(struct radeon_device *rdev);
bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor);
+int r600_get_platform_caps(struct radeon_device *rdev);
+
int r600_parse_extended_power_table(struct radeon_device *rdev);
void r600_free_extended_power_table(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index e887d027b6d0..f21db7a0b34d 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -113,19 +113,16 @@ extern int radeon_hard_reset;
#define RADEONFB_CONN_LIMIT 4
#define RADEON_BIOS_NUM_SCRATCH 8
-/* max number of rings */
-#define RADEON_NUM_RINGS 6
-
/* fence seq are set to this number when signaled */
#define RADEON_FENCE_SIGNALED_SEQ 0LL
/* internal ring indices */
/* r1xx+ has gfx CP ring */
-#define RADEON_RING_TYPE_GFX_INDEX 0
+#define RADEON_RING_TYPE_GFX_INDEX 0
/* cayman has 2 compute CP rings */
-#define CAYMAN_RING_TYPE_CP1_INDEX 1
-#define CAYMAN_RING_TYPE_CP2_INDEX 2
+#define CAYMAN_RING_TYPE_CP1_INDEX 1
+#define CAYMAN_RING_TYPE_CP2_INDEX 2
/* R600+ has an async dma ring */
#define R600_RING_TYPE_DMA_INDEX 3
@@ -133,7 +130,17 @@ extern int radeon_hard_reset;
#define CAYMAN_RING_TYPE_DMA1_INDEX 4
/* R600+ */
-#define R600_RING_TYPE_UVD_INDEX 5
+#define R600_RING_TYPE_UVD_INDEX 5
+
+/* TN+ */
+#define TN_RING_TYPE_VCE1_INDEX 6
+#define TN_RING_TYPE_VCE2_INDEX 7
+
+/* max number of rings */
+#define RADEON_NUM_RINGS 8
+
+/* number of hw syncs before falling back on blocking */
+#define RADEON_NUM_SYNCS 4
/* number of hw syncs before falling back on blocking */
#define RADEON_NUM_SYNCS 4
@@ -356,9 +363,8 @@ int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence **fence, i
void radeon_fence_process(struct radeon_device *rdev, int ring);
bool radeon_fence_signaled(struct radeon_fence *fence);
int radeon_fence_wait(struct radeon_fence *fence, bool interruptible);
-int radeon_fence_wait_locked(struct radeon_fence *fence);
-int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring);
-int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring);
+int radeon_fence_wait_next(struct radeon_device *rdev, int ring);
+int radeon_fence_wait_empty(struct radeon_device *rdev, int ring);
int radeon_fence_wait_any(struct radeon_device *rdev,
struct radeon_fence **fences,
bool intr);
@@ -450,6 +456,7 @@ struct radeon_bo {
/* Protected by gem.mutex */
struct list_head list;
/* Protected by tbo.reserved */
+ u32 initial_domain;
u32 placements[3];
struct ttm_placement placement;
struct ttm_buffer_object tbo;
@@ -472,16 +479,6 @@ struct radeon_bo {
};
#define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base)
-struct radeon_bo_list {
- struct ttm_validate_buffer tv;
- struct radeon_bo *bo;
- uint64_t gpu_offset;
- bool written;
- unsigned domain;
- unsigned alt_domain;
- u32 tiling_flags;
-};
-
int radeon_gem_debugfs_init(struct radeon_device *rdev);
/* sub-allocation manager, it has to be protected by another lock.
@@ -789,7 +786,6 @@ struct radeon_ib {
struct radeon_ring {
struct radeon_bo *ring_obj;
volatile uint32_t *ring;
- unsigned rptr;
unsigned rptr_offs;
unsigned rptr_save_reg;
u64 next_rptr_gpu_addr;
@@ -799,8 +795,8 @@ struct radeon_ring {
unsigned ring_size;
unsigned ring_free_dw;
int count_dw;
- unsigned long last_activity;
- unsigned last_rptr;
+ atomic_t last_rptr;
+ atomic64_t last_activity;
uint64_t gpu_addr;
uint32_t align_mask;
uint32_t ptr_mask;
@@ -852,17 +848,22 @@ struct radeon_mec {
#define R600_PTE_READABLE (1 << 5)
#define R600_PTE_WRITEABLE (1 << 6)
+struct radeon_vm_pt {
+ struct radeon_bo *bo;
+ uint64_t addr;
+};
+
struct radeon_vm {
- struct list_head list;
struct list_head va;
unsigned id;
/* contains the page directory */
- struct radeon_sa_bo *page_directory;
+ struct radeon_bo *page_directory;
uint64_t pd_gpu_addr;
+ unsigned max_pde_used;
/* array of page tables, one for each page directory entry */
- struct radeon_sa_bo **page_tables;
+ struct radeon_vm_pt *page_tables;
struct mutex mutex;
/* last fence for cs using this vm */
@@ -874,10 +875,7 @@ struct radeon_vm {
};
struct radeon_vm_manager {
- struct mutex lock;
- struct list_head lru_vm;
struct radeon_fence *active[RADEON_NUM_VM];
- struct radeon_sa_manager sa_manager;
uint32_t max_pfn;
/* number of VMIDs */
unsigned nvm;
@@ -953,8 +951,8 @@ void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *c
void radeon_ring_undo(struct radeon_ring *ring);
void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *cp);
int radeon_ring_test(struct radeon_device *rdev, struct radeon_ring *cp);
-void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring);
-void radeon_ring_lockup_update(struct radeon_ring *ring);
+void radeon_ring_lockup_update(struct radeon_device *rdev,
+ struct radeon_ring *ring);
bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
unsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring,
uint32_t **data);
@@ -980,9 +978,12 @@ void cayman_dma_fini(struct radeon_device *rdev);
struct radeon_cs_reloc {
struct drm_gem_object *gobj;
struct radeon_bo *robj;
- struct radeon_bo_list lobj;
+ struct ttm_validate_buffer tv;
+ uint64_t gpu_offset;
+ unsigned domain;
+ unsigned alt_domain;
+ uint32_t tiling_flags;
uint32_t handle;
- uint32_t flags;
};
struct radeon_cs_chunk {
@@ -1006,6 +1007,7 @@ struct radeon_cs_parser {
unsigned nrelocs;
struct radeon_cs_reloc *relocs;
struct radeon_cs_reloc **relocs_ptr;
+ struct radeon_cs_reloc *vm_bos;
struct list_head validated;
unsigned dma_reloc_idx;
/* indices of various chunks */
@@ -1255,6 +1257,17 @@ enum radeon_dpm_event_src {
RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL = 4
};
+#define RADEON_MAX_VCE_LEVELS 6
+
+enum radeon_vce_level {
+ RADEON_VCE_LEVEL_AC_ALL = 0, /* AC, All cases */
+ RADEON_VCE_LEVEL_DC_EE = 1, /* DC, entropy encoding */
+ RADEON_VCE_LEVEL_DC_LL_LOW = 2, /* DC, low latency queue, res <= 720 */
+ RADEON_VCE_LEVEL_DC_LL_HIGH = 3, /* DC, low latency queue, 1080 >= res > 720 */
+ RADEON_VCE_LEVEL_DC_GP_LOW = 4, /* DC, general purpose queue, res <= 720 */
+ RADEON_VCE_LEVEL_DC_GP_HIGH = 5, /* DC, general purpose queue, 1080 >= res > 720 */
+};
+
struct radeon_ps {
u32 caps; /* vbios flags */
u32 class; /* vbios flags */
@@ -1265,6 +1278,8 @@ struct radeon_ps {
/* VCE clocks */
u32 evclk;
u32 ecclk;
+ bool vce_active;
+ enum radeon_vce_level vce_level;
/* asic priv */
void *ps_priv;
};
@@ -1439,6 +1454,17 @@ enum radeon_dpm_forced_level {
RADEON_DPM_FORCED_LEVEL_HIGH = 2,
};
+struct radeon_vce_state {
+ /* vce clocks */
+ u32 evclk;
+ u32 ecclk;
+ /* gpu clocks */
+ u32 sclk;
+ u32 mclk;
+ u8 clk_idx;
+ u8 pstate;
+};
+
struct radeon_dpm {
struct radeon_ps *ps;
/* number of valid power states */
@@ -1451,6 +1477,9 @@ struct radeon_dpm {
struct radeon_ps *boot_ps;
/* default uvd power state */
struct radeon_ps *uvd_ps;
+ /* vce requirements */
+ struct radeon_vce_state vce_states[RADEON_MAX_VCE_LEVELS];
+ enum radeon_vce_level vce_level;
enum radeon_pm_state_type state;
enum radeon_pm_state_type user_state;
u32 platform_caps;
@@ -1476,6 +1505,7 @@ struct radeon_dpm {
/* special states active */
bool thermal_active;
bool uvd_active;
+ bool vce_active;
/* thermal handling */
struct radeon_dpm_thermal thermal;
/* forced levels */
@@ -1486,6 +1516,7 @@ struct radeon_dpm {
};
void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable);
+void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable);
struct radeon_pm {
struct mutex mutex;
@@ -1591,6 +1622,45 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev,
int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev,
unsigned cg_upll_func_cntl);
+/*
+ * VCE
+ */
+#define RADEON_MAX_VCE_HANDLES 16
+#define RADEON_VCE_STACK_SIZE (1024*1024)
+#define RADEON_VCE_HEAP_SIZE (4*1024*1024)
+
+struct radeon_vce {
+ struct radeon_bo *vcpu_bo;
+ uint64_t gpu_addr;
+ unsigned fw_version;
+ unsigned fb_version;
+ atomic_t handles[RADEON_MAX_VCE_HANDLES];
+ struct drm_file *filp[RADEON_MAX_VCE_HANDLES];
+ struct delayed_work idle_work;
+};
+
+int radeon_vce_init(struct radeon_device *rdev);
+void radeon_vce_fini(struct radeon_device *rdev);
+int radeon_vce_suspend(struct radeon_device *rdev);
+int radeon_vce_resume(struct radeon_device *rdev);
+int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
+ uint32_t handle, struct radeon_fence **fence);
+int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
+ uint32_t handle, struct radeon_fence **fence);
+void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp);
+void radeon_vce_note_usage(struct radeon_device *rdev);
+int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi);
+int radeon_vce_cs_parse(struct radeon_cs_parser *p);
+bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
+ struct radeon_ring *ring,
+ struct radeon_semaphore *semaphore,
+ bool emit_wait);
+void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
+void radeon_vce_fence_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence);
+int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
+int radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
+
struct r600_audio_pin {
int channels;
int rate;
@@ -1780,6 +1850,7 @@ struct radeon_asic {
void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes);
void (*set_clock_gating)(struct radeon_device *rdev, int enable);
int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk);
+ int (*set_vce_clocks)(struct radeon_device *rdev, u32 evclk, u32 ecclk);
int (*get_temperature)(struct radeon_device *rdev);
} pm;
/* dynamic power management */
@@ -2041,6 +2112,8 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
int radeon_gem_va_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
+int radeon_gem_op_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
@@ -2186,6 +2259,7 @@ struct radeon_device {
struct radeon_gem gem;
struct radeon_pm pm;
struct radeon_uvd uvd;
+ struct radeon_vce vce;
uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH];
struct radeon_wb wb;
struct radeon_dummy_page dummy_page;
@@ -2205,6 +2279,7 @@ struct radeon_device {
const struct firmware *sdma_fw; /* CIK SDMA firmware */
const struct firmware *smc_fw; /* SMC firmware */
const struct firmware *uvd_fw; /* UVD firmware */
+ const struct firmware *vce_fw; /* VCE firmware */
struct r600_vram_scratch vram_scratch;
int msi_enabled; /* msi enabled */
struct r600_ih ih; /* r6/700 interrupt ring */
@@ -2229,6 +2304,10 @@ struct radeon_device {
/* virtual memory */
struct radeon_vm_manager vm_manager;
struct mutex gpu_clock_mutex;
+ /* memory stats */
+ atomic64_t vram_usage;
+ atomic64_t gtt_usage;
+ atomic64_t num_bytes_moved;
/* ACPI interface */
struct radeon_atif atif;
struct radeon_atcs atcs;
@@ -2639,6 +2718,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->pm.set_pcie_lanes((rdev), (l))
#define radeon_set_clock_gating(rdev, e) (rdev)->asic->pm.set_clock_gating((rdev), (e))
#define radeon_set_uvd_clocks(rdev, v, d) (rdev)->asic->pm.set_uvd_clocks((rdev), (v), (d))
+#define radeon_set_vce_clocks(rdev, ev, ec) (rdev)->asic->pm.set_vce_clocks((rdev), (ev), (ec))
#define radeon_get_temperature(rdev) (rdev)->asic->pm.get_temperature((rdev))
#define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->surface.set_reg((rdev), (r), (f), (p), (o), (s)))
#define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->surface.clear_reg((rdev), (r)))
@@ -2715,16 +2795,22 @@ extern void radeon_program_register_sequence(struct radeon_device *rdev,
*/
int radeon_vm_manager_init(struct radeon_device *rdev);
void radeon_vm_manager_fini(struct radeon_device *rdev);
-void radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm);
+int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm);
void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm);
-int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm);
-void radeon_vm_add_to_lru(struct radeon_device *rdev, struct radeon_vm *vm);
+struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ struct list_head *head);
struct radeon_fence *radeon_vm_grab_id(struct radeon_device *rdev,
struct radeon_vm *vm, int ring);
+void radeon_vm_flush(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ int ring);
void radeon_vm_fence(struct radeon_device *rdev,
struct radeon_vm *vm,
struct radeon_fence *fence);
uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr);
+int radeon_vm_update_page_directory(struct radeon_device *rdev,
+ struct radeon_vm *vm);
int radeon_vm_bo_update(struct radeon_device *rdev,
struct radeon_vm *vm,
struct radeon_bo *bo,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index dda02bfc10a4..b8a24a75d4ff 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -1987,6 +1987,19 @@ static struct radeon_asic_ring ci_dma_ring = {
.set_wptr = &cik_sdma_set_wptr,
};
+static struct radeon_asic_ring ci_vce_ring = {
+ .ib_execute = &radeon_vce_ib_execute,
+ .emit_fence = &radeon_vce_fence_emit,
+ .emit_semaphore = &radeon_vce_semaphore_emit,
+ .cs_parse = &radeon_vce_cs_parse,
+ .ring_test = &radeon_vce_ring_test,
+ .ib_test = &radeon_vce_ib_test,
+ .is_lockup = &radeon_ring_test_lockup,
+ .get_rptr = &vce_v1_0_get_rptr,
+ .get_wptr = &vce_v1_0_get_wptr,
+ .set_wptr = &vce_v1_0_set_wptr,
+};
+
static struct radeon_asic ci_asic = {
.init = &cik_init,
.fini = &cik_fini,
@@ -2015,6 +2028,8 @@ static struct radeon_asic ci_asic = {
[R600_RING_TYPE_DMA_INDEX] = &ci_dma_ring,
[CAYMAN_RING_TYPE_DMA1_INDEX] = &ci_dma_ring,
[R600_RING_TYPE_UVD_INDEX] = &cayman_uvd_ring,
+ [TN_RING_TYPE_VCE1_INDEX] = &ci_vce_ring,
+ [TN_RING_TYPE_VCE2_INDEX] = &ci_vce_ring,
},
.irq = {
.set = &cik_irq_set,
@@ -2061,6 +2076,7 @@ static struct radeon_asic ci_asic = {
.set_pcie_lanes = NULL,
.set_clock_gating = NULL,
.set_uvd_clocks = &cik_set_uvd_clocks,
+ .set_vce_clocks = &cik_set_vce_clocks,
.get_temperature = &ci_get_temp,
},
.dpm = {
@@ -2117,6 +2133,8 @@ static struct radeon_asic kv_asic = {
[R600_RING_TYPE_DMA_INDEX] = &ci_dma_ring,
[CAYMAN_RING_TYPE_DMA1_INDEX] = &ci_dma_ring,
[R600_RING_TYPE_UVD_INDEX] = &cayman_uvd_ring,
+ [TN_RING_TYPE_VCE1_INDEX] = &ci_vce_ring,
+ [TN_RING_TYPE_VCE2_INDEX] = &ci_vce_ring,
},
.irq = {
.set = &cik_irq_set,
@@ -2163,6 +2181,7 @@ static struct radeon_asic kv_asic = {
.set_pcie_lanes = NULL,
.set_clock_gating = NULL,
.set_uvd_clocks = &cik_set_uvd_clocks,
+ .set_vce_clocks = &cik_set_vce_clocks,
.get_temperature = &kv_get_temp,
},
.dpm = {
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index ae637cfda783..3d55a3a39e82 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -717,6 +717,7 @@ u32 cik_get_xclk(struct radeon_device *rdev);
uint32_t cik_pciep_rreg(struct radeon_device *rdev, uint32_t reg);
void cik_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
+int cik_set_vce_clocks(struct radeon_device *rdev, u32 evclk, u32 ecclk);
void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
struct radeon_fence *fence);
bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
@@ -863,4 +864,17 @@ bool uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
/* uvd v4.2 */
int uvd_v4_2_resume(struct radeon_device *rdev);
+/* vce v1.0 */
+uint32_t vce_v1_0_get_rptr(struct radeon_device *rdev,
+ struct radeon_ring *ring);
+uint32_t vce_v1_0_get_wptr(struct radeon_device *rdev,
+ struct radeon_ring *ring);
+void vce_v1_0_set_wptr(struct radeon_device *rdev,
+ struct radeon_ring *ring);
+int vce_v1_0_init(struct radeon_device *rdev);
+int vce_v1_0_start(struct radeon_device *rdev);
+
+/* vce v2.0 */
+int vce_v2_0_resume(struct radeon_device *rdev);
+
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 82d4f865546e..c566b486ca08 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -89,7 +89,7 @@ static void radeon_property_change_mode(struct drm_encoder *encoder)
if (crtc && crtc->enabled) {
drm_crtc_helper_set_mode(crtc, &crtc->mode,
- crtc->x, crtc->y, crtc->fb);
+ crtc->x, crtc->y, crtc->primary->fb);
}
}
@@ -1595,6 +1595,7 @@ radeon_add_atom_connector(struct drm_device *dev,
uint32_t subpixel_order = SubPixelNone;
bool shared_ddc = false;
bool is_dp_bridge = false;
+ bool has_aux = false;
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
return;
@@ -1672,7 +1673,9 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
else
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
- if (!radeon_dig_connector->dp_i2c_bus)
+ if (radeon_dig_connector->dp_i2c_bus)
+ has_aux = true;
+ else
DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
@@ -1895,7 +1898,9 @@ radeon_add_atom_connector(struct drm_device *dev,
if (!radeon_dig_connector->dp_i2c_bus)
DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
- if (!radeon_connector->ddc_bus)
+ if (radeon_connector->ddc_bus)
+ has_aux = true;
+ else
DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
subpixel_order = SubPixelHorizontalRGB;
@@ -1939,7 +1944,9 @@ radeon_add_atom_connector(struct drm_device *dev,
if (i2c_bus->valid) {
/* add DP i2c bus */
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
- if (!radeon_dig_connector->dp_i2c_bus)
+ if (radeon_dig_connector->dp_i2c_bus)
+ has_aux = true;
+ else
DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
@@ -2000,6 +2007,10 @@ radeon_add_atom_connector(struct drm_device *dev,
connector->display_info.subpixel_order = subpixel_order;
drm_sysfs_connector_add(connector);
+
+ if (has_aux)
+ radeon_dp_aux_init(radeon_connector);
+
return;
failed:
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index dfb5a1db87d4..2b6e0ebcc13a 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -24,16 +24,59 @@
* Authors:
* Jerome Glisse <glisse@freedesktop.org>
*/
+#include <linux/list_sort.h>
#include <drm/drmP.h>
#include <drm/radeon_drm.h>
#include "radeon_reg.h"
#include "radeon.h"
#include "radeon_trace.h"
+#define RADEON_CS_MAX_PRIORITY 32u
+#define RADEON_CS_NUM_BUCKETS (RADEON_CS_MAX_PRIORITY + 1)
+
+/* This is based on the bucket sort with O(n) time complexity.
+ * An item with priority "i" is added to bucket[i]. The lists are then
+ * concatenated in descending order.
+ */
+struct radeon_cs_buckets {
+ struct list_head bucket[RADEON_CS_NUM_BUCKETS];
+};
+
+static void radeon_cs_buckets_init(struct radeon_cs_buckets *b)
+{
+ unsigned i;
+
+ for (i = 0; i < RADEON_CS_NUM_BUCKETS; i++)
+ INIT_LIST_HEAD(&b->bucket[i]);
+}
+
+static void radeon_cs_buckets_add(struct radeon_cs_buckets *b,
+ struct list_head *item, unsigned priority)
+{
+ /* Since buffers which appear sooner in the relocation list are
+ * likely to be used more often than buffers which appear later
+ * in the list, the sort mustn't change the ordering of buffers
+ * with the same priority, i.e. it must be stable.
+ */
+ list_add_tail(item, &b->bucket[min(priority, RADEON_CS_MAX_PRIORITY)]);
+}
+
+static void radeon_cs_buckets_get_list(struct radeon_cs_buckets *b,
+ struct list_head *out_list)
+{
+ unsigned i;
+
+ /* Connect the sorted buckets in the output list. */
+ for (i = 0; i < RADEON_CS_NUM_BUCKETS; i++) {
+ list_splice(&b->bucket[i], out_list);
+ }
+}
+
static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
{
struct drm_device *ddev = p->rdev->ddev;
struct radeon_cs_chunk *chunk;
+ struct radeon_cs_buckets buckets;
unsigned i, j;
bool duplicate;
@@ -52,8 +95,12 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->relocs == NULL) {
return -ENOMEM;
}
+
+ radeon_cs_buckets_init(&buckets);
+
for (i = 0; i < p->nrelocs; i++) {
struct drm_radeon_cs_reloc *r;
+ unsigned priority;
duplicate = false;
r = (struct drm_radeon_cs_reloc *)&chunk->kdata[i*4];
@@ -78,8 +125,14 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
}
p->relocs_ptr[i] = &p->relocs[i];
p->relocs[i].robj = gem_to_radeon_bo(p->relocs[i].gobj);
- p->relocs[i].lobj.bo = p->relocs[i].robj;
- p->relocs[i].lobj.written = !!r->write_domain;
+
+ /* The userspace buffer priorities are from 0 to 15. A higher
+ * number means the buffer is more important.
+ * Also, the buffers used for write have a higher priority than
+ * the buffers used for read only, which doubles the range
+ * to 0 to 31. 32 is reserved for the kernel driver.
+ */
+ priority = (r->flags & 0xf) * 2 + !!r->write_domain;
/* the first reloc of an UVD job is the msg and that must be in
VRAM, also but everything into VRAM on AGP cards to avoid
@@ -87,29 +140,38 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->ring == R600_RING_TYPE_UVD_INDEX &&
(i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
/* TODO: is this still needed for NI+ ? */
- p->relocs[i].lobj.domain =
+ p->relocs[i].domain =
RADEON_GEM_DOMAIN_VRAM;
- p->relocs[i].lobj.alt_domain =
+ p->relocs[i].alt_domain =
RADEON_GEM_DOMAIN_VRAM;
+ /* prioritize this over any other relocation */
+ priority = RADEON_CS_MAX_PRIORITY;
} else {
uint32_t domain = r->write_domain ?
r->write_domain : r->read_domains;
- p->relocs[i].lobj.domain = domain;
+ p->relocs[i].domain = domain;
if (domain == RADEON_GEM_DOMAIN_VRAM)
domain |= RADEON_GEM_DOMAIN_GTT;
- p->relocs[i].lobj.alt_domain = domain;
+ p->relocs[i].alt_domain = domain;
}
- p->relocs[i].lobj.tv.bo = &p->relocs[i].robj->tbo;
+ p->relocs[i].tv.bo = &p->relocs[i].robj->tbo;
p->relocs[i].handle = r->handle;
- radeon_bo_list_add_object(&p->relocs[i].lobj,
- &p->validated);
+ radeon_cs_buckets_add(&buckets, &p->relocs[i].tv.head,
+ priority);
}
- return radeon_bo_list_validate(&p->ticket, &p->validated, p->ring);
+
+ radeon_cs_buckets_get_list(&buckets, &p->validated);
+
+ if (p->cs_flags & RADEON_CS_USE_VM)
+ p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm,
+ &p->validated);
+
+ return radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring);
}
static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
@@ -147,6 +209,10 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
case RADEON_CS_RING_UVD:
p->ring = R600_RING_TYPE_UVD_INDEX;
break;
+ case RADEON_CS_RING_VCE:
+ /* TODO: only use the low priority ring for now */
+ p->ring = TN_RING_TYPE_VCE1_INDEX;
+ break;
}
return 0;
}
@@ -286,6 +352,16 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
return 0;
}
+static int cmp_size_smaller_first(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ struct radeon_cs_reloc *la = list_entry(a, struct radeon_cs_reloc, tv.head);
+ struct radeon_cs_reloc *lb = list_entry(b, struct radeon_cs_reloc, tv.head);
+
+ /* Sort A before B if A is smaller. */
+ return (int)la->robj->tbo.num_pages - (int)lb->robj->tbo.num_pages;
+}
+
/**
* cs_parser_fini() - clean parser states
* @parser: parser structure holding parsing context.
@@ -299,6 +375,18 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
unsigned i;
if (!error) {
+ /* Sort the buffer list from the smallest to largest buffer,
+ * which affects the order of buffers in the LRU list.
+ * This assures that the smallest buffers are added first
+ * to the LRU list, so they are likely to be later evicted
+ * first, instead of large buffers whose eviction is more
+ * expensive.
+ *
+ * This slightly lowers the number of bytes moved by TTM
+ * per frame under memory pressure.
+ */
+ list_sort(NULL, &parser->validated, cmp_size_smaller_first);
+
ttm_eu_fence_buffer_objects(&parser->ticket,
&parser->validated,
parser->ib.fence);
@@ -316,6 +404,7 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
kfree(parser->track);
kfree(parser->relocs);
kfree(parser->relocs_ptr);
+ kfree(parser->vm_bos);
for (i = 0; i < parser->nchunks; i++)
drm_free_large(parser->chunks[i].kdata);
kfree(parser->chunks);
@@ -343,6 +432,9 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev,
if (parser->ring == R600_RING_TYPE_UVD_INDEX)
radeon_uvd_note_usage(rdev);
+ else if ((parser->ring == TN_RING_TYPE_VCE1_INDEX) ||
+ (parser->ring == TN_RING_TYPE_VCE2_INDEX))
+ radeon_vce_note_usage(rdev);
radeon_cs_sync_rings(parser);
r = radeon_ib_schedule(rdev, &parser->ib, NULL);
@@ -352,24 +444,32 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev,
return r;
}
-static int radeon_bo_vm_update_pte(struct radeon_cs_parser *parser,
+static int radeon_bo_vm_update_pte(struct radeon_cs_parser *p,
struct radeon_vm *vm)
{
- struct radeon_device *rdev = parser->rdev;
- struct radeon_bo_list *lobj;
- struct radeon_bo *bo;
- int r;
+ struct radeon_device *rdev = p->rdev;
+ int i, r;
- r = radeon_vm_bo_update(rdev, vm, rdev->ring_tmp_bo.bo, &rdev->ring_tmp_bo.bo->tbo.mem);
- if (r) {
+ r = radeon_vm_update_page_directory(rdev, vm);
+ if (r)
return r;
- }
- list_for_each_entry(lobj, &parser->validated, tv.head) {
- bo = lobj->bo;
- r = radeon_vm_bo_update(parser->rdev, vm, bo, &bo->tbo.mem);
- if (r) {
+
+ r = radeon_vm_bo_update(rdev, vm, rdev->ring_tmp_bo.bo,
+ &rdev->ring_tmp_bo.bo->tbo.mem);
+ if (r)
+ return r;
+
+ for (i = 0; i < p->nrelocs; i++) {
+ struct radeon_bo *bo;
+
+ /* ignore duplicates */
+ if (p->relocs_ptr[i] != &p->relocs[i])
+ continue;
+
+ bo = p->relocs[i].robj;
+ r = radeon_vm_bo_update(rdev, vm, bo, &bo->tbo.mem);
+ if (r)
return r;
- }
}
return 0;
}
@@ -401,20 +501,13 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
if (parser->ring == R600_RING_TYPE_UVD_INDEX)
radeon_uvd_note_usage(rdev);
- mutex_lock(&rdev->vm_manager.lock);
mutex_lock(&vm->mutex);
- r = radeon_vm_alloc_pt(rdev, vm);
- if (r) {
- goto out;
- }
r = radeon_bo_vm_update_pte(parser, vm);
if (r) {
goto out;
}
radeon_cs_sync_rings(parser);
radeon_semaphore_sync_to(parser->ib.semaphore, vm->fence);
- radeon_semaphore_sync_to(parser->ib.semaphore,
- radeon_vm_grab_id(rdev, vm, parser->ring));
if ((rdev->family >= CHIP_TAHITI) &&
(parser->chunk_const_ib_idx != -1)) {
@@ -423,14 +516,8 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
r = radeon_ib_schedule(rdev, &parser->ib, NULL);
}
- if (!r) {
- radeon_vm_fence(rdev, vm, parser->ib.fence);
- }
-
out:
- radeon_vm_add_to_lru(rdev, vm);
mutex_unlock(&vm->mutex);
- mutex_unlock(&rdev->vm_manager.lock);
return r;
}
@@ -698,9 +785,9 @@ int radeon_cs_packet_next_reloc(struct radeon_cs_parser *p,
/* FIXME: we assume reloc size is 4 dwords */
if (nomm) {
*cs_reloc = p->relocs;
- (*cs_reloc)->lobj.gpu_offset =
+ (*cs_reloc)->gpu_offset =
(u64)relocs_chunk->kdata[idx + 3] << 32;
- (*cs_reloc)->lobj.gpu_offset |= relocs_chunk->kdata[idx + 0];
+ (*cs_reloc)->gpu_offset |= relocs_chunk->kdata[idx + 0];
} else
*cs_reloc = p->relocs_ptr[(idx / 4)];
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 044bc98fb459..835516d2d257 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1191,14 +1191,12 @@ int radeon_device_init(struct radeon_device *rdev,
r = radeon_gem_init(rdev);
if (r)
return r;
- /* initialize vm here */
- mutex_init(&rdev->vm_manager.lock);
+
/* Adjust VM size here.
* Currently set to 4GB ((1 << 20) 4k pages).
* Max GPUVM size for cayman and SI is 40 bits.
*/
rdev->vm_manager.max_pfn = 1 << 20;
- INIT_LIST_HEAD(&rdev->vm_manager.lru_vm);
/* Set asic functions */
r = radeon_asic_init(rdev);
@@ -1426,7 +1424,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
/* unpin the front buffers */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb);
+ struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->primary->fb);
struct radeon_bo *robj;
if (rfb == NULL || rfb->obj == NULL) {
@@ -1445,10 +1443,9 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
/* evict vram memory */
radeon_bo_evict_vram(rdev);
- mutex_lock(&rdev->ring_lock);
/* wait for gpu to finish processing current batch */
for (i = 0; i < RADEON_NUM_RINGS; i++) {
- r = radeon_fence_wait_empty_locked(rdev, i);
+ r = radeon_fence_wait_empty(rdev, i);
if (r) {
/* delay GPU reset to resume */
force_completion = true;
@@ -1457,7 +1454,6 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
if (force_completion) {
radeon_fence_driver_force_completion(rdev);
}
- mutex_unlock(&rdev->ring_lock);
radeon_save_bios_scratch_regs(rdev);
@@ -1555,10 +1551,12 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
/* reset hpd state */
radeon_hpd_init(rdev);
/* blat the mode back in */
- drm_helper_resume_force_mode(dev);
- /* turn on display hw */
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+ if (fbcon) {
+ drm_helper_resume_force_mode(dev);
+ /* turn on display hw */
+ 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);
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index fbd8b930f2be..386cfa4c194d 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -34,6 +34,8 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
+#include <linux/gcd.h>
+
static void avivo_crtc_load_lut(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -369,7 +371,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
work->event = event;
work->rdev = rdev;
work->crtc_id = radeon_crtc->crtc_id;
- old_radeon_fb = to_radeon_framebuffer(crtc->fb);
+ old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
new_radeon_fb = to_radeon_framebuffer(fb);
/* schedule unpin of the old buffer */
obj = old_radeon_fb->obj;
@@ -460,7 +462,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
spin_unlock_irqrestore(&dev->event_lock, flags);
/* update crtc fb */
- crtc->fb = fb;
+ crtc->primary->fb = fb;
r = drm_vblank_get(dev, radeon_crtc->crtc_id);
if (r) {
@@ -792,6 +794,7 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
if (radeon_connector->edid) {
drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
+ drm_edid_to_eld(&radeon_connector->base, radeon_connector->edid);
return ret;
}
drm_mode_connector_update_edid_property(&radeon_connector->base, NULL);
@@ -799,66 +802,57 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
}
/* avivo */
-static void avivo_get_fb_div(struct radeon_pll *pll,
- u32 target_clock,
- u32 post_div,
- u32 ref_div,
- u32 *fb_div,
- u32 *frac_fb_div)
-{
- u32 tmp = post_div * ref_div;
- tmp *= target_clock;
- *fb_div = tmp / pll->reference_freq;
- *frac_fb_div = tmp % pll->reference_freq;
-
- if (*fb_div > pll->max_feedback_div)
- *fb_div = pll->max_feedback_div;
- else if (*fb_div < pll->min_feedback_div)
- *fb_div = pll->min_feedback_div;
-}
-
-static u32 avivo_get_post_div(struct radeon_pll *pll,
- u32 target_clock)
+/**
+ * avivo_reduce_ratio - fractional number reduction
+ *
+ * @nom: nominator
+ * @den: denominator
+ * @nom_min: minimum value for nominator
+ * @den_min: minimum value for denominator
+ *
+ * Find the greatest common divisor and apply it on both nominator and
+ * denominator, but make nominator and denominator are at least as large
+ * as their minimum values.
+ */
+static void avivo_reduce_ratio(unsigned *nom, unsigned *den,
+ unsigned nom_min, unsigned den_min)
{
- u32 vco, post_div, tmp;
-
- if (pll->flags & RADEON_PLL_USE_POST_DIV)
- return pll->post_div;
-
- if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) {
- if (pll->flags & RADEON_PLL_IS_LCD)
- vco = pll->lcd_pll_out_min;
- else
- vco = pll->pll_out_min;
- } else {
- if (pll->flags & RADEON_PLL_IS_LCD)
- vco = pll->lcd_pll_out_max;
- else
- vco = pll->pll_out_max;
+ unsigned tmp;
+
+ /* reduce the numbers to a simpler ratio */
+ tmp = gcd(*nom, *den);
+ *nom /= tmp;
+ *den /= tmp;
+
+ /* make sure nominator is large enough */
+ if (*nom < nom_min) {
+ tmp = (nom_min + *nom - 1) / *nom;
+ *nom *= tmp;
+ *den *= tmp;
}
- post_div = vco / target_clock;
- tmp = vco % target_clock;
-
- if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) {
- if (tmp)
- post_div++;
- } else {
- if (!tmp)
- post_div--;
+ /* make sure the denominator is large enough */
+ if (*den < den_min) {
+ tmp = (den_min + *den - 1) / *den;
+ *nom *= tmp;
+ *den *= tmp;
}
-
- if (post_div > pll->max_post_div)
- post_div = pll->max_post_div;
- else if (post_div < pll->min_post_div)
- post_div = pll->min_post_div;
-
- return post_div;
}
-#define MAX_TOLERANCE 10
-
+/**
+ * radeon_compute_pll_avivo - compute PLL paramaters
+ *
+ * @pll: information about the PLL
+ * @dot_clock_p: resulting pixel clock
+ * fb_div_p: resulting feedback divider
+ * frac_fb_div_p: fractional part of the feedback divider
+ * ref_div_p: resulting reference divider
+ * post_div_p: resulting reference divider
+ *
+ * Try to calculate the PLL parameters to generate the given frequency:
+ * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div)
+ */
void radeon_compute_pll_avivo(struct radeon_pll *pll,
u32 freq,
u32 *dot_clock_p,
@@ -867,53 +861,123 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
u32 *ref_div_p,
u32 *post_div_p)
{
- u32 target_clock = freq / 10;
- u32 post_div = avivo_get_post_div(pll, target_clock);
- u32 ref_div = pll->min_ref_div;
- u32 fb_div = 0, frac_fb_div = 0, tmp;
+ unsigned fb_div_min, fb_div_max, fb_div;
+ unsigned post_div_min, post_div_max, post_div;
+ unsigned ref_div_min, ref_div_max, ref_div;
+ unsigned post_div_best, diff_best;
+ unsigned nom, den, tmp;
- if (pll->flags & RADEON_PLL_USE_REF_DIV)
- ref_div = pll->reference_div;
+ /* determine allowed feedback divider range */
+ fb_div_min = pll->min_feedback_div;
+ fb_div_max = pll->max_feedback_div;
if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
- avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div);
- frac_fb_div = (100 * frac_fb_div) / pll->reference_freq;
- if (frac_fb_div >= 5) {
- frac_fb_div -= 5;
- frac_fb_div = frac_fb_div / 10;
- frac_fb_div++;
+ fb_div_min *= 10;
+ fb_div_max *= 10;
+ }
+
+ /* determine allowed ref divider range */
+ if (pll->flags & RADEON_PLL_USE_REF_DIV)
+ ref_div_min = pll->reference_div;
+ else
+ ref_div_min = pll->min_ref_div;
+ ref_div_max = pll->max_ref_div;
+
+ /* determine allowed post divider range */
+ if (pll->flags & RADEON_PLL_USE_POST_DIV) {
+ post_div_min = pll->post_div;
+ post_div_max = pll->post_div;
+ } else {
+ unsigned target_clock = freq / 10;
+ unsigned vco_min, vco_max;
+
+ if (pll->flags & RADEON_PLL_IS_LCD) {
+ vco_min = pll->lcd_pll_out_min;
+ vco_max = pll->lcd_pll_out_max;
+ } else {
+ vco_min = pll->pll_out_min;
+ vco_max = pll->pll_out_max;
}
- if (frac_fb_div >= 10) {
- fb_div++;
- frac_fb_div = 0;
+
+ post_div_min = vco_min / target_clock;
+ if ((target_clock * post_div_min) < vco_min)
+ ++post_div_min;
+ if (post_div_min < pll->min_post_div)
+ post_div_min = pll->min_post_div;
+
+ post_div_max = vco_max / target_clock;
+ if ((target_clock * post_div_max) > vco_max)
+ --post_div_max;
+ if (post_div_max > pll->max_post_div)
+ post_div_max = pll->max_post_div;
+ }
+
+ /* represent the searched ratio as fractional number */
+ nom = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? freq : freq / 10;
+ den = pll->reference_freq;
+
+ /* reduce the numbers to a simpler ratio */
+ avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
+
+ /* now search for a post divider */
+ if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP)
+ post_div_best = post_div_min;
+ else
+ post_div_best = post_div_max;
+ diff_best = ~0;
+
+ for (post_div = post_div_min; post_div <= post_div_max; ++post_div) {
+ unsigned diff = abs(den - den / post_div * post_div);
+ if (diff < diff_best || (diff == diff_best &&
+ !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) {
+
+ post_div_best = post_div;
+ diff_best = diff;
}
+ }
+ post_div = post_div_best;
+
+ /* get matching reference and feedback divider */
+ ref_div = max(den / post_div, 1u);
+ fb_div = nom;
+
+ /* we're almost done, but reference and feedback
+ divider might be to large now */
+
+ tmp = ref_div;
+
+ if (fb_div > fb_div_max) {
+ ref_div = ref_div * fb_div_max / fb_div;
+ fb_div = fb_div_max;
+ }
+
+ if (ref_div > ref_div_max) {
+ ref_div = ref_div_max;
+ fb_div = nom * ref_div_max / tmp;
+ }
+
+ /* reduce the numbers to a simpler ratio once more */
+ /* this also makes sure that the reference divider is large enough */
+ avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
+
+ /* and finally save the result */
+ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
+ *fb_div_p = fb_div / 10;
+ *frac_fb_div_p = fb_div % 10;
} else {
- while (ref_div <= pll->max_ref_div) {
- avivo_get_fb_div(pll, target_clock, post_div, ref_div,
- &fb_div, &frac_fb_div);
- if (frac_fb_div >= (pll->reference_freq / 2))
- fb_div++;
- frac_fb_div = 0;
- tmp = (pll->reference_freq * fb_div) / (post_div * ref_div);
- tmp = (tmp * 10000) / target_clock;
-
- if (tmp > (10000 + MAX_TOLERANCE))
- ref_div++;
- else if (tmp >= (10000 - MAX_TOLERANCE))
- break;
- else
- ref_div++;
- }
+ *fb_div_p = fb_div;
+ *frac_fb_div_p = 0;
}
- *dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) /
- (ref_div * post_div * 10);
- *fb_div_p = fb_div;
- *frac_fb_div_p = frac_fb_div;
+ *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) +
+ (pll->reference_freq * *frac_fb_div_p)) /
+ (ref_div * post_div * 10);
*ref_div_p = ref_div;
*post_div_p = post_div;
- DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n",
- *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div);
+
+ DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
+ freq, *dot_clock_p, *fb_div_p, *frac_fb_div_p,
+ ref_div, post_div);
}
/* pre-avivo */
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index f633c2782170..d0eba48dd74e 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -79,9 +79,11 @@
* 2.35.0 - Add CIK macrotile mode array query
* 2.36.0 - Fix CIK DCE tiling setup
* 2.37.0 - allow GS ring setup on r6xx/r7xx
+ * 2.38.0 - RADEON_GEM_OP (GET_INITIAL_DOMAIN, SET_INITIAL_DOMAIN),
+ * CIK: 1D and linear tiling modes contain valid PIPE_CONFIG
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 37
+#define KMS_DRIVER_MINOR 38
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index c37cb79a9489..a77b1c13ea43 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -288,7 +288,6 @@ static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq)
* @rdev: radeon device pointer
* @target_seq: sequence number(s) we want to wait for
* @intr: use interruptable sleep
- * @lock_ring: whether the ring should be locked or not
*
* Wait for the requested sequence number(s) to be written by any ring
* (all asics). Sequnce number array is indexed by ring id.
@@ -299,7 +298,7 @@ static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq)
* -EDEADLK is returned when a GPU lockup has been detected.
*/
static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq,
- bool intr, bool lock_ring)
+ bool intr)
{
uint64_t last_seq[RADEON_NUM_RINGS];
bool signaled;
@@ -358,9 +357,6 @@ static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq,
if (i != RADEON_NUM_RINGS)
continue;
- if (lock_ring)
- mutex_lock(&rdev->ring_lock);
-
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
if (!target_seq[i])
continue;
@@ -378,14 +374,9 @@ static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq,
/* remember that we need an reset */
rdev->needs_reset = true;
- if (lock_ring)
- mutex_unlock(&rdev->ring_lock);
wake_up_all(&rdev->fence_queue);
return -EDEADLK;
}
-
- if (lock_ring)
- mutex_unlock(&rdev->ring_lock);
}
}
return 0;
@@ -416,7 +407,7 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ)
return 0;
- r = radeon_fence_wait_seq(fence->rdev, seq, intr, true);
+ r = radeon_fence_wait_seq(fence->rdev, seq, intr);
if (r)
return r;
@@ -464,7 +455,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
if (num_rings == 0)
return -ENOENT;
- r = radeon_fence_wait_seq(rdev, seq, intr, true);
+ r = radeon_fence_wait_seq(rdev, seq, intr);
if (r) {
return r;
}
@@ -472,37 +463,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
}
/**
- * radeon_fence_wait_locked - wait for a fence to signal
- *
- * @fence: radeon fence object
- *
- * Wait for the requested fence to signal (all asics).
- * Returns 0 if the fence has passed, error for all other cases.
- */
-int radeon_fence_wait_locked(struct radeon_fence *fence)
-{
- uint64_t seq[RADEON_NUM_RINGS] = {};
- int r;
-
- if (fence == NULL) {
- WARN(1, "Querying an invalid fence : %p !\n", fence);
- return -EINVAL;
- }
-
- seq[fence->ring] = fence->seq;
- if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ)
- return 0;
-
- r = radeon_fence_wait_seq(fence->rdev, seq, false, false);
- if (r)
- return r;
-
- fence->seq = RADEON_FENCE_SIGNALED_SEQ;
- return 0;
-}
-
-/**
- * radeon_fence_wait_next_locked - wait for the next fence to signal
+ * radeon_fence_wait_next - wait for the next fence to signal
*
* @rdev: radeon device pointer
* @ring: ring index the fence is associated with
@@ -511,7 +472,7 @@ int radeon_fence_wait_locked(struct radeon_fence *fence)
* Returns 0 if the next fence has passed, error for all other cases.
* Caller must hold ring lock.
*/
-int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring)
+int radeon_fence_wait_next(struct radeon_device *rdev, int ring)
{
uint64_t seq[RADEON_NUM_RINGS] = {};
@@ -521,11 +482,11 @@ int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring)
already the last emited fence */
return -ENOENT;
}
- return radeon_fence_wait_seq(rdev, seq, false, false);
+ return radeon_fence_wait_seq(rdev, seq, false);
}
/**
- * radeon_fence_wait_empty_locked - wait for all fences to signal
+ * radeon_fence_wait_empty - wait for all fences to signal
*
* @rdev: radeon device pointer
* @ring: ring index the fence is associated with
@@ -534,7 +495,7 @@ int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring)
* Returns 0 if the fences have passed, error for all other cases.
* Caller must hold ring lock.
*/
-int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring)
+int radeon_fence_wait_empty(struct radeon_device *rdev, int ring)
{
uint64_t seq[RADEON_NUM_RINGS] = {};
int r;
@@ -543,7 +504,7 @@ int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring)
if (!seq[ring])
return 0;
- r = radeon_fence_wait_seq(rdev, seq, false, false);
+ r = radeon_fence_wait_seq(rdev, seq, false);
if (r) {
if (r == -EDEADLK)
return -EDEADLK;
@@ -794,7 +755,7 @@ void radeon_fence_driver_fini(struct radeon_device *rdev)
for (ring = 0; ring < RADEON_NUM_RINGS; ring++) {
if (!rdev->fence_drv[ring].initialized)
continue;
- r = radeon_fence_wait_empty_locked(rdev, ring);
+ r = radeon_fence_wait_empty(rdev, ring);
if (r) {
/* no need to trigger GPU reset as we are unloading */
radeon_fence_driver_force_completion(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index a8f9b463bf2a..2e723651069b 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -28,8 +28,6 @@
#include <drm/drmP.h>
#include <drm/radeon_drm.h>
#include "radeon.h"
-#include "radeon_reg.h"
-#include "radeon_trace.h"
/*
* GART
@@ -394,959 +392,3 @@ void radeon_gart_fini(struct radeon_device *rdev)
radeon_dummy_page_fini(rdev);
}
-
-/*
- * GPUVM
- * GPUVM is similar to the legacy gart on older asics, however
- * rather than there being a single global gart table
- * for the entire GPU, there are multiple VM page tables active
- * at any given time. The VM page tables can contain a mix
- * vram pages and system memory pages and system memory pages
- * can be mapped as snooped (cached system pages) or unsnooped
- * (uncached system pages).
- * Each VM has an ID associated with it and there is a page table
- * associated with each VMID. When execting a command buffer,
- * the kernel tells the the ring what VMID to use for that command
- * buffer. VMIDs are allocated dynamically as commands are submitted.
- * The userspace drivers maintain their own address space and the kernel
- * sets up their pages tables accordingly when they submit their
- * command buffers and a VMID is assigned.
- * Cayman/Trinity support up to 8 active VMs at any given time;
- * SI supports 16.
- */
-
-/*
- * vm helpers
- *
- * TODO bind a default page at vm initialization for default address
- */
-
-/**
- * radeon_vm_num_pde - return the number of page directory entries
- *
- * @rdev: radeon_device pointer
- *
- * Calculate the number of page directory entries (cayman+).
- */
-static unsigned radeon_vm_num_pdes(struct radeon_device *rdev)
-{
- return rdev->vm_manager.max_pfn >> RADEON_VM_BLOCK_SIZE;
-}
-
-/**
- * radeon_vm_directory_size - returns the size of the page directory in bytes
- *
- * @rdev: radeon_device pointer
- *
- * Calculate the size of the page directory in bytes (cayman+).
- */
-static unsigned radeon_vm_directory_size(struct radeon_device *rdev)
-{
- return RADEON_GPU_PAGE_ALIGN(radeon_vm_num_pdes(rdev) * 8);
-}
-
-/**
- * radeon_vm_manager_init - init the vm manager
- *
- * @rdev: radeon_device pointer
- *
- * Init the vm manager (cayman+).
- * Returns 0 for success, error for failure.
- */
-int radeon_vm_manager_init(struct radeon_device *rdev)
-{
- struct radeon_vm *vm;
- struct radeon_bo_va *bo_va;
- int r;
- unsigned size;
-
- if (!rdev->vm_manager.enabled) {
- /* allocate enough for 2 full VM pts */
- size = radeon_vm_directory_size(rdev);
- size += rdev->vm_manager.max_pfn * 8;
- size *= 2;
- r = radeon_sa_bo_manager_init(rdev, &rdev->vm_manager.sa_manager,
- RADEON_GPU_PAGE_ALIGN(size),
- RADEON_VM_PTB_ALIGN_SIZE,
- RADEON_GEM_DOMAIN_VRAM);
- if (r) {
- dev_err(rdev->dev, "failed to allocate vm bo (%dKB)\n",
- (rdev->vm_manager.max_pfn * 8) >> 10);
- return r;
- }
-
- r = radeon_asic_vm_init(rdev);
- if (r)
- return r;
-
- rdev->vm_manager.enabled = true;
-
- r = radeon_sa_bo_manager_start(rdev, &rdev->vm_manager.sa_manager);
- if (r)
- return r;
- }
-
- /* restore page table */
- list_for_each_entry(vm, &rdev->vm_manager.lru_vm, list) {
- if (vm->page_directory == NULL)
- continue;
-
- list_for_each_entry(bo_va, &vm->va, vm_list) {
- bo_va->valid = false;
- }
- }
- return 0;
-}
-
-/**
- * radeon_vm_free_pt - free the page table for a specific vm
- *
- * @rdev: radeon_device pointer
- * @vm: vm to unbind
- *
- * Free the page table of a specific vm (cayman+).
- *
- * Global and local mutex must be lock!
- */
-static void radeon_vm_free_pt(struct radeon_device *rdev,
- struct radeon_vm *vm)
-{
- struct radeon_bo_va *bo_va;
- int i;
-
- if (!vm->page_directory)
- return;
-
- list_del_init(&vm->list);
- radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence);
-
- list_for_each_entry(bo_va, &vm->va, vm_list) {
- bo_va->valid = false;
- }
-
- if (vm->page_tables == NULL)
- return;
-
- for (i = 0; i < radeon_vm_num_pdes(rdev); i++)
- radeon_sa_bo_free(rdev, &vm->page_tables[i], vm->fence);
-
- kfree(vm->page_tables);
-}
-
-/**
- * radeon_vm_manager_fini - tear down the vm manager
- *
- * @rdev: radeon_device pointer
- *
- * Tear down the VM manager (cayman+).
- */
-void radeon_vm_manager_fini(struct radeon_device *rdev)
-{
- struct radeon_vm *vm, *tmp;
- int i;
-
- if (!rdev->vm_manager.enabled)
- return;
-
- mutex_lock(&rdev->vm_manager.lock);
- /* free all allocated page tables */
- list_for_each_entry_safe(vm, tmp, &rdev->vm_manager.lru_vm, list) {
- mutex_lock(&vm->mutex);
- radeon_vm_free_pt(rdev, vm);
- mutex_unlock(&vm->mutex);
- }
- for (i = 0; i < RADEON_NUM_VM; ++i) {
- radeon_fence_unref(&rdev->vm_manager.active[i]);
- }
- radeon_asic_vm_fini(rdev);
- mutex_unlock(&rdev->vm_manager.lock);
-
- radeon_sa_bo_manager_suspend(rdev, &rdev->vm_manager.sa_manager);
- radeon_sa_bo_manager_fini(rdev, &rdev->vm_manager.sa_manager);
- rdev->vm_manager.enabled = false;
-}
-
-/**
- * radeon_vm_evict - evict page table to make room for new one
- *
- * @rdev: radeon_device pointer
- * @vm: VM we want to allocate something for
- *
- * Evict a VM from the lru, making sure that it isn't @vm. (cayman+).
- * Returns 0 for success, -ENOMEM for failure.
- *
- * Global and local mutex must be locked!
- */
-static int radeon_vm_evict(struct radeon_device *rdev, struct radeon_vm *vm)
-{
- struct radeon_vm *vm_evict;
-
- if (list_empty(&rdev->vm_manager.lru_vm))
- return -ENOMEM;
-
- vm_evict = list_first_entry(&rdev->vm_manager.lru_vm,
- struct radeon_vm, list);
- if (vm_evict == vm)
- return -ENOMEM;
-
- mutex_lock(&vm_evict->mutex);
- radeon_vm_free_pt(rdev, vm_evict);
- mutex_unlock(&vm_evict->mutex);
- return 0;
-}
-
-/**
- * radeon_vm_alloc_pt - allocates a page table for a VM
- *
- * @rdev: radeon_device pointer
- * @vm: vm to bind
- *
- * Allocate a page table for the requested vm (cayman+).
- * Returns 0 for success, error for failure.
- *
- * Global and local mutex must be locked!
- */
-int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm)
-{
- unsigned pd_size, pd_entries, pts_size;
- struct radeon_ib ib;
- int r;
-
- if (vm == NULL) {
- return -EINVAL;
- }
-
- if (vm->page_directory != NULL) {
- return 0;
- }
-
- pd_size = radeon_vm_directory_size(rdev);
- pd_entries = radeon_vm_num_pdes(rdev);
-
-retry:
- r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager,
- &vm->page_directory, pd_size,
- RADEON_VM_PTB_ALIGN_SIZE, false);
- if (r == -ENOMEM) {
- r = radeon_vm_evict(rdev, vm);
- if (r)
- return r;
- goto retry;
-
- } else if (r) {
- return r;
- }
-
- vm->pd_gpu_addr = radeon_sa_bo_gpu_addr(vm->page_directory);
-
- /* Initially clear the page directory */
- r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib,
- NULL, pd_entries * 2 + 64);
- if (r) {
- radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence);
- return r;
- }
-
- ib.length_dw = 0;
-
- radeon_asic_vm_set_page(rdev, &ib, vm->pd_gpu_addr,
- 0, pd_entries, 0, 0);
-
- radeon_semaphore_sync_to(ib.semaphore, vm->fence);
- r = radeon_ib_schedule(rdev, &ib, NULL);
- if (r) {
- radeon_ib_free(rdev, &ib);
- radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence);
- return r;
- }
- radeon_fence_unref(&vm->fence);
- vm->fence = radeon_fence_ref(ib.fence);
- radeon_ib_free(rdev, &ib);
- radeon_fence_unref(&vm->last_flush);
-
- /* allocate page table array */
- pts_size = radeon_vm_num_pdes(rdev) * sizeof(struct radeon_sa_bo *);
- vm->page_tables = kzalloc(pts_size, GFP_KERNEL);
-
- if (vm->page_tables == NULL) {
- DRM_ERROR("Cannot allocate memory for page table array\n");
- radeon_sa_bo_free(rdev, &vm->page_directory, vm->fence);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/**
- * radeon_vm_add_to_lru - add VMs page table to LRU list
- *
- * @rdev: radeon_device pointer
- * @vm: vm to add to LRU
- *
- * Add the allocated page table to the LRU list (cayman+).
- *
- * Global mutex must be locked!
- */
-void radeon_vm_add_to_lru(struct radeon_device *rdev, struct radeon_vm *vm)
-{
- list_del_init(&vm->list);
- list_add_tail(&vm->list, &rdev->vm_manager.lru_vm);
-}
-
-/**
- * radeon_vm_grab_id - allocate the next free VMID
- *
- * @rdev: radeon_device pointer
- * @vm: vm to allocate id for
- * @ring: ring we want to submit job to
- *
- * Allocate an id for the vm (cayman+).
- * Returns the fence we need to sync to (if any).
- *
- * Global and local mutex must be locked!
- */
-struct radeon_fence *radeon_vm_grab_id(struct radeon_device *rdev,
- struct radeon_vm *vm, int ring)
-{
- struct radeon_fence *best[RADEON_NUM_RINGS] = {};
- unsigned choices[2] = {};
- unsigned i;
-
- /* check if the id is still valid */
- if (vm->last_id_use && vm->last_id_use == rdev->vm_manager.active[vm->id])
- return NULL;
-
- /* we definately need to flush */
- radeon_fence_unref(&vm->last_flush);
-
- /* skip over VMID 0, since it is the system VM */
- for (i = 1; i < rdev->vm_manager.nvm; ++i) {
- struct radeon_fence *fence = rdev->vm_manager.active[i];
-
- if (fence == NULL) {
- /* found a free one */
- vm->id = i;
- trace_radeon_vm_grab_id(vm->id, ring);
- return NULL;
- }
-
- if (radeon_fence_is_earlier(fence, best[fence->ring])) {
- best[fence->ring] = fence;
- choices[fence->ring == ring ? 0 : 1] = i;
- }
- }
-
- for (i = 0; i < 2; ++i) {
- if (choices[i]) {
- vm->id = choices[i];
- trace_radeon_vm_grab_id(vm->id, ring);
- return rdev->vm_manager.active[choices[i]];
- }
- }
-
- /* should never happen */
- BUG();
- return NULL;
-}
-
-/**
- * radeon_vm_fence - remember fence for vm
- *
- * @rdev: radeon_device pointer
- * @vm: vm we want to fence
- * @fence: fence to remember
- *
- * Fence the vm (cayman+).
- * Set the fence used to protect page table and id.
- *
- * Global and local mutex must be locked!
- */
-void radeon_vm_fence(struct radeon_device *rdev,
- struct radeon_vm *vm,
- struct radeon_fence *fence)
-{
- radeon_fence_unref(&rdev->vm_manager.active[vm->id]);
- rdev->vm_manager.active[vm->id] = radeon_fence_ref(fence);
-
- radeon_fence_unref(&vm->fence);
- vm->fence = radeon_fence_ref(fence);
-
- radeon_fence_unref(&vm->last_id_use);
- vm->last_id_use = radeon_fence_ref(fence);
-}
-
-/**
- * radeon_vm_bo_find - find the bo_va for a specific vm & bo
- *
- * @vm: requested vm
- * @bo: requested buffer object
- *
- * Find @bo inside the requested vm (cayman+).
- * Search inside the @bos vm list for the requested vm
- * Returns the found bo_va or NULL if none is found
- *
- * Object has to be reserved!
- */
-struct radeon_bo_va *radeon_vm_bo_find(struct radeon_vm *vm,
- struct radeon_bo *bo)
-{
- struct radeon_bo_va *bo_va;
-
- list_for_each_entry(bo_va, &bo->va, bo_list) {
- if (bo_va->vm == vm) {
- return bo_va;
- }
- }
- return NULL;
-}
-
-/**
- * radeon_vm_bo_add - add a bo to a specific vm
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- * @bo: radeon buffer object
- *
- * Add @bo into the requested vm (cayman+).
- * Add @bo to the list of bos associated with the vm
- * Returns newly added bo_va or NULL for failure
- *
- * Object has to be reserved!
- */
-struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev,
- struct radeon_vm *vm,
- struct radeon_bo *bo)
-{
- struct radeon_bo_va *bo_va;
-
- bo_va = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
- if (bo_va == NULL) {
- return NULL;
- }
- bo_va->vm = vm;
- bo_va->bo = bo;
- bo_va->soffset = 0;
- bo_va->eoffset = 0;
- bo_va->flags = 0;
- bo_va->valid = false;
- bo_va->ref_count = 1;
- INIT_LIST_HEAD(&bo_va->bo_list);
- INIT_LIST_HEAD(&bo_va->vm_list);
-
- mutex_lock(&vm->mutex);
- list_add(&bo_va->vm_list, &vm->va);
- list_add_tail(&bo_va->bo_list, &bo->va);
- mutex_unlock(&vm->mutex);
-
- return bo_va;
-}
-
-/**
- * radeon_vm_bo_set_addr - set bos virtual address inside a vm
- *
- * @rdev: radeon_device pointer
- * @bo_va: bo_va to store the address
- * @soffset: requested offset of the buffer in the VM address space
- * @flags: attributes of pages (read/write/valid/etc.)
- *
- * Set offset of @bo_va (cayman+).
- * Validate and set the offset requested within the vm address space.
- * Returns 0 for success, error for failure.
- *
- * Object has to be reserved!
- */
-int radeon_vm_bo_set_addr(struct radeon_device *rdev,
- struct radeon_bo_va *bo_va,
- uint64_t soffset,
- uint32_t flags)
-{
- uint64_t size = radeon_bo_size(bo_va->bo);
- uint64_t eoffset, last_offset = 0;
- struct radeon_vm *vm = bo_va->vm;
- struct radeon_bo_va *tmp;
- struct list_head *head;
- unsigned last_pfn;
-
- if (soffset) {
- /* make sure object fit at this offset */
- eoffset = soffset + size;
- if (soffset >= eoffset) {
- return -EINVAL;
- }
-
- last_pfn = eoffset / RADEON_GPU_PAGE_SIZE;
- if (last_pfn > rdev->vm_manager.max_pfn) {
- dev_err(rdev->dev, "va above limit (0x%08X > 0x%08X)\n",
- last_pfn, rdev->vm_manager.max_pfn);
- return -EINVAL;
- }
-
- } else {
- eoffset = last_pfn = 0;
- }
-
- mutex_lock(&vm->mutex);
- head = &vm->va;
- last_offset = 0;
- list_for_each_entry(tmp, &vm->va, vm_list) {
- if (bo_va == tmp) {
- /* skip over currently modified bo */
- continue;
- }
-
- if (soffset >= last_offset && eoffset <= tmp->soffset) {
- /* bo can be added before this one */
- break;
- }
- if (eoffset > tmp->soffset && soffset < tmp->eoffset) {
- /* bo and tmp overlap, invalid offset */
- dev_err(rdev->dev, "bo %p va 0x%08X conflict with (bo %p 0x%08X 0x%08X)\n",
- bo_va->bo, (unsigned)bo_va->soffset, tmp->bo,
- (unsigned)tmp->soffset, (unsigned)tmp->eoffset);
- mutex_unlock(&vm->mutex);
- return -EINVAL;
- }
- last_offset = tmp->eoffset;
- head = &tmp->vm_list;
- }
-
- bo_va->soffset = soffset;
- bo_va->eoffset = eoffset;
- bo_va->flags = flags;
- bo_va->valid = false;
- list_move(&bo_va->vm_list, head);
-
- mutex_unlock(&vm->mutex);
- return 0;
-}
-
-/**
- * radeon_vm_map_gart - get the physical address of a gart page
- *
- * @rdev: radeon_device pointer
- * @addr: the unmapped addr
- *
- * Look up the physical address of the page that the pte resolves
- * to (cayman+).
- * Returns the physical address of the page.
- */
-uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr)
-{
- uint64_t result;
-
- /* page table offset */
- result = rdev->gart.pages_addr[addr >> PAGE_SHIFT];
-
- /* in case cpu page size != gpu page size*/
- result |= addr & (~PAGE_MASK);
-
- return result;
-}
-
-/**
- * radeon_vm_page_flags - translate page flags to what the hw uses
- *
- * @flags: flags comming from userspace
- *
- * Translate the flags the userspace ABI uses to hw flags.
- */
-static uint32_t radeon_vm_page_flags(uint32_t flags)
-{
- uint32_t hw_flags = 0;
- hw_flags |= (flags & RADEON_VM_PAGE_VALID) ? R600_PTE_VALID : 0;
- hw_flags |= (flags & RADEON_VM_PAGE_READABLE) ? R600_PTE_READABLE : 0;
- hw_flags |= (flags & RADEON_VM_PAGE_WRITEABLE) ? R600_PTE_WRITEABLE : 0;
- if (flags & RADEON_VM_PAGE_SYSTEM) {
- hw_flags |= R600_PTE_SYSTEM;
- hw_flags |= (flags & RADEON_VM_PAGE_SNOOPED) ? R600_PTE_SNOOPED : 0;
- }
- return hw_flags;
-}
-
-/**
- * radeon_vm_update_pdes - make sure that page directory is valid
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- * @start: start of GPU address range
- * @end: end of GPU address range
- *
- * Allocates new page tables if necessary
- * and updates the page directory (cayman+).
- * Returns 0 for success, error for failure.
- *
- * Global and local mutex must be locked!
- */
-static int radeon_vm_update_pdes(struct radeon_device *rdev,
- struct radeon_vm *vm,
- struct radeon_ib *ib,
- uint64_t start, uint64_t end)
-{
- static const uint32_t incr = RADEON_VM_PTE_COUNT * 8;
-
- uint64_t last_pde = ~0, last_pt = ~0;
- unsigned count = 0;
- uint64_t pt_idx;
- int r;
-
- start = (start / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
- end = (end / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
-
- /* walk over the address space and update the page directory */
- for (pt_idx = start; pt_idx <= end; ++pt_idx) {
- uint64_t pde, pt;
-
- if (vm->page_tables[pt_idx])
- continue;
-
-retry:
- r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager,
- &vm->page_tables[pt_idx],
- RADEON_VM_PTE_COUNT * 8,
- RADEON_GPU_PAGE_SIZE, false);
-
- if (r == -ENOMEM) {
- r = radeon_vm_evict(rdev, vm);
- if (r)
- return r;
- goto retry;
- } else if (r) {
- return r;
- }
-
- pde = vm->pd_gpu_addr + pt_idx * 8;
-
- pt = radeon_sa_bo_gpu_addr(vm->page_tables[pt_idx]);
-
- if (((last_pde + 8 * count) != pde) ||
- ((last_pt + incr * count) != pt)) {
-
- if (count) {
- radeon_asic_vm_set_page(rdev, ib, last_pde,
- last_pt, count, incr,
- R600_PTE_VALID);
-
- count *= RADEON_VM_PTE_COUNT;
- radeon_asic_vm_set_page(rdev, ib, last_pt, 0,
- count, 0, 0);
- }
-
- count = 1;
- last_pde = pde;
- last_pt = pt;
- } else {
- ++count;
- }
- }
-
- if (count) {
- radeon_asic_vm_set_page(rdev, ib, last_pde, last_pt, count,
- incr, R600_PTE_VALID);
-
- count *= RADEON_VM_PTE_COUNT;
- radeon_asic_vm_set_page(rdev, ib, last_pt, 0,
- count, 0, 0);
- }
-
- return 0;
-}
-
-/**
- * radeon_vm_update_ptes - make sure that page tables are valid
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- * @start: start of GPU address range
- * @end: end of GPU address range
- * @dst: destination address to map to
- * @flags: mapping flags
- *
- * Update the page tables in the range @start - @end (cayman+).
- *
- * Global and local mutex must be locked!
- */
-static void radeon_vm_update_ptes(struct radeon_device *rdev,
- struct radeon_vm *vm,
- struct radeon_ib *ib,
- uint64_t start, uint64_t end,
- uint64_t dst, uint32_t flags)
-{
- static const uint64_t mask = RADEON_VM_PTE_COUNT - 1;
-
- uint64_t last_pte = ~0, last_dst = ~0;
- unsigned count = 0;
- uint64_t addr;
-
- start = start / RADEON_GPU_PAGE_SIZE;
- end = end / RADEON_GPU_PAGE_SIZE;
-
- /* walk over the address space and update the page tables */
- for (addr = start; addr < end; ) {
- uint64_t pt_idx = addr >> RADEON_VM_BLOCK_SIZE;
- unsigned nptes;
- uint64_t pte;
-
- if ((addr & ~mask) == (end & ~mask))
- nptes = end - addr;
- else
- nptes = RADEON_VM_PTE_COUNT - (addr & mask);
-
- pte = radeon_sa_bo_gpu_addr(vm->page_tables[pt_idx]);
- pte += (addr & mask) * 8;
-
- if ((last_pte + 8 * count) != pte) {
-
- if (count) {
- radeon_asic_vm_set_page(rdev, ib, last_pte,
- last_dst, count,
- RADEON_GPU_PAGE_SIZE,
- flags);
- }
-
- count = nptes;
- last_pte = pte;
- last_dst = dst;
- } else {
- count += nptes;
- }
-
- addr += nptes;
- dst += nptes * RADEON_GPU_PAGE_SIZE;
- }
-
- if (count) {
- radeon_asic_vm_set_page(rdev, ib, last_pte,
- last_dst, count,
- RADEON_GPU_PAGE_SIZE, flags);
- }
-}
-
-/**
- * radeon_vm_bo_update - map a bo into the vm page table
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- * @bo: radeon buffer object
- * @mem: ttm mem
- *
- * Fill in the page table entries for @bo (cayman+).
- * Returns 0 for success, -EINVAL for failure.
- *
- * Object have to be reserved & global and local mutex must be locked!
- */
-int radeon_vm_bo_update(struct radeon_device *rdev,
- struct radeon_vm *vm,
- struct radeon_bo *bo,
- struct ttm_mem_reg *mem)
-{
- struct radeon_ib ib;
- struct radeon_bo_va *bo_va;
- unsigned nptes, npdes, ndw;
- uint64_t addr;
- int r;
-
- /* nothing to do if vm isn't bound */
- if (vm->page_directory == NULL)
- return 0;
-
- bo_va = radeon_vm_bo_find(vm, bo);
- if (bo_va == NULL) {
- dev_err(rdev->dev, "bo %p not in vm %p\n", bo, vm);
- return -EINVAL;
- }
-
- if (!bo_va->soffset) {
- dev_err(rdev->dev, "bo %p don't has a mapping in vm %p\n",
- bo, vm);
- return -EINVAL;
- }
-
- if ((bo_va->valid && mem) || (!bo_va->valid && mem == NULL))
- return 0;
-
- bo_va->flags &= ~RADEON_VM_PAGE_VALID;
- bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM;
- if (mem) {
- addr = mem->start << PAGE_SHIFT;
- if (mem->mem_type != TTM_PL_SYSTEM) {
- bo_va->flags |= RADEON_VM_PAGE_VALID;
- bo_va->valid = true;
- }
- if (mem->mem_type == TTM_PL_TT) {
- bo_va->flags |= RADEON_VM_PAGE_SYSTEM;
- } else {
- addr += rdev->vm_manager.vram_base_offset;
- }
- } else {
- addr = 0;
- bo_va->valid = false;
- }
-
- trace_radeon_vm_bo_update(bo_va);
-
- nptes = radeon_bo_ngpu_pages(bo);
-
- /* assume two extra pdes in case the mapping overlaps the borders */
- npdes = (nptes >> RADEON_VM_BLOCK_SIZE) + 2;
-
- /* padding, etc. */
- ndw = 64;
-
- if (RADEON_VM_BLOCK_SIZE > 11)
- /* reserve space for one header for every 2k dwords */
- ndw += (nptes >> 11) * 4;
- else
- /* reserve space for one header for
- every (1 << BLOCK_SIZE) entries */
- ndw += (nptes >> RADEON_VM_BLOCK_SIZE) * 4;
-
- /* reserve space for pte addresses */
- ndw += nptes * 2;
-
- /* reserve space for one header for every 2k dwords */
- ndw += (npdes >> 11) * 4;
-
- /* reserve space for pde addresses */
- ndw += npdes * 2;
-
- /* reserve space for clearing new page tables */
- ndw += npdes * 2 * RADEON_VM_PTE_COUNT;
-
- /* update too big for an IB */
- if (ndw > 0xfffff)
- return -ENOMEM;
-
- r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib, NULL, ndw * 4);
- if (r)
- return r;
- ib.length_dw = 0;
-
- r = radeon_vm_update_pdes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset);
- if (r) {
- radeon_ib_free(rdev, &ib);
- return r;
- }
-
- radeon_vm_update_ptes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset,
- addr, radeon_vm_page_flags(bo_va->flags));
-
- radeon_semaphore_sync_to(ib.semaphore, vm->fence);
- r = radeon_ib_schedule(rdev, &ib, NULL);
- if (r) {
- radeon_ib_free(rdev, &ib);
- return r;
- }
- radeon_fence_unref(&vm->fence);
- vm->fence = radeon_fence_ref(ib.fence);
- radeon_ib_free(rdev, &ib);
- radeon_fence_unref(&vm->last_flush);
-
- return 0;
-}
-
-/**
- * radeon_vm_bo_rmv - remove a bo to a specific vm
- *
- * @rdev: radeon_device pointer
- * @bo_va: requested bo_va
- *
- * Remove @bo_va->bo from the requested vm (cayman+).
- * Remove @bo_va->bo from the list of bos associated with the bo_va->vm and
- * remove the ptes for @bo_va in the page table.
- * Returns 0 for success.
- *
- * Object have to be reserved!
- */
-int radeon_vm_bo_rmv(struct radeon_device *rdev,
- struct radeon_bo_va *bo_va)
-{
- int r = 0;
-
- mutex_lock(&rdev->vm_manager.lock);
- mutex_lock(&bo_va->vm->mutex);
- if (bo_va->soffset) {
- r = radeon_vm_bo_update(rdev, bo_va->vm, bo_va->bo, NULL);
- }
- mutex_unlock(&rdev->vm_manager.lock);
- list_del(&bo_va->vm_list);
- mutex_unlock(&bo_va->vm->mutex);
- list_del(&bo_va->bo_list);
-
- kfree(bo_va);
- return r;
-}
-
-/**
- * radeon_vm_bo_invalidate - mark the bo as invalid
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- * @bo: radeon buffer object
- *
- * Mark @bo as invalid (cayman+).
- */
-void radeon_vm_bo_invalidate(struct radeon_device *rdev,
- struct radeon_bo *bo)
-{
- struct radeon_bo_va *bo_va;
-
- list_for_each_entry(bo_va, &bo->va, bo_list) {
- bo_va->valid = false;
- }
-}
-
-/**
- * radeon_vm_init - initialize a vm instance
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- *
- * Init @vm fields (cayman+).
- */
-void radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
-{
- vm->id = 0;
- vm->fence = NULL;
- vm->last_flush = NULL;
- vm->last_id_use = NULL;
- mutex_init(&vm->mutex);
- INIT_LIST_HEAD(&vm->list);
- INIT_LIST_HEAD(&vm->va);
-}
-
-/**
- * radeon_vm_fini - tear down a vm instance
- *
- * @rdev: radeon_device pointer
- * @vm: requested vm
- *
- * Tear down @vm (cayman+).
- * Unbind the VM and remove all bos from the vm bo list
- */
-void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm)
-{
- struct radeon_bo_va *bo_va, *tmp;
- int r;
-
- mutex_lock(&rdev->vm_manager.lock);
- mutex_lock(&vm->mutex);
- radeon_vm_free_pt(rdev, vm);
- mutex_unlock(&rdev->vm_manager.lock);
-
- if (!list_empty(&vm->va)) {
- dev_err(rdev->dev, "still active bo inside vm\n");
- }
- list_for_each_entry_safe(bo_va, tmp, &vm->va, vm_list) {
- list_del_init(&bo_va->vm_list);
- r = radeon_bo_reserve(bo_va->bo, false);
- if (!r) {
- list_del_init(&bo_va->bo_list);
- radeon_bo_unreserve(bo_va->bo);
- kfree(bo_va);
- }
- }
- radeon_fence_unref(&vm->fence);
- radeon_fence_unref(&vm->last_flush);
- radeon_fence_unref(&vm->last_id_use);
- mutex_unlock(&vm->mutex);
-}
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index b96c819024b3..d09650c1d720 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -344,18 +344,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
}
robj = gem_to_radeon_bo(gobj);
r = radeon_bo_wait(robj, &cur_placement, true);
- switch (cur_placement) {
- case TTM_PL_VRAM:
- args->domain = RADEON_GEM_DOMAIN_VRAM;
- break;
- case TTM_PL_TT:
- args->domain = RADEON_GEM_DOMAIN_GTT;
- break;
- case TTM_PL_SYSTEM:
- args->domain = RADEON_GEM_DOMAIN_CPU;
- default:
- break;
- }
+ args->domain = radeon_mem_type_to_domain(cur_placement);
drm_gem_object_unreference_unlocked(gobj);
r = radeon_gem_handle_lockup(rdev, r);
return r;
@@ -533,6 +522,42 @@ out:
return r;
}
+int radeon_gem_op_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct drm_radeon_gem_op *args = data;
+ struct drm_gem_object *gobj;
+ struct radeon_bo *robj;
+ int r;
+
+ gobj = drm_gem_object_lookup(dev, filp, args->handle);
+ if (gobj == NULL) {
+ return -ENOENT;
+ }
+ robj = gem_to_radeon_bo(gobj);
+ r = radeon_bo_reserve(robj, false);
+ if (unlikely(r))
+ goto out;
+
+ switch (args->op) {
+ case RADEON_GEM_OP_GET_INITIAL_DOMAIN:
+ args->value = robj->initial_domain;
+ break;
+ case RADEON_GEM_OP_SET_INITIAL_DOMAIN:
+ robj->initial_domain = args->value & (RADEON_GEM_DOMAIN_VRAM |
+ RADEON_GEM_DOMAIN_GTT |
+ RADEON_GEM_DOMAIN_CPU);
+ break;
+ default:
+ r = -EINVAL;
+ }
+
+ radeon_bo_unreserve(robj);
+out:
+ drm_gem_object_unreference_unlocked(gobj);
+ return r;
+}
+
int radeon_mode_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 66ed3ea71440..3e49342a20e6 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -441,6 +441,9 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
case RADEON_CS_RING_UVD:
*value = rdev->ring[R600_RING_TYPE_UVD_INDEX].ready;
break;
+ case RADEON_CS_RING_VCE:
+ *value = rdev->ring[TN_RING_TYPE_VCE1_INDEX].ready;
+ break;
default:
return -EINVAL;
}
@@ -485,6 +488,27 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
else
*value = rdev->pm.default_sclk * 10;
break;
+ case RADEON_INFO_VCE_FW_VERSION:
+ *value = rdev->vce.fw_version;
+ break;
+ case RADEON_INFO_VCE_FB_VERSION:
+ *value = rdev->vce.fb_version;
+ break;
+ case RADEON_INFO_NUM_BYTES_MOVED:
+ value = (uint32_t*)&value64;
+ value_size = sizeof(uint64_t);
+ value64 = atomic64_read(&rdev->num_bytes_moved);
+ break;
+ case RADEON_INFO_VRAM_USAGE:
+ value = (uint32_t*)&value64;
+ value_size = sizeof(uint64_t);
+ value64 = atomic64_read(&rdev->vram_usage);
+ break;
+ case RADEON_INFO_GTT_USAGE:
+ value = (uint32_t*)&value64;
+ value_size = sizeof(uint64_t);
+ value64 = atomic64_read(&rdev->gtt_usage);
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
@@ -543,7 +567,9 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
return -ENOMEM;
}
- radeon_vm_init(rdev, &fpriv->vm);
+ r = radeon_vm_init(rdev, &fpriv->vm);
+ if (r)
+ return r;
r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false);
if (r)
@@ -624,6 +650,7 @@ void radeon_driver_preclose_kms(struct drm_device *dev,
if (rdev->cmask_filp == file_priv)
rdev->cmask_filp = NULL;
radeon_uvd_free_handles(rdev, file_priv);
+ radeon_vce_free_handles(rdev, file_priv);
}
/*
@@ -818,5 +845,6 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
};
int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms);
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 0b158f98d287..cafb1ccf2ec3 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -385,7 +385,7 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc,
DRM_DEBUG_KMS("\n");
/* no fb bound */
- if (!atomic && !crtc->fb) {
+ if (!atomic && !crtc->primary->fb) {
DRM_DEBUG_KMS("No FB bound\n");
return 0;
}
@@ -395,8 +395,8 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc,
target_fb = fb;
}
else {
- radeon_fb = to_radeon_framebuffer(crtc->fb);
- target_fb = crtc->fb;
+ radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
+ target_fb = crtc->primary->fb;
}
switch (target_fb->bits_per_pixel) {
@@ -444,7 +444,7 @@ retry:
* We don't shutdown the display controller because new buffer
* will end up in same spot.
*/
- if (!atomic && fb && fb != crtc->fb) {
+ if (!atomic && fb && fb != crtc->primary->fb) {
struct radeon_bo *old_rbo;
unsigned long nsize, osize;
@@ -555,7 +555,7 @@ retry:
WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset);
WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch);
- if (!atomic && fb && fb != crtc->fb) {
+ if (!atomic && fb && fb != crtc->primary->fb) {
radeon_fb = to_radeon_framebuffer(fb);
rbo = gem_to_radeon_bo(radeon_fb->obj);
r = radeon_bo_reserve(rbo, false);
@@ -599,7 +599,7 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
}
}
- switch (crtc->fb->bits_per_pixel) {
+ switch (crtc->primary->fb->bits_per_pixel) {
case 8:
format = 2;
break;
@@ -1087,12 +1087,12 @@ static void radeon_crtc_commit(struct drm_crtc *crtc)
static void radeon_crtc_disable(struct drm_crtc *crtc)
{
radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
- if (crtc->fb) {
+ if (crtc->primary->fb) {
int r;
struct radeon_framebuffer *radeon_fb;
struct radeon_bo *rbo;
- radeon_fb = to_radeon_framebuffer(crtc->fb);
+ radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
rbo = gem_to_radeon_bo(radeon_fb->obj);
r = radeon_bo_reserve(rbo, false);
if (unlikely(r))
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 402dbe32c234..832d9fa1a4c4 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -192,6 +192,7 @@ struct radeon_i2c_chan {
struct i2c_algo_dp_aux_data dp;
} algo;
struct radeon_i2c_bus_rec rec;
+ struct drm_dp_aux aux;
};
/* mostly for macs, but really any system without connector tables */
@@ -690,6 +691,9 @@ 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 radeon_dp_set_rx_power_state(struct drm_connector *connector,
+ u8 power_state);
+extern void radeon_dp_aux_init(struct radeon_connector *radeon_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_disp_eng_pll_init(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 08595cf90b01..19bec0dbfa38 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -56,11 +56,36 @@ static void radeon_bo_clear_va(struct radeon_bo *bo)
}
}
+static void radeon_update_memory_usage(struct radeon_bo *bo,
+ unsigned mem_type, int sign)
+{
+ struct radeon_device *rdev = bo->rdev;
+ u64 size = (u64)bo->tbo.num_pages << PAGE_SHIFT;
+
+ switch (mem_type) {
+ case TTM_PL_TT:
+ if (sign > 0)
+ atomic64_add(size, &rdev->gtt_usage);
+ else
+ atomic64_sub(size, &rdev->gtt_usage);
+ break;
+ case TTM_PL_VRAM:
+ if (sign > 0)
+ atomic64_add(size, &rdev->vram_usage);
+ else
+ atomic64_sub(size, &rdev->vram_usage);
+ break;
+ }
+}
+
static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)
{
struct radeon_bo *bo;
bo = container_of(tbo, struct radeon_bo, tbo);
+
+ radeon_update_memory_usage(bo, bo->tbo.mem.mem_type, -1);
+
mutex_lock(&bo->rdev->gem.mutex);
list_del_init(&bo->list);
mutex_unlock(&bo->rdev->gem.mutex);
@@ -79,7 +104,7 @@ bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo)
void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
{
- u32 c = 0;
+ u32 c = 0, i;
rbo->placement.fpfn = 0;
rbo->placement.lpfn = 0;
@@ -106,6 +131,17 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
rbo->placement.num_placement = c;
rbo->placement.num_busy_placement = c;
+
+ /*
+ * Use two-ended allocation depending on the buffer size to
+ * improve fragmentation quality.
+ * 512kb was measured as the most optimal number.
+ */
+ if (rbo->tbo.mem.size > 512 * 1024) {
+ for (i = 0; i < c; i++) {
+ rbo->placements[i] |= TTM_PL_FLAG_TOPDOWN;
+ }
+ }
}
int radeon_bo_create(struct radeon_device *rdev,
@@ -120,7 +156,6 @@ int radeon_bo_create(struct radeon_device *rdev,
size = ALIGN(size, PAGE_SIZE);
- rdev->mman.bdev.dev_mapping = rdev->ddev->dev_mapping;
if (kernel) {
type = ttm_bo_type_kernel;
} else if (sg) {
@@ -145,6 +180,9 @@ int radeon_bo_create(struct radeon_device *rdev,
bo->surface_reg = -1;
INIT_LIST_HEAD(&bo->list);
INIT_LIST_HEAD(&bo->va);
+ bo->initial_domain = domain & (RADEON_GEM_DOMAIN_VRAM |
+ RADEON_GEM_DOMAIN_GTT |
+ RADEON_GEM_DOMAIN_CPU);
radeon_ttm_placement_from_domain(bo, domain);
/* Kernel allocation are uninterruptible */
down_read(&rdev->pm.mclk_lock);
@@ -338,39 +376,105 @@ void radeon_bo_fini(struct radeon_device *rdev)
arch_phys_wc_del(rdev->mc.vram_mtrr);
}
-void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
- struct list_head *head)
+/* Returns how many bytes TTM can move per IB.
+ */
+static u64 radeon_bo_get_threshold_for_moves(struct radeon_device *rdev)
{
- if (lobj->written) {
- list_add(&lobj->tv.head, head);
- } else {
- list_add_tail(&lobj->tv.head, head);
- }
+ u64 real_vram_size = rdev->mc.real_vram_size;
+ u64 vram_usage = atomic64_read(&rdev->vram_usage);
+
+ /* This function is based on the current VRAM usage.
+ *
+ * - If all of VRAM is free, allow relocating the number of bytes that
+ * is equal to 1/4 of the size of VRAM for this IB.
+
+ * - If more than one half of VRAM is occupied, only allow relocating
+ * 1 MB of data for this IB.
+ *
+ * - From 0 to one half of used VRAM, the threshold decreases
+ * linearly.
+ * __________________
+ * 1/4 of -|\ |
+ * VRAM | \ |
+ * | \ |
+ * | \ |
+ * | \ |
+ * | \ |
+ * | \ |
+ * | \________|1 MB
+ * |----------------|
+ * VRAM 0 % 100 %
+ * used used
+ *
+ * Note: It's a threshold, not a limit. The threshold must be crossed
+ * for buffer relocations to stop, so any buffer of an arbitrary size
+ * can be moved as long as the threshold isn't crossed before
+ * the relocation takes place. We don't want to disable buffer
+ * relocations completely.
+ *
+ * The idea is that buffers should be placed in VRAM at creation time
+ * and TTM should only do a minimum number of relocations during
+ * command submission. In practice, you need to submit at least
+ * a dozen IBs to move all buffers to VRAM if they are in GTT.
+ *
+ * Also, things can get pretty crazy under memory pressure and actual
+ * VRAM usage can change a lot, so playing safe even at 50% does
+ * consistently increase performance.
+ */
+
+ u64 half_vram = real_vram_size >> 1;
+ u64 half_free_vram = vram_usage >= half_vram ? 0 : half_vram - vram_usage;
+ u64 bytes_moved_threshold = half_free_vram >> 1;
+ return max(bytes_moved_threshold, 1024*1024ull);
}
-int radeon_bo_list_validate(struct ww_acquire_ctx *ticket,
+int radeon_bo_list_validate(struct radeon_device *rdev,
+ struct ww_acquire_ctx *ticket,
struct list_head *head, int ring)
{
- struct radeon_bo_list *lobj;
+ struct radeon_cs_reloc *lobj;
struct radeon_bo *bo;
- u32 domain;
int r;
+ u64 bytes_moved = 0, initial_bytes_moved;
+ u64 bytes_moved_threshold = radeon_bo_get_threshold_for_moves(rdev);
r = ttm_eu_reserve_buffers(ticket, head);
if (unlikely(r != 0)) {
return r;
}
+
list_for_each_entry(lobj, head, tv.head) {
- bo = lobj->bo;
+ bo = lobj->robj;
if (!bo->pin_count) {
- domain = lobj->domain;
-
+ u32 domain = lobj->domain;
+ u32 current_domain =
+ radeon_mem_type_to_domain(bo->tbo.mem.mem_type);
+
+ /* Check if this buffer will be moved and don't move it
+ * if we have moved too many buffers for this IB already.
+ *
+ * Note that this allows moving at least one buffer of
+ * any size, because it doesn't take the current "bo"
+ * into account. We don't want to disallow buffer moves
+ * completely.
+ */
+ if (current_domain != RADEON_GEM_DOMAIN_CPU &&
+ (domain & current_domain) == 0 && /* will be moved */
+ bytes_moved > bytes_moved_threshold) {
+ /* don't move it */
+ domain = current_domain;
+ }
+
retry:
radeon_ttm_placement_from_domain(bo, domain);
if (ring == R600_RING_TYPE_UVD_INDEX)
radeon_uvd_force_into_uvd_segment(bo);
- r = ttm_bo_validate(&bo->tbo, &bo->placement,
- true, false);
+
+ initial_bytes_moved = atomic64_read(&rdev->num_bytes_moved);
+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
+ bytes_moved += atomic64_read(&rdev->num_bytes_moved) -
+ initial_bytes_moved;
+
if (unlikely(r)) {
if (r != -ERESTARTSYS && domain != lobj->alt_domain) {
domain = lobj->alt_domain;
@@ -564,14 +668,23 @@ int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
}
void radeon_bo_move_notify(struct ttm_buffer_object *bo,
- struct ttm_mem_reg *mem)
+ struct ttm_mem_reg *new_mem)
{
struct radeon_bo *rbo;
+
if (!radeon_ttm_bo_is_radeon_bo(bo))
return;
+
rbo = container_of(bo, struct radeon_bo, tbo);
radeon_bo_check_tiling(rbo, 0, 1);
radeon_vm_bo_invalidate(rbo->rdev, rbo);
+
+ /* update statistics */
+ if (!new_mem)
+ return;
+
+ radeon_update_memory_usage(rbo, bo->mem.mem_type, -1);
+ radeon_update_memory_usage(rbo, new_mem->mem_type, 1);
}
int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 209b11150263..9e7b25a0629d 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -138,9 +138,8 @@ extern int radeon_bo_evict_vram(struct radeon_device *rdev);
extern void radeon_bo_force_delete(struct radeon_device *rdev);
extern int radeon_bo_init(struct radeon_device *rdev);
extern void radeon_bo_fini(struct radeon_device *rdev);
-extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
- struct list_head *head);
-extern int radeon_bo_list_validate(struct ww_acquire_ctx *ticket,
+extern int radeon_bo_list_validate(struct radeon_device *rdev,
+ struct ww_acquire_ctx *ticket,
struct list_head *head, int ring);
extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
struct vm_area_struct *vma);
@@ -151,7 +150,7 @@ extern void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
extern int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
bool force_drop);
extern void radeon_bo_move_notify(struct ttm_buffer_object *bo,
- struct ttm_mem_reg *mem);
+ struct ttm_mem_reg *new_mem);
extern int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
extern int radeon_bo_get_surface_reg(struct radeon_bo *bo);
@@ -181,7 +180,7 @@ extern int radeon_sa_bo_manager_suspend(struct radeon_device *rdev,
extern int radeon_sa_bo_new(struct radeon_device *rdev,
struct radeon_sa_manager *sa_manager,
struct radeon_sa_bo **sa_bo,
- unsigned size, unsigned align, bool block);
+ unsigned size, unsigned align);
extern void radeon_sa_bo_free(struct radeon_device *rdev,
struct radeon_sa_bo **sa_bo,
struct radeon_fence *fence);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 8e8153e471c2..ee738a524639 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -260,7 +260,7 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev)
if (!ring->ready) {
continue;
}
- r = radeon_fence_wait_empty_locked(rdev, i);
+ r = radeon_fence_wait_empty(rdev, i);
if (r) {
/* needs a GPU reset dont reset here */
mutex_unlock(&rdev->ring_lock);
@@ -826,6 +826,9 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
/* no need to reprogram if nothing changed unless we are on BTC+ */
if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
+ /* vce just modifies an existing state so force a change */
+ if (ps->vce_active != rdev->pm.dpm.vce_active)
+ goto force;
if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
/* for pre-BTC and APUs if the num crtcs changed but state is the same,
* all we need to do is update the display configuration.
@@ -862,16 +865,21 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
}
}
+force:
if (radeon_dpm == 1) {
printk("switching from power state:\n");
radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
printk("switching to power state:\n");
radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
}
+
mutex_lock(&rdev->ddev->struct_mutex);
down_write(&rdev->pm.mclk_lock);
mutex_lock(&rdev->ring_lock);
+ /* update whether vce is active */
+ ps->vce_active = rdev->pm.dpm.vce_active;
+
ret = radeon_dpm_pre_set_power_state(rdev);
if (ret)
goto done;
@@ -888,7 +896,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
for (i = 0; i < RADEON_NUM_RINGS; i++) {
struct radeon_ring *ring = &rdev->ring[i];
if (ring->ready)
- radeon_fence_wait_empty_locked(rdev, i);
+ radeon_fence_wait_empty(rdev, i);
}
/* program the new power state */
@@ -935,8 +943,6 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
if (enable) {
mutex_lock(&rdev->pm.mutex);
rdev->pm.dpm.uvd_active = true;
- /* disable this for now */
-#if 0
if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
@@ -946,7 +952,6 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
else
-#endif
dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
rdev->pm.dpm.state = dpm_state;
mutex_unlock(&rdev->pm.mutex);
@@ -960,6 +965,23 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
}
}
+void radeon_dpm_enable_vce(struct radeon_device *rdev, bool enable)
+{
+ if (enable) {
+ mutex_lock(&rdev->pm.mutex);
+ rdev->pm.dpm.vce_active = true;
+ /* XXX select vce level based on ring/task */
+ rdev->pm.dpm.vce_level = RADEON_VCE_LEVEL_AC_ALL;
+ mutex_unlock(&rdev->pm.mutex);
+ } else {
+ mutex_lock(&rdev->pm.mutex);
+ rdev->pm.dpm.vce_active = false;
+ mutex_unlock(&rdev->pm.mutex);
+ }
+
+ radeon_pm_compute_clocks(rdev);
+}
+
static void radeon_pm_suspend_old(struct radeon_device *rdev)
{
mutex_lock(&rdev->pm.mutex);
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 15e44a7281ab..f8050f5429e2 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -63,7 +63,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring,
{
int r;
- r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256, true);
+ r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256);
if (r) {
dev_err(rdev->dev, "failed to get a new IB (%d)\n", r);
return r;
@@ -145,6 +145,13 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
return r;
}
+ /* grab a vm id if necessary */
+ if (ib->vm) {
+ struct radeon_fence *vm_id_fence;
+ vm_id_fence = radeon_vm_grab_id(rdev, ib->vm, ib->ring);
+ radeon_semaphore_sync_to(ib->semaphore, vm_id_fence);
+ }
+
/* sync with other rings */
r = radeon_semaphore_sync_rings(rdev, ib->semaphore, ib->ring);
if (r) {
@@ -153,11 +160,9 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
return r;
}
- /* if we can't remember our last VM flush then flush now! */
- /* XXX figure out why we have to flush for every IB */
- if (ib->vm /*&& !ib->vm->last_flush*/) {
- radeon_ring_vm_flush(rdev, ib->ring, ib->vm);
- }
+ if (ib->vm)
+ radeon_vm_flush(rdev, ib->vm, ib->ring);
+
if (const_ib) {
radeon_ring_ib_execute(rdev, const_ib->ring, const_ib);
radeon_semaphore_free(rdev, &const_ib->semaphore, NULL);
@@ -172,10 +177,10 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
if (const_ib) {
const_ib->fence = radeon_fence_ref(ib->fence);
}
- /* we just flushed the VM, remember that */
- if (ib->vm && !ib->vm->last_flush) {
- ib->vm->last_flush = radeon_fence_ref(ib->fence);
- }
+
+ if (ib->vm)
+ radeon_vm_fence(rdev, ib->vm, ib->fence);
+
radeon_ring_unlock_commit(rdev, ring);
return 0;
}
@@ -257,6 +262,7 @@ int radeon_ib_ring_tests(struct radeon_device *rdev)
r = radeon_ib_test(rdev, i, ring);
if (r) {
ring->ready = false;
+ rdev->needs_reset = false;
if (i == RADEON_RING_TYPE_GFX_INDEX) {
/* oh, oh, that's really bad */
@@ -342,13 +348,17 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev,
*/
void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring)
{
- ring->rptr = radeon_ring_get_rptr(rdev, ring);
+ uint32_t rptr = radeon_ring_get_rptr(rdev, ring);
+
/* This works because ring_size is a power of 2 */
- ring->ring_free_dw = (ring->rptr + (ring->ring_size / 4));
+ ring->ring_free_dw = rptr + (ring->ring_size / 4);
ring->ring_free_dw -= ring->wptr;
ring->ring_free_dw &= ring->ptr_mask;
if (!ring->ring_free_dw) {
+ /* this is an empty ring */
ring->ring_free_dw = ring->ring_size / 4;
+ /* update lockup info to avoid false positive */
+ radeon_ring_lockup_update(rdev, ring);
}
}
@@ -372,19 +382,13 @@ int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *ring, unsi
/* Align requested size with padding so unlock_commit can
* pad safely */
radeon_ring_free_size(rdev, ring);
- if (ring->ring_free_dw == (ring->ring_size / 4)) {
- /* This is an empty ring update lockup info to avoid
- * false positive.
- */
- radeon_ring_lockup_update(ring);
- }
ndw = (ndw + ring->align_mask) & ~ring->align_mask;
while (ndw > (ring->ring_free_dw - 1)) {
radeon_ring_free_size(rdev, ring);
if (ndw < ring->ring_free_dw) {
break;
}
- r = radeon_fence_wait_next_locked(rdev, ring->idx);
+ r = radeon_fence_wait_next(rdev, ring->idx);
if (r)
return r;
}
@@ -478,39 +482,17 @@ void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *rin
}
/**
- * radeon_ring_force_activity - add some nop packets to the ring
- *
- * @rdev: radeon_device pointer
- * @ring: radeon_ring structure holding ring information
- *
- * Add some nop packets to the ring to force activity (all asics).
- * Used for lockup detection to see if the rptr is advancing.
- */
-void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *ring)
-{
- int r;
-
- radeon_ring_free_size(rdev, ring);
- if (ring->rptr == ring->wptr) {
- r = radeon_ring_alloc(rdev, ring, 1);
- if (!r) {
- radeon_ring_write(ring, ring->nop);
- radeon_ring_commit(rdev, ring);
- }
- }
-}
-
-/**
* radeon_ring_lockup_update - update lockup variables
*
* @ring: radeon_ring structure holding ring information
*
* Update the last rptr value and timestamp (all asics).
*/
-void radeon_ring_lockup_update(struct radeon_ring *ring)
+void radeon_ring_lockup_update(struct radeon_device *rdev,
+ struct radeon_ring *ring)
{
- ring->last_rptr = ring->rptr;
- ring->last_activity = jiffies;
+ atomic_set(&ring->last_rptr, radeon_ring_get_rptr(rdev, ring));
+ atomic64_set(&ring->last_activity, jiffies_64);
}
/**
@@ -518,40 +500,23 @@ void radeon_ring_lockup_update(struct radeon_ring *ring)
* @rdev: radeon device structure
* @ring: radeon_ring structure holding ring information
*
- * We don't need to initialize the lockup tracking information as we will either
- * have CP rptr to a different value of jiffies wrap around which will force
- * initialization of the lockup tracking informations.
- *
- * A possible false positivie is if we get call after while and last_cp_rptr ==
- * the current CP rptr, even if it's unlikely it might happen. To avoid this
- * if the elapsed time since last call is bigger than 2 second than we return
- * false and update the tracking information. Due to this the caller must call
- * radeon_ring_test_lockup several time in less than 2sec for lockup to be reported
- * the fencing code should be cautious about that.
- *
- * Caller should write to the ring to force CP to do something so we don't get
- * false positive when CP is just gived nothing to do.
- *
- **/
+ */
bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
{
- unsigned long cjiffies, elapsed;
+ uint32_t rptr = radeon_ring_get_rptr(rdev, ring);
+ uint64_t last = atomic64_read(&ring->last_activity);
+ uint64_t elapsed;
- cjiffies = jiffies;
- if (!time_after(cjiffies, ring->last_activity)) {
- /* likely a wrap around */
- radeon_ring_lockup_update(ring);
+ if (rptr != atomic_read(&ring->last_rptr)) {
+ /* ring is still working, no lockup */
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- ring->rptr = radeon_ring_get_rptr(rdev, ring);
- if (ring->rptr != ring->last_rptr) {
- /* CP is still working no lockup */
- radeon_ring_lockup_update(ring);
- return false;
- }
- elapsed = jiffies_to_msecs(cjiffies - ring->last_activity);
+
+ elapsed = jiffies_to_msecs(jiffies_64 - last);
if (radeon_lockup_timeout && elapsed >= radeon_lockup_timeout) {
- dev_err(rdev->dev, "GPU lockup CP stall for more than %lumsec\n", elapsed);
+ dev_err(rdev->dev, "ring %d stalled for more than %llumsec\n",
+ ring->idx, elapsed);
return true;
}
/* give a chance to the GPU ... */
@@ -709,7 +674,7 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig
if (radeon_debugfs_ring_init(rdev, ring)) {
DRM_ERROR("Failed to register debugfs file for rings !\n");
}
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return 0;
}
@@ -780,8 +745,6 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data)
seq_printf(m, "driver's copy of the wptr: 0x%08x [%5d]\n",
ring->wptr, ring->wptr);
- seq_printf(m, "driver's copy of the rptr: 0x%08x [%5d]\n",
- ring->rptr, ring->rptr);
seq_printf(m, "last semaphore signal addr : 0x%016llx\n",
ring->last_semaphore_signal_addr);
seq_printf(m, "last semaphore wait addr : 0x%016llx\n",
@@ -814,6 +777,8 @@ static int cayman_cp2_index = CAYMAN_RING_TYPE_CP2_INDEX;
static int radeon_dma1_index = R600_RING_TYPE_DMA_INDEX;
static int radeon_dma2_index = CAYMAN_RING_TYPE_DMA1_INDEX;
static int r600_uvd_index = R600_RING_TYPE_UVD_INDEX;
+static int si_vce1_index = TN_RING_TYPE_VCE1_INDEX;
+static int si_vce2_index = TN_RING_TYPE_VCE2_INDEX;
static struct drm_info_list radeon_debugfs_ring_info_list[] = {
{"radeon_ring_gfx", radeon_debugfs_ring_info, 0, &radeon_gfx_index},
@@ -822,6 +787,8 @@ static struct drm_info_list radeon_debugfs_ring_info_list[] = {
{"radeon_ring_dma1", radeon_debugfs_ring_info, 0, &radeon_dma1_index},
{"radeon_ring_dma2", radeon_debugfs_ring_info, 0, &radeon_dma2_index},
{"radeon_ring_uvd", radeon_debugfs_ring_info, 0, &r600_uvd_index},
+ {"radeon_ring_vce1", radeon_debugfs_ring_info, 0, &si_vce1_index},
+ {"radeon_ring_vce2", radeon_debugfs_ring_info, 0, &si_vce2_index},
};
static int radeon_debugfs_sa_info(struct seq_file *m, void *data)
diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c
index c0625805cdd7..adcf3e2f07da 100644
--- a/drivers/gpu/drm/radeon/radeon_sa.c
+++ b/drivers/gpu/drm/radeon/radeon_sa.c
@@ -312,7 +312,7 @@ static bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager,
int radeon_sa_bo_new(struct radeon_device *rdev,
struct radeon_sa_manager *sa_manager,
struct radeon_sa_bo **sa_bo,
- unsigned size, unsigned align, bool block)
+ unsigned size, unsigned align)
{
struct radeon_fence *fences[RADEON_NUM_RINGS];
unsigned tries[RADEON_NUM_RINGS];
@@ -353,14 +353,11 @@ int radeon_sa_bo_new(struct radeon_device *rdev,
r = radeon_fence_wait_any(rdev, fences, false);
spin_lock(&sa_manager->wq.lock);
/* if we have nothing to wait for block */
- if (r == -ENOENT && block) {
+ if (r == -ENOENT) {
r = wait_event_interruptible_locked(
sa_manager->wq,
radeon_sa_event(sa_manager, size, align)
);
-
- } else if (r == -ENOENT) {
- r = -ENOMEM;
}
} while (!r);
diff --git a/drivers/gpu/drm/radeon/radeon_semaphore.c b/drivers/gpu/drm/radeon/radeon_semaphore.c
index 9006b32d5eed..dbd6bcde92de 100644
--- a/drivers/gpu/drm/radeon/radeon_semaphore.c
+++ b/drivers/gpu/drm/radeon/radeon_semaphore.c
@@ -42,7 +42,7 @@ int radeon_semaphore_create(struct radeon_device *rdev,
return -ENOMEM;
}
r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &(*semaphore)->sa_bo,
- 8 * RADEON_NUM_SYNCS, 8, true);
+ 8 * RADEON_NUM_SYNCS, 8);
if (r) {
kfree(*semaphore);
*semaphore = NULL;
@@ -147,7 +147,9 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev,
if (++count > RADEON_NUM_SYNCS) {
/* not enough room, wait manually */
- radeon_fence_wait_locked(fence);
+ r = radeon_fence_wait(fence, false);
+ if (r)
+ return r;
continue;
}
@@ -161,7 +163,9 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev,
if (!radeon_semaphore_emit_signal(rdev, i, semaphore)) {
/* signaling wasn't successful wait manually */
radeon_ring_undo(&rdev->ring[i]);
- radeon_fence_wait_locked(fence);
+ r = radeon_fence_wait(fence, false);
+ if (r)
+ return r;
continue;
}
@@ -169,7 +173,9 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev,
if (!radeon_semaphore_emit_wait(rdev, ring, semaphore)) {
/* waiting wasn't successful wait manually */
radeon_ring_undo(&rdev->ring[i]);
- radeon_fence_wait_locked(fence);
+ r = radeon_fence_wait(fence, false);
+ if (r)
+ return r;
continue;
}
diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
index 12e8099a0823..3a13e0d1055c 100644
--- a/drivers/gpu/drm/radeon/radeon_test.c
+++ b/drivers/gpu/drm/radeon/radeon_test.c
@@ -257,20 +257,36 @@ static int radeon_test_create_and_emit_fence(struct radeon_device *rdev,
struct radeon_ring *ring,
struct radeon_fence **fence)
{
+ uint32_t handle = ring->idx ^ 0xdeafbeef;
int r;
if (ring->idx == R600_RING_TYPE_UVD_INDEX) {
- r = radeon_uvd_get_create_msg(rdev, ring->idx, 1, NULL);
+ r = radeon_uvd_get_create_msg(rdev, ring->idx, handle, NULL);
if (r) {
DRM_ERROR("Failed to get dummy create msg\n");
return r;
}
- r = radeon_uvd_get_destroy_msg(rdev, ring->idx, 1, fence);
+ r = radeon_uvd_get_destroy_msg(rdev, ring->idx, handle, fence);
if (r) {
DRM_ERROR("Failed to get dummy destroy msg\n");
return r;
}
+
+ } else if (ring->idx == TN_RING_TYPE_VCE1_INDEX ||
+ ring->idx == TN_RING_TYPE_VCE2_INDEX) {
+ r = radeon_vce_get_create_msg(rdev, ring->idx, handle, NULL);
+ if (r) {
+ DRM_ERROR("Failed to get dummy create msg\n");
+ return r;
+ }
+
+ r = radeon_vce_get_destroy_msg(rdev, ring->idx, handle, fence);
+ if (r) {
+ DRM_ERROR("Failed to get dummy destroy msg\n");
+ return r;
+ }
+
} else {
r = radeon_ring_lock(rdev, ring, 64);
if (r) {
@@ -486,6 +502,16 @@ out_cleanup:
printk(KERN_WARNING "Error while testing ring sync (%d).\n", r);
}
+static bool radeon_test_sync_possible(struct radeon_ring *ringA,
+ struct radeon_ring *ringB)
+{
+ if (ringA->idx == TN_RING_TYPE_VCE2_INDEX &&
+ ringB->idx == TN_RING_TYPE_VCE1_INDEX)
+ return false;
+
+ return true;
+}
+
void radeon_test_syncing(struct radeon_device *rdev)
{
int i, j, k;
@@ -500,6 +526,9 @@ void radeon_test_syncing(struct radeon_device *rdev)
if (!ringB->ready)
continue;
+ if (!radeon_test_sync_possible(ringA, ringB))
+ continue;
+
DRM_INFO("Testing syncing between rings %d and %d...\n", i, j);
radeon_test_ring_sync(rdev, ringA, ringB);
@@ -511,6 +540,12 @@ void radeon_test_syncing(struct radeon_device *rdev)
if (!ringC->ready)
continue;
+ if (!radeon_test_sync_possible(ringA, ringC))
+ continue;
+
+ if (!radeon_test_sync_possible(ringB, ringC))
+ continue;
+
DRM_INFO("Testing syncing between rings %d, %d and %d...\n", i, j, k);
radeon_test_ring_sync2(rdev, ringA, ringB, ringC);
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 040a2a10ea17..c8a8a5144ec1 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -406,8 +406,14 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
if (r) {
memcpy:
r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem);
+ if (r) {
+ return r;
+ }
}
- return r;
+
+ /* update statistics */
+ atomic64_add((u64)bo->num_pages << PAGE_SHIFT, &rdev->num_bytes_moved);
+ return 0;
}
static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
@@ -701,7 +707,9 @@ int radeon_ttm_init(struct radeon_device *rdev)
/* No others user of address space so set it to 0 */
r = ttm_bo_device_init(&rdev->mman.bdev,
rdev->mman.bo_global_ref.ref.object,
- &radeon_bo_driver, DRM_FILE_PAGE_OFFSET,
+ &radeon_bo_driver,
+ rdev->ddev->anon_inode->i_mapping,
+ DRM_FILE_PAGE_OFFSET,
rdev->need_dma32);
if (r) {
DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
@@ -742,7 +750,6 @@ int radeon_ttm_init(struct radeon_device *rdev)
}
DRM_INFO("radeon: %uM of GTT memory ready.\n",
(unsigned)(rdev->mc.gtt_size / (1024 * 1024)));
- rdev->mman.bdev.dev_mapping = rdev->ddev->dev_mapping;
r = radeon_ttm_debugfs_init(rdev);
if (r) {
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c
index 3e6804b2b2ef..5748bdaeacce 100644
--- a/drivers/gpu/drm/radeon/radeon_uvd.c
+++ b/drivers/gpu/drm/radeon/radeon_uvd.c
@@ -455,7 +455,7 @@ static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p,
}
reloc = p->relocs_ptr[(idx / 4)];
- start = reloc->lobj.gpu_offset;
+ start = reloc->gpu_offset;
end = start + radeon_bo_size(reloc->robj);
start += offset;
@@ -807,8 +807,7 @@ void radeon_uvd_note_usage(struct radeon_device *rdev)
(rdev->pm.dpm.hd != hd)) {
rdev->pm.dpm.sd = sd;
rdev->pm.dpm.hd = hd;
- /* disable this for now */
- /*streams_changed = true;*/
+ streams_changed = true;
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
new file mode 100644
index 000000000000..76e9904bc537
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_vce.c
@@ -0,0 +1,699 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Christian König <christian.koenig@amd.com>
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include <drm/drm.h>
+
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "sid.h"
+
+/* 1 second timeout */
+#define VCE_IDLE_TIMEOUT_MS 1000
+
+/* Firmware Names */
+#define FIRMWARE_BONAIRE "radeon/BONAIRE_vce.bin"
+
+MODULE_FIRMWARE(FIRMWARE_BONAIRE);
+
+static void radeon_vce_idle_work_handler(struct work_struct *work);
+
+/**
+ * radeon_vce_init - allocate memory, load vce firmware
+ *
+ * @rdev: radeon_device pointer
+ *
+ * First step to get VCE online, allocate memory and load the firmware
+ */
+int radeon_vce_init(struct radeon_device *rdev)
+{
+ static const char *fw_version = "[ATI LIB=VCEFW,";
+ static const char *fb_version = "[ATI LIB=VCEFWSTATS,";
+ unsigned long size;
+ const char *fw_name, *c;
+ uint8_t start, mid, end;
+ int i, r;
+
+ INIT_DELAYED_WORK(&rdev->vce.idle_work, radeon_vce_idle_work_handler);
+
+ switch (rdev->family) {
+ case CHIP_BONAIRE:
+ case CHIP_KAVERI:
+ case CHIP_KABINI:
+ fw_name = FIRMWARE_BONAIRE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ r = request_firmware(&rdev->vce_fw, fw_name, rdev->dev);
+ if (r) {
+ dev_err(rdev->dev, "radeon_vce: Can't load firmware \"%s\"\n",
+ fw_name);
+ return r;
+ }
+
+ /* search for firmware version */
+
+ size = rdev->vce_fw->size - strlen(fw_version) - 9;
+ c = rdev->vce_fw->data;
+ for (;size > 0; --size, ++c)
+ if (strncmp(c, fw_version, strlen(fw_version)) == 0)
+ break;
+
+ if (size == 0)
+ return -EINVAL;
+
+ c += strlen(fw_version);
+ if (sscanf(c, "%2hhd.%2hhd.%2hhd]", &start, &mid, &end) != 3)
+ return -EINVAL;
+
+ /* search for feedback version */
+
+ size = rdev->vce_fw->size - strlen(fb_version) - 3;
+ c = rdev->vce_fw->data;
+ for (;size > 0; --size, ++c)
+ if (strncmp(c, fb_version, strlen(fb_version)) == 0)
+ break;
+
+ if (size == 0)
+ return -EINVAL;
+
+ c += strlen(fb_version);
+ if (sscanf(c, "%2u]", &rdev->vce.fb_version) != 1)
+ return -EINVAL;
+
+ DRM_INFO("Found VCE firmware/feedback version %hhd.%hhd.%hhd / %d!\n",
+ start, mid, end, rdev->vce.fb_version);
+
+ rdev->vce.fw_version = (start << 24) | (mid << 16) | (end << 8);
+
+ /* we can only work with this fw version for now */
+ if (rdev->vce.fw_version != ((40 << 24) | (2 << 16) | (2 << 8)))
+ return -EINVAL;
+
+ /* allocate firmware, stack and heap BO */
+
+ size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size) +
+ RADEON_VCE_STACK_SIZE + RADEON_VCE_HEAP_SIZE;
+ r = radeon_bo_create(rdev, size, PAGE_SIZE, true,
+ RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->vce.vcpu_bo);
+ if (r) {
+ dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r);
+ return r;
+ }
+
+ r = radeon_bo_reserve(rdev->vce.vcpu_bo, false);
+ if (r) {
+ radeon_bo_unref(&rdev->vce.vcpu_bo);
+ dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r);
+ return r;
+ }
+
+ r = radeon_bo_pin(rdev->vce.vcpu_bo, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->vce.gpu_addr);
+ radeon_bo_unreserve(rdev->vce.vcpu_bo);
+ if (r) {
+ radeon_bo_unref(&rdev->vce.vcpu_bo);
+ dev_err(rdev->dev, "(%d) VCE bo pin failed\n", r);
+ return r;
+ }
+
+ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
+ atomic_set(&rdev->vce.handles[i], 0);
+ rdev->vce.filp[i] = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * radeon_vce_fini - free memory
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Last step on VCE teardown, free firmware memory
+ */
+void radeon_vce_fini(struct radeon_device *rdev)
+{
+ if (rdev->vce.vcpu_bo == NULL)
+ return;
+
+ radeon_bo_unref(&rdev->vce.vcpu_bo);
+
+ release_firmware(rdev->vce_fw);
+}
+
+/**
+ * radeon_vce_suspend - unpin VCE fw memory
+ *
+ * @rdev: radeon_device pointer
+ *
+ */
+int radeon_vce_suspend(struct radeon_device *rdev)
+{
+ int i;
+
+ if (rdev->vce.vcpu_bo == NULL)
+ return 0;
+
+ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
+ if (atomic_read(&rdev->vce.handles[i]))
+ break;
+
+ if (i == RADEON_MAX_VCE_HANDLES)
+ return 0;
+
+ /* TODO: suspending running encoding sessions isn't supported */
+ return -EINVAL;
+}
+
+/**
+ * radeon_vce_resume - pin VCE fw memory
+ *
+ * @rdev: radeon_device pointer
+ *
+ */
+int radeon_vce_resume(struct radeon_device *rdev)
+{
+ void *cpu_addr;
+ int r;
+
+ if (rdev->vce.vcpu_bo == NULL)
+ return -EINVAL;
+
+ r = radeon_bo_reserve(rdev->vce.vcpu_bo, false);
+ if (r) {
+ dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r);
+ return r;
+ }
+
+ r = radeon_bo_kmap(rdev->vce.vcpu_bo, &cpu_addr);
+ if (r) {
+ radeon_bo_unreserve(rdev->vce.vcpu_bo);
+ dev_err(rdev->dev, "(%d) VCE map failed\n", r);
+ return r;
+ }
+
+ memcpy(cpu_addr, rdev->vce_fw->data, rdev->vce_fw->size);
+
+ radeon_bo_kunmap(rdev->vce.vcpu_bo);
+
+ radeon_bo_unreserve(rdev->vce.vcpu_bo);
+
+ return 0;
+}
+
+/**
+ * radeon_vce_idle_work_handler - power off VCE
+ *
+ * @work: pointer to work structure
+ *
+ * power of VCE when it's not used any more
+ */
+static void radeon_vce_idle_work_handler(struct work_struct *work)
+{
+ struct radeon_device *rdev =
+ container_of(work, struct radeon_device, vce.idle_work.work);
+
+ if ((radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE1_INDEX) == 0) &&
+ (radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE2_INDEX) == 0)) {
+ if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+ radeon_dpm_enable_vce(rdev, false);
+ } else {
+ radeon_set_vce_clocks(rdev, 0, 0);
+ }
+ } else {
+ schedule_delayed_work(&rdev->vce.idle_work,
+ msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS));
+ }
+}
+
+/**
+ * radeon_vce_note_usage - power up VCE
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Make sure VCE is powerd up when we want to use it
+ */
+void radeon_vce_note_usage(struct radeon_device *rdev)
+{
+ bool streams_changed = false;
+ bool set_clocks = !cancel_delayed_work_sync(&rdev->vce.idle_work);
+ set_clocks &= schedule_delayed_work(&rdev->vce.idle_work,
+ msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS));
+
+ if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+ /* XXX figure out if the streams changed */
+ streams_changed = false;
+ }
+
+ if (set_clocks || streams_changed) {
+ if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+ radeon_dpm_enable_vce(rdev, true);
+ } else {
+ radeon_set_vce_clocks(rdev, 53300, 40000);
+ }
+ }
+}
+
+/**
+ * radeon_vce_free_handles - free still open VCE handles
+ *
+ * @rdev: radeon_device pointer
+ * @filp: drm file pointer
+ *
+ * Close all VCE handles still open by this file pointer
+ */
+void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp)
+{
+ int i, r;
+ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
+ uint32_t handle = atomic_read(&rdev->vce.handles[i]);
+ if (!handle || rdev->vce.filp[i] != filp)
+ continue;
+
+ radeon_vce_note_usage(rdev);
+
+ r = radeon_vce_get_destroy_msg(rdev, TN_RING_TYPE_VCE1_INDEX,
+ handle, NULL);
+ if (r)
+ DRM_ERROR("Error destroying VCE handle (%d)!\n", r);
+
+ rdev->vce.filp[i] = NULL;
+ atomic_set(&rdev->vce.handles[i], 0);
+ }
+}
+
+/**
+ * radeon_vce_get_create_msg - generate a VCE create msg
+ *
+ * @rdev: radeon_device pointer
+ * @ring: ring we should submit the msg to
+ * @handle: VCE session handle to use
+ * @fence: optional fence to return
+ *
+ * Open up a stream for HW test
+ */
+int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
+ uint32_t handle, struct radeon_fence **fence)
+{
+ const unsigned ib_size_dw = 1024;
+ struct radeon_ib ib;
+ uint64_t dummy;
+ int i, r;
+
+ r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4);
+ if (r) {
+ DRM_ERROR("radeon: failed to get ib (%d).\n", r);
+ return r;
+ }
+
+ dummy = ib.gpu_addr + 1024;
+
+ /* stitch together an VCE create msg */
+ ib.length_dw = 0;
+ ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
+ ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
+ ib.ptr[ib.length_dw++] = handle;
+
+ ib.ptr[ib.length_dw++] = 0x00000030; /* len */
+ ib.ptr[ib.length_dw++] = 0x01000001; /* create cmd */
+ ib.ptr[ib.length_dw++] = 0x00000000;
+ ib.ptr[ib.length_dw++] = 0x00000042;
+ ib.ptr[ib.length_dw++] = 0x0000000a;
+ ib.ptr[ib.length_dw++] = 0x00000001;
+ ib.ptr[ib.length_dw++] = 0x00000080;
+ ib.ptr[ib.length_dw++] = 0x00000060;
+ ib.ptr[ib.length_dw++] = 0x00000100;
+ ib.ptr[ib.length_dw++] = 0x00000100;
+ ib.ptr[ib.length_dw++] = 0x0000000c;
+ ib.ptr[ib.length_dw++] = 0x00000000;
+
+ ib.ptr[ib.length_dw++] = 0x00000014; /* len */
+ ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */
+ ib.ptr[ib.length_dw++] = upper_32_bits(dummy);
+ ib.ptr[ib.length_dw++] = dummy;
+ ib.ptr[ib.length_dw++] = 0x00000001;
+
+ for (i = ib.length_dw; i < ib_size_dw; ++i)
+ ib.ptr[i] = 0x0;
+
+ r = radeon_ib_schedule(rdev, &ib, NULL);
+ if (r) {
+ DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
+ }
+
+ if (fence)
+ *fence = radeon_fence_ref(ib.fence);
+
+ radeon_ib_free(rdev, &ib);
+
+ return r;
+}
+
+/**
+ * radeon_vce_get_destroy_msg - generate a VCE destroy msg
+ *
+ * @rdev: radeon_device pointer
+ * @ring: ring we should submit the msg to
+ * @handle: VCE session handle to use
+ * @fence: optional fence to return
+ *
+ * Close up a stream for HW test or if userspace failed to do so
+ */
+int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
+ uint32_t handle, struct radeon_fence **fence)
+{
+ const unsigned ib_size_dw = 1024;
+ struct radeon_ib ib;
+ uint64_t dummy;
+ int i, r;
+
+ r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4);
+ if (r) {
+ DRM_ERROR("radeon: failed to get ib (%d).\n", r);
+ return r;
+ }
+
+ dummy = ib.gpu_addr + 1024;
+
+ /* stitch together an VCE destroy msg */
+ ib.length_dw = 0;
+ ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
+ ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
+ ib.ptr[ib.length_dw++] = handle;
+
+ ib.ptr[ib.length_dw++] = 0x00000014; /* len */
+ ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */
+ ib.ptr[ib.length_dw++] = upper_32_bits(dummy);
+ ib.ptr[ib.length_dw++] = dummy;
+ ib.ptr[ib.length_dw++] = 0x00000001;
+
+ ib.ptr[ib.length_dw++] = 0x00000008; /* len */
+ ib.ptr[ib.length_dw++] = 0x02000001; /* destroy cmd */
+
+ for (i = ib.length_dw; i < ib_size_dw; ++i)
+ ib.ptr[i] = 0x0;
+
+ r = radeon_ib_schedule(rdev, &ib, NULL);
+ if (r) {
+ DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
+ }
+
+ if (fence)
+ *fence = radeon_fence_ref(ib.fence);
+
+ radeon_ib_free(rdev, &ib);
+
+ return r;
+}
+
+/**
+ * radeon_vce_cs_reloc - command submission relocation
+ *
+ * @p: parser context
+ * @lo: address of lower dword
+ * @hi: address of higher dword
+ *
+ * Patch relocation inside command stream with real buffer address
+ */
+int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi)
+{
+ struct radeon_cs_chunk *relocs_chunk;
+ uint64_t offset;
+ unsigned idx;
+
+ relocs_chunk = &p->chunks[p->chunk_relocs_idx];
+ offset = radeon_get_ib_value(p, lo);
+ idx = radeon_get_ib_value(p, hi);
+
+ if (idx >= relocs_chunk->length_dw) {
+ DRM_ERROR("Relocs at %d after relocations chunk end %d !\n",
+ idx, relocs_chunk->length_dw);
+ return -EINVAL;
+ }
+
+ offset += p->relocs_ptr[(idx / 4)]->gpu_offset;
+
+ p->ib.ptr[lo] = offset & 0xFFFFFFFF;
+ p->ib.ptr[hi] = offset >> 32;
+
+ return 0;
+}
+
+/**
+ * radeon_vce_cs_parse - parse and validate the command stream
+ *
+ * @p: parser context
+ *
+ */
+int radeon_vce_cs_parse(struct radeon_cs_parser *p)
+{
+ uint32_t handle = 0;
+ bool destroy = false;
+ int i, r;
+
+ while (p->idx < p->chunks[p->chunk_ib_idx].length_dw) {
+ uint32_t len = radeon_get_ib_value(p, p->idx);
+ uint32_t cmd = radeon_get_ib_value(p, p->idx + 1);
+
+ if ((len < 8) || (len & 3)) {
+ DRM_ERROR("invalid VCE command length (%d)!\n", len);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case 0x00000001: // session
+ handle = radeon_get_ib_value(p, p->idx + 2);
+ break;
+
+ case 0x00000002: // task info
+ case 0x01000001: // create
+ case 0x04000001: // config extension
+ case 0x04000002: // pic control
+ case 0x04000005: // rate control
+ case 0x04000007: // motion estimation
+ case 0x04000008: // rdo
+ break;
+
+ case 0x03000001: // encode
+ r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9);
+ if (r)
+ return r;
+
+ r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11);
+ if (r)
+ return r;
+ break;
+
+ case 0x02000001: // destroy
+ destroy = true;
+ break;
+
+ case 0x05000001: // context buffer
+ case 0x05000004: // video bitstream buffer
+ case 0x05000005: // feedback buffer
+ r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2);
+ if (r)
+ return r;
+ break;
+
+ default:
+ DRM_ERROR("invalid VCE command (0x%x)!\n", cmd);
+ return -EINVAL;
+ }
+
+ p->idx += len / 4;
+ }
+
+ if (destroy) {
+ /* IB contains a destroy msg, free the handle */
+ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
+ atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0);
+
+ return 0;
+ }
+
+ /* create or encode, validate the handle */
+ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
+ if (atomic_read(&p->rdev->vce.handles[i]) == handle)
+ return 0;
+ }
+
+ /* handle not found try to alloc a new one */
+ for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
+ if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) {
+ p->rdev->vce.filp[i] = p->filp;
+ return 0;
+ }
+ }
+
+ DRM_ERROR("No more free VCE handles!\n");
+ return -EINVAL;
+}
+
+/**
+ * radeon_vce_semaphore_emit - emit a semaphore command
+ *
+ * @rdev: radeon_device pointer
+ * @ring: engine to use
+ * @semaphore: address of semaphore
+ * @emit_wait: true=emit wait, false=emit signal
+ *
+ */
+bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
+ struct radeon_ring *ring,
+ struct radeon_semaphore *semaphore,
+ bool emit_wait)
+{
+ uint64_t addr = semaphore->gpu_addr;
+
+ radeon_ring_write(ring, VCE_CMD_SEMAPHORE);
+ radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF);
+ radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF);
+ radeon_ring_write(ring, 0x01003000 | (emit_wait ? 1 : 0));
+ if (!emit_wait)
+ radeon_ring_write(ring, VCE_CMD_END);
+
+ return true;
+}
+
+/**
+ * radeon_vce_ib_execute - execute indirect buffer
+ *
+ * @rdev: radeon_device pointer
+ * @ib: the IB to execute
+ *
+ */
+void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+ struct radeon_ring *ring = &rdev->ring[ib->ring];
+ radeon_ring_write(ring, VCE_CMD_IB);
+ radeon_ring_write(ring, ib->gpu_addr);
+ radeon_ring_write(ring, upper_32_bits(ib->gpu_addr));
+ radeon_ring_write(ring, ib->length_dw);
+}
+
+/**
+ * radeon_vce_fence_emit - add a fence command to the ring
+ *
+ * @rdev: radeon_device pointer
+ * @fence: the fence
+ *
+ */
+void radeon_vce_fence_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence)
+{
+ struct radeon_ring *ring = &rdev->ring[fence->ring];
+ uint32_t addr = rdev->fence_drv[fence->ring].gpu_addr;
+
+ radeon_ring_write(ring, VCE_CMD_FENCE);
+ radeon_ring_write(ring, addr);
+ radeon_ring_write(ring, upper_32_bits(addr));
+ radeon_ring_write(ring, fence->seq);
+ radeon_ring_write(ring, VCE_CMD_TRAP);
+ radeon_ring_write(ring, VCE_CMD_END);
+}
+
+/**
+ * radeon_vce_ring_test - test if VCE ring is working
+ *
+ * @rdev: radeon_device pointer
+ * @ring: the engine to test on
+ *
+ */
+int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+ uint32_t rptr = vce_v1_0_get_rptr(rdev, ring);
+ unsigned i;
+ int r;
+
+ r = radeon_ring_lock(rdev, ring, 16);
+ if (r) {
+ DRM_ERROR("radeon: vce failed to lock ring %d (%d).\n",
+ ring->idx, r);
+ return r;
+ }
+ radeon_ring_write(ring, VCE_CMD_END);
+ radeon_ring_unlock_commit(rdev, ring);
+
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (vce_v1_0_get_rptr(rdev, ring) != rptr)
+ break;
+ DRM_UDELAY(1);
+ }
+
+ if (i < rdev->usec_timeout) {
+ DRM_INFO("ring test on %d succeeded in %d usecs\n",
+ ring->idx, i);
+ } else {
+ DRM_ERROR("radeon: ring %d test failed\n",
+ ring->idx);
+ r = -ETIMEDOUT;
+ }
+
+ return r;
+}
+
+/**
+ * radeon_vce_ib_test - test if VCE IBs are working
+ *
+ * @rdev: radeon_device pointer
+ * @ring: the engine to test on
+ *
+ */
+int radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+ struct radeon_fence *fence = NULL;
+ int r;
+
+ r = radeon_vce_get_create_msg(rdev, ring->idx, 1, NULL);
+ if (r) {
+ DRM_ERROR("radeon: failed to get create msg (%d).\n", r);
+ goto error;
+ }
+
+ r = radeon_vce_get_destroy_msg(rdev, ring->idx, 1, &fence);
+ if (r) {
+ DRM_ERROR("radeon: failed to get destroy ib (%d).\n", r);
+ goto error;
+ }
+
+ r = radeon_fence_wait(fence, false);
+ if (r) {
+ DRM_ERROR("radeon: fence wait failed (%d).\n", r);
+ } else {
+ DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
+ }
+error:
+ radeon_fence_unref(&fence);
+ return r;
+}
diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c
new file mode 100644
index 000000000000..2aae6ce49d32
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_vm.c
@@ -0,0 +1,966 @@
+/*
+ * Copyright 2008 Advanced Micro Devices, Inc.
+ * Copyright 2008 Red Hat Inc.
+ * Copyright 2009 Jerome Glisse.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alex Deucher
+ * Jerome Glisse
+ */
+#include <drm/drmP.h>
+#include <drm/radeon_drm.h>
+#include "radeon.h"
+#include "radeon_trace.h"
+
+/*
+ * GPUVM
+ * GPUVM is similar to the legacy gart on older asics, however
+ * rather than there being a single global gart table
+ * for the entire GPU, there are multiple VM page tables active
+ * at any given time. The VM page tables can contain a mix
+ * vram pages and system memory pages and system memory pages
+ * can be mapped as snooped (cached system pages) or unsnooped
+ * (uncached system pages).
+ * Each VM has an ID associated with it and there is a page table
+ * associated with each VMID. When execting a command buffer,
+ * the kernel tells the the ring what VMID to use for that command
+ * buffer. VMIDs are allocated dynamically as commands are submitted.
+ * The userspace drivers maintain their own address space and the kernel
+ * sets up their pages tables accordingly when they submit their
+ * command buffers and a VMID is assigned.
+ * Cayman/Trinity support up to 8 active VMs at any given time;
+ * SI supports 16.
+ */
+
+/**
+ * radeon_vm_num_pde - return the number of page directory entries
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Calculate the number of page directory entries (cayman+).
+ */
+static unsigned radeon_vm_num_pdes(struct radeon_device *rdev)
+{
+ return rdev->vm_manager.max_pfn >> RADEON_VM_BLOCK_SIZE;
+}
+
+/**
+ * radeon_vm_directory_size - returns the size of the page directory in bytes
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Calculate the size of the page directory in bytes (cayman+).
+ */
+static unsigned radeon_vm_directory_size(struct radeon_device *rdev)
+{
+ return RADEON_GPU_PAGE_ALIGN(radeon_vm_num_pdes(rdev) * 8);
+}
+
+/**
+ * radeon_vm_manager_init - init the vm manager
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Init the vm manager (cayman+).
+ * Returns 0 for success, error for failure.
+ */
+int radeon_vm_manager_init(struct radeon_device *rdev)
+{
+ int r;
+
+ if (!rdev->vm_manager.enabled) {
+ r = radeon_asic_vm_init(rdev);
+ if (r)
+ return r;
+
+ rdev->vm_manager.enabled = true;
+ }
+ return 0;
+}
+
+/**
+ * radeon_vm_manager_fini - tear down the vm manager
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tear down the VM manager (cayman+).
+ */
+void radeon_vm_manager_fini(struct radeon_device *rdev)
+{
+ int i;
+
+ if (!rdev->vm_manager.enabled)
+ return;
+
+ for (i = 0; i < RADEON_NUM_VM; ++i)
+ radeon_fence_unref(&rdev->vm_manager.active[i]);
+ radeon_asic_vm_fini(rdev);
+ rdev->vm_manager.enabled = false;
+}
+
+/**
+ * radeon_vm_get_bos - add the vm BOs to a validation list
+ *
+ * @vm: vm providing the BOs
+ * @head: head of validation list
+ *
+ * Add the page directory to the list of BOs to
+ * validate for command submission (cayman+).
+ */
+struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ struct list_head *head)
+{
+ struct radeon_cs_reloc *list;
+ unsigned i, idx, size;
+
+ size = (radeon_vm_num_pdes(rdev) + 1) * sizeof(struct radeon_cs_reloc);
+ list = kmalloc(size, GFP_KERNEL);
+ if (!list)
+ return NULL;
+
+ /* add the vm page table to the list */
+ list[0].gobj = NULL;
+ list[0].robj = vm->page_directory;
+ list[0].domain = RADEON_GEM_DOMAIN_VRAM;
+ list[0].alt_domain = RADEON_GEM_DOMAIN_VRAM;
+ list[0].tv.bo = &vm->page_directory->tbo;
+ list[0].tiling_flags = 0;
+ list[0].handle = 0;
+ list_add(&list[0].tv.head, head);
+
+ for (i = 0, idx = 1; i <= vm->max_pde_used; i++) {
+ if (!vm->page_tables[i].bo)
+ continue;
+
+ list[idx].gobj = NULL;
+ list[idx].robj = vm->page_tables[i].bo;
+ list[idx].domain = RADEON_GEM_DOMAIN_VRAM;
+ list[idx].alt_domain = RADEON_GEM_DOMAIN_VRAM;
+ list[idx].tv.bo = &list[idx].robj->tbo;
+ list[idx].tiling_flags = 0;
+ list[idx].handle = 0;
+ list_add(&list[idx++].tv.head, head);
+ }
+
+ return list;
+}
+
+/**
+ * radeon_vm_grab_id - allocate the next free VMID
+ *
+ * @rdev: radeon_device pointer
+ * @vm: vm to allocate id for
+ * @ring: ring we want to submit job to
+ *
+ * Allocate an id for the vm (cayman+).
+ * Returns the fence we need to sync to (if any).
+ *
+ * Global and local mutex must be locked!
+ */
+struct radeon_fence *radeon_vm_grab_id(struct radeon_device *rdev,
+ struct radeon_vm *vm, int ring)
+{
+ struct radeon_fence *best[RADEON_NUM_RINGS] = {};
+ unsigned choices[2] = {};
+ unsigned i;
+
+ /* check if the id is still valid */
+ if (vm->last_id_use && vm->last_id_use == rdev->vm_manager.active[vm->id])
+ return NULL;
+
+ /* we definately need to flush */
+ radeon_fence_unref(&vm->last_flush);
+
+ /* skip over VMID 0, since it is the system VM */
+ for (i = 1; i < rdev->vm_manager.nvm; ++i) {
+ struct radeon_fence *fence = rdev->vm_manager.active[i];
+
+ if (fence == NULL) {
+ /* found a free one */
+ vm->id = i;
+ trace_radeon_vm_grab_id(vm->id, ring);
+ return NULL;
+ }
+
+ if (radeon_fence_is_earlier(fence, best[fence->ring])) {
+ best[fence->ring] = fence;
+ choices[fence->ring == ring ? 0 : 1] = i;
+ }
+ }
+
+ for (i = 0; i < 2; ++i) {
+ if (choices[i]) {
+ vm->id = choices[i];
+ trace_radeon_vm_grab_id(vm->id, ring);
+ return rdev->vm_manager.active[choices[i]];
+ }
+ }
+
+ /* should never happen */
+ BUG();
+ return NULL;
+}
+
+/**
+ * radeon_vm_flush - hardware flush the vm
+ *
+ * @rdev: radeon_device pointer
+ * @vm: vm we want to flush
+ * @ring: ring to use for flush
+ *
+ * Flush the vm (cayman+).
+ *
+ * Global and local mutex must be locked!
+ */
+void radeon_vm_flush(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ int ring)
+{
+ uint64_t pd_addr = radeon_bo_gpu_offset(vm->page_directory);
+
+ /* if we can't remember our last VM flush then flush now! */
+ /* XXX figure out why we have to flush all the time */
+ if (!vm->last_flush || true || pd_addr != vm->pd_gpu_addr) {
+ vm->pd_gpu_addr = pd_addr;
+ radeon_ring_vm_flush(rdev, ring, vm);
+ }
+}
+
+/**
+ * radeon_vm_fence - remember fence for vm
+ *
+ * @rdev: radeon_device pointer
+ * @vm: vm we want to fence
+ * @fence: fence to remember
+ *
+ * Fence the vm (cayman+).
+ * Set the fence used to protect page table and id.
+ *
+ * Global and local mutex must be locked!
+ */
+void radeon_vm_fence(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ struct radeon_fence *fence)
+{
+ radeon_fence_unref(&vm->fence);
+ vm->fence = radeon_fence_ref(fence);
+
+ radeon_fence_unref(&rdev->vm_manager.active[vm->id]);
+ rdev->vm_manager.active[vm->id] = radeon_fence_ref(fence);
+
+ radeon_fence_unref(&vm->last_id_use);
+ vm->last_id_use = radeon_fence_ref(fence);
+
+ /* we just flushed the VM, remember that */
+ if (!vm->last_flush)
+ vm->last_flush = radeon_fence_ref(fence);
+}
+
+/**
+ * radeon_vm_bo_find - find the bo_va for a specific vm & bo
+ *
+ * @vm: requested vm
+ * @bo: requested buffer object
+ *
+ * Find @bo inside the requested vm (cayman+).
+ * Search inside the @bos vm list for the requested vm
+ * Returns the found bo_va or NULL if none is found
+ *
+ * Object has to be reserved!
+ */
+struct radeon_bo_va *radeon_vm_bo_find(struct radeon_vm *vm,
+ struct radeon_bo *bo)
+{
+ struct radeon_bo_va *bo_va;
+
+ list_for_each_entry(bo_va, &bo->va, bo_list) {
+ if (bo_va->vm == vm) {
+ return bo_va;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * radeon_vm_bo_add - add a bo to a specific vm
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ * @bo: radeon buffer object
+ *
+ * Add @bo into the requested vm (cayman+).
+ * Add @bo to the list of bos associated with the vm
+ * Returns newly added bo_va or NULL for failure
+ *
+ * Object has to be reserved!
+ */
+struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ struct radeon_bo *bo)
+{
+ struct radeon_bo_va *bo_va;
+
+ bo_va = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
+ if (bo_va == NULL) {
+ return NULL;
+ }
+ bo_va->vm = vm;
+ bo_va->bo = bo;
+ bo_va->soffset = 0;
+ bo_va->eoffset = 0;
+ bo_va->flags = 0;
+ bo_va->valid = false;
+ bo_va->ref_count = 1;
+ INIT_LIST_HEAD(&bo_va->bo_list);
+ INIT_LIST_HEAD(&bo_va->vm_list);
+
+ mutex_lock(&vm->mutex);
+ list_add(&bo_va->vm_list, &vm->va);
+ list_add_tail(&bo_va->bo_list, &bo->va);
+ mutex_unlock(&vm->mutex);
+
+ return bo_va;
+}
+
+/**
+ * radeon_vm_clear_bo - initially clear the page dir/table
+ *
+ * @rdev: radeon_device pointer
+ * @bo: bo to clear
+ */
+static int radeon_vm_clear_bo(struct radeon_device *rdev,
+ struct radeon_bo *bo)
+{
+ struct ttm_validate_buffer tv;
+ struct ww_acquire_ctx ticket;
+ struct list_head head;
+ struct radeon_ib ib;
+ unsigned entries;
+ uint64_t addr;
+ int r;
+
+ memset(&tv, 0, sizeof(tv));
+ tv.bo = &bo->tbo;
+
+ INIT_LIST_HEAD(&head);
+ list_add(&tv.head, &head);
+
+ r = ttm_eu_reserve_buffers(&ticket, &head);
+ if (r)
+ return r;
+
+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
+ if (r)
+ goto error;
+
+ addr = radeon_bo_gpu_offset(bo);
+ entries = radeon_bo_size(bo) / 8;
+
+ r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib,
+ NULL, entries * 2 + 64);
+ if (r)
+ goto error;
+
+ ib.length_dw = 0;
+
+ radeon_asic_vm_set_page(rdev, &ib, addr, 0, entries, 0, 0);
+
+ r = radeon_ib_schedule(rdev, &ib, NULL);
+ if (r)
+ goto error;
+
+ ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence);
+ radeon_ib_free(rdev, &ib);
+
+ return 0;
+
+error:
+ ttm_eu_backoff_reservation(&ticket, &head);
+ return r;
+}
+
+/**
+ * radeon_vm_bo_set_addr - set bos virtual address inside a vm
+ *
+ * @rdev: radeon_device pointer
+ * @bo_va: bo_va to store the address
+ * @soffset: requested offset of the buffer in the VM address space
+ * @flags: attributes of pages (read/write/valid/etc.)
+ *
+ * Set offset of @bo_va (cayman+).
+ * Validate and set the offset requested within the vm address space.
+ * Returns 0 for success, error for failure.
+ *
+ * Object has to be reserved!
+ */
+int radeon_vm_bo_set_addr(struct radeon_device *rdev,
+ struct radeon_bo_va *bo_va,
+ uint64_t soffset,
+ uint32_t flags)
+{
+ uint64_t size = radeon_bo_size(bo_va->bo);
+ uint64_t eoffset, last_offset = 0;
+ struct radeon_vm *vm = bo_va->vm;
+ struct radeon_bo_va *tmp;
+ struct list_head *head;
+ unsigned last_pfn, pt_idx;
+ int r;
+
+ if (soffset) {
+ /* make sure object fit at this offset */
+ eoffset = soffset + size;
+ if (soffset >= eoffset) {
+ return -EINVAL;
+ }
+
+ last_pfn = eoffset / RADEON_GPU_PAGE_SIZE;
+ if (last_pfn > rdev->vm_manager.max_pfn) {
+ dev_err(rdev->dev, "va above limit (0x%08X > 0x%08X)\n",
+ last_pfn, rdev->vm_manager.max_pfn);
+ return -EINVAL;
+ }
+
+ } else {
+ eoffset = last_pfn = 0;
+ }
+
+ mutex_lock(&vm->mutex);
+ head = &vm->va;
+ last_offset = 0;
+ list_for_each_entry(tmp, &vm->va, vm_list) {
+ if (bo_va == tmp) {
+ /* skip over currently modified bo */
+ continue;
+ }
+
+ if (soffset >= last_offset && eoffset <= tmp->soffset) {
+ /* bo can be added before this one */
+ break;
+ }
+ if (eoffset > tmp->soffset && soffset < tmp->eoffset) {
+ /* bo and tmp overlap, invalid offset */
+ dev_err(rdev->dev, "bo %p va 0x%08X conflict with (bo %p 0x%08X 0x%08X)\n",
+ bo_va->bo, (unsigned)bo_va->soffset, tmp->bo,
+ (unsigned)tmp->soffset, (unsigned)tmp->eoffset);
+ mutex_unlock(&vm->mutex);
+ return -EINVAL;
+ }
+ last_offset = tmp->eoffset;
+ head = &tmp->vm_list;
+ }
+
+ bo_va->soffset = soffset;
+ bo_va->eoffset = eoffset;
+ bo_va->flags = flags;
+ bo_va->valid = false;
+ list_move(&bo_va->vm_list, head);
+
+ soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
+ eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
+
+ if (eoffset > vm->max_pde_used)
+ vm->max_pde_used = eoffset;
+
+ radeon_bo_unreserve(bo_va->bo);
+
+ /* walk over the address space and allocate the page tables */
+ for (pt_idx = soffset; pt_idx <= eoffset; ++pt_idx) {
+ struct radeon_bo *pt;
+
+ if (vm->page_tables[pt_idx].bo)
+ continue;
+
+ /* drop mutex to allocate and clear page table */
+ mutex_unlock(&vm->mutex);
+
+ r = radeon_bo_create(rdev, RADEON_VM_PTE_COUNT * 8,
+ RADEON_GPU_PAGE_SIZE, false,
+ RADEON_GEM_DOMAIN_VRAM, NULL, &pt);
+ if (r)
+ return r;
+
+ r = radeon_vm_clear_bo(rdev, pt);
+ if (r) {
+ radeon_bo_unref(&pt);
+ radeon_bo_reserve(bo_va->bo, false);
+ return r;
+ }
+
+ /* aquire mutex again */
+ mutex_lock(&vm->mutex);
+ if (vm->page_tables[pt_idx].bo) {
+ /* someone else allocated the pt in the meantime */
+ mutex_unlock(&vm->mutex);
+ radeon_bo_unref(&pt);
+ mutex_lock(&vm->mutex);
+ continue;
+ }
+
+ vm->page_tables[pt_idx].addr = 0;
+ vm->page_tables[pt_idx].bo = pt;
+ }
+
+ mutex_unlock(&vm->mutex);
+ return radeon_bo_reserve(bo_va->bo, false);
+}
+
+/**
+ * radeon_vm_map_gart - get the physical address of a gart page
+ *
+ * @rdev: radeon_device pointer
+ * @addr: the unmapped addr
+ *
+ * Look up the physical address of the page that the pte resolves
+ * to (cayman+).
+ * Returns the physical address of the page.
+ */
+uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr)
+{
+ uint64_t result;
+
+ /* page table offset */
+ result = rdev->gart.pages_addr[addr >> PAGE_SHIFT];
+
+ /* in case cpu page size != gpu page size*/
+ result |= addr & (~PAGE_MASK);
+
+ return result;
+}
+
+/**
+ * radeon_vm_page_flags - translate page flags to what the hw uses
+ *
+ * @flags: flags comming from userspace
+ *
+ * Translate the flags the userspace ABI uses to hw flags.
+ */
+static uint32_t radeon_vm_page_flags(uint32_t flags)
+{
+ uint32_t hw_flags = 0;
+ hw_flags |= (flags & RADEON_VM_PAGE_VALID) ? R600_PTE_VALID : 0;
+ hw_flags |= (flags & RADEON_VM_PAGE_READABLE) ? R600_PTE_READABLE : 0;
+ hw_flags |= (flags & RADEON_VM_PAGE_WRITEABLE) ? R600_PTE_WRITEABLE : 0;
+ if (flags & RADEON_VM_PAGE_SYSTEM) {
+ hw_flags |= R600_PTE_SYSTEM;
+ hw_flags |= (flags & RADEON_VM_PAGE_SNOOPED) ? R600_PTE_SNOOPED : 0;
+ }
+ return hw_flags;
+}
+
+/**
+ * radeon_vm_update_pdes - make sure that page directory is valid
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ * @start: start of GPU address range
+ * @end: end of GPU address range
+ *
+ * Allocates new page tables if necessary
+ * and updates the page directory (cayman+).
+ * Returns 0 for success, error for failure.
+ *
+ * Global and local mutex must be locked!
+ */
+int radeon_vm_update_page_directory(struct radeon_device *rdev,
+ struct radeon_vm *vm)
+{
+ static const uint32_t incr = RADEON_VM_PTE_COUNT * 8;
+
+ uint64_t pd_addr = radeon_bo_gpu_offset(vm->page_directory);
+ uint64_t last_pde = ~0, last_pt = ~0;
+ unsigned count = 0, pt_idx, ndw;
+ struct radeon_ib ib;
+ int r;
+
+ /* padding, etc. */
+ ndw = 64;
+
+ /* assume the worst case */
+ ndw += vm->max_pde_used * 12;
+
+ /* update too big for an IB */
+ if (ndw > 0xfffff)
+ return -ENOMEM;
+
+ r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib, NULL, ndw * 4);
+ if (r)
+ return r;
+ ib.length_dw = 0;
+
+ /* walk over the address space and update the page directory */
+ for (pt_idx = 0; pt_idx <= vm->max_pde_used; ++pt_idx) {
+ struct radeon_bo *bo = vm->page_tables[pt_idx].bo;
+ uint64_t pde, pt;
+
+ if (bo == NULL)
+ continue;
+
+ pt = radeon_bo_gpu_offset(bo);
+ if (vm->page_tables[pt_idx].addr == pt)
+ continue;
+ vm->page_tables[pt_idx].addr = pt;
+
+ pde = pd_addr + pt_idx * 8;
+ if (((last_pde + 8 * count) != pde) ||
+ ((last_pt + incr * count) != pt)) {
+
+ if (count) {
+ radeon_asic_vm_set_page(rdev, &ib, last_pde,
+ last_pt, count, incr,
+ R600_PTE_VALID);
+ }
+
+ count = 1;
+ last_pde = pde;
+ last_pt = pt;
+ } else {
+ ++count;
+ }
+ }
+
+ if (count)
+ radeon_asic_vm_set_page(rdev, &ib, last_pde, last_pt, count,
+ incr, R600_PTE_VALID);
+
+ if (ib.length_dw != 0) {
+ radeon_semaphore_sync_to(ib.semaphore, vm->last_id_use);
+ r = radeon_ib_schedule(rdev, &ib, NULL);
+ if (r) {
+ radeon_ib_free(rdev, &ib);
+ return r;
+ }
+ radeon_fence_unref(&vm->fence);
+ vm->fence = radeon_fence_ref(ib.fence);
+ radeon_fence_unref(&vm->last_flush);
+ }
+ radeon_ib_free(rdev, &ib);
+
+ return 0;
+}
+
+/**
+ * radeon_vm_update_ptes - make sure that page tables are valid
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ * @start: start of GPU address range
+ * @end: end of GPU address range
+ * @dst: destination address to map to
+ * @flags: mapping flags
+ *
+ * Update the page tables in the range @start - @end (cayman+).
+ *
+ * Global and local mutex must be locked!
+ */
+static void radeon_vm_update_ptes(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ struct radeon_ib *ib,
+ uint64_t start, uint64_t end,
+ uint64_t dst, uint32_t flags)
+{
+ static const uint64_t mask = RADEON_VM_PTE_COUNT - 1;
+
+ uint64_t last_pte = ~0, last_dst = ~0;
+ unsigned count = 0;
+ uint64_t addr;
+
+ start = start / RADEON_GPU_PAGE_SIZE;
+ end = end / RADEON_GPU_PAGE_SIZE;
+
+ /* walk over the address space and update the page tables */
+ for (addr = start; addr < end; ) {
+ uint64_t pt_idx = addr >> RADEON_VM_BLOCK_SIZE;
+ unsigned nptes;
+ uint64_t pte;
+
+ if ((addr & ~mask) == (end & ~mask))
+ nptes = end - addr;
+ else
+ nptes = RADEON_VM_PTE_COUNT - (addr & mask);
+
+ pte = radeon_bo_gpu_offset(vm->page_tables[pt_idx].bo);
+ pte += (addr & mask) * 8;
+
+ if ((last_pte + 8 * count) != pte) {
+
+ if (count) {
+ radeon_asic_vm_set_page(rdev, ib, last_pte,
+ last_dst, count,
+ RADEON_GPU_PAGE_SIZE,
+ flags);
+ }
+
+ count = nptes;
+ last_pte = pte;
+ last_dst = dst;
+ } else {
+ count += nptes;
+ }
+
+ addr += nptes;
+ dst += nptes * RADEON_GPU_PAGE_SIZE;
+ }
+
+ if (count) {
+ radeon_asic_vm_set_page(rdev, ib, last_pte,
+ last_dst, count,
+ RADEON_GPU_PAGE_SIZE, flags);
+ }
+}
+
+/**
+ * radeon_vm_bo_update - map a bo into the vm page table
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ * @bo: radeon buffer object
+ * @mem: ttm mem
+ *
+ * Fill in the page table entries for @bo (cayman+).
+ * Returns 0 for success, -EINVAL for failure.
+ *
+ * Object have to be reserved and mutex must be locked!
+ */
+int radeon_vm_bo_update(struct radeon_device *rdev,
+ struct radeon_vm *vm,
+ struct radeon_bo *bo,
+ struct ttm_mem_reg *mem)
+{
+ struct radeon_ib ib;
+ struct radeon_bo_va *bo_va;
+ unsigned nptes, ndw;
+ uint64_t addr;
+ int r;
+
+ bo_va = radeon_vm_bo_find(vm, bo);
+ if (bo_va == NULL) {
+ dev_err(rdev->dev, "bo %p not in vm %p\n", bo, vm);
+ return -EINVAL;
+ }
+
+ if (!bo_va->soffset) {
+ dev_err(rdev->dev, "bo %p don't has a mapping in vm %p\n",
+ bo, vm);
+ return -EINVAL;
+ }
+
+ if ((bo_va->valid && mem) || (!bo_va->valid && mem == NULL))
+ return 0;
+
+ bo_va->flags &= ~RADEON_VM_PAGE_VALID;
+ bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM;
+ if (mem) {
+ addr = mem->start << PAGE_SHIFT;
+ if (mem->mem_type != TTM_PL_SYSTEM) {
+ bo_va->flags |= RADEON_VM_PAGE_VALID;
+ bo_va->valid = true;
+ }
+ if (mem->mem_type == TTM_PL_TT) {
+ bo_va->flags |= RADEON_VM_PAGE_SYSTEM;
+ } else {
+ addr += rdev->vm_manager.vram_base_offset;
+ }
+ } else {
+ addr = 0;
+ bo_va->valid = false;
+ }
+
+ trace_radeon_vm_bo_update(bo_va);
+
+ nptes = radeon_bo_ngpu_pages(bo);
+
+ /* padding, etc. */
+ ndw = 64;
+
+ if (RADEON_VM_BLOCK_SIZE > 11)
+ /* reserve space for one header for every 2k dwords */
+ ndw += (nptes >> 11) * 4;
+ else
+ /* reserve space for one header for
+ every (1 << BLOCK_SIZE) entries */
+ ndw += (nptes >> RADEON_VM_BLOCK_SIZE) * 4;
+
+ /* reserve space for pte addresses */
+ ndw += nptes * 2;
+
+ /* update too big for an IB */
+ if (ndw > 0xfffff)
+ return -ENOMEM;
+
+ r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib, NULL, ndw * 4);
+ if (r)
+ return r;
+ ib.length_dw = 0;
+
+ radeon_vm_update_ptes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset,
+ addr, radeon_vm_page_flags(bo_va->flags));
+
+ radeon_semaphore_sync_to(ib.semaphore, vm->fence);
+ r = radeon_ib_schedule(rdev, &ib, NULL);
+ if (r) {
+ radeon_ib_free(rdev, &ib);
+ return r;
+ }
+ radeon_fence_unref(&vm->fence);
+ vm->fence = radeon_fence_ref(ib.fence);
+ radeon_ib_free(rdev, &ib);
+ radeon_fence_unref(&vm->last_flush);
+
+ return 0;
+}
+
+/**
+ * radeon_vm_bo_rmv - remove a bo to a specific vm
+ *
+ * @rdev: radeon_device pointer
+ * @bo_va: requested bo_va
+ *
+ * Remove @bo_va->bo from the requested vm (cayman+).
+ * Remove @bo_va->bo from the list of bos associated with the bo_va->vm and
+ * remove the ptes for @bo_va in the page table.
+ * Returns 0 for success.
+ *
+ * Object have to be reserved!
+ */
+int radeon_vm_bo_rmv(struct radeon_device *rdev,
+ struct radeon_bo_va *bo_va)
+{
+ int r = 0;
+
+ mutex_lock(&bo_va->vm->mutex);
+ if (bo_va->soffset)
+ r = radeon_vm_bo_update(rdev, bo_va->vm, bo_va->bo, NULL);
+
+ list_del(&bo_va->vm_list);
+ mutex_unlock(&bo_va->vm->mutex);
+ list_del(&bo_va->bo_list);
+
+ kfree(bo_va);
+ return r;
+}
+
+/**
+ * radeon_vm_bo_invalidate - mark the bo as invalid
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ * @bo: radeon buffer object
+ *
+ * Mark @bo as invalid (cayman+).
+ */
+void radeon_vm_bo_invalidate(struct radeon_device *rdev,
+ struct radeon_bo *bo)
+{
+ struct radeon_bo_va *bo_va;
+
+ list_for_each_entry(bo_va, &bo->va, bo_list) {
+ bo_va->valid = false;
+ }
+}
+
+/**
+ * radeon_vm_init - initialize a vm instance
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ *
+ * Init @vm fields (cayman+).
+ */
+int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
+{
+ unsigned pd_size, pd_entries, pts_size;
+ int r;
+
+ vm->id = 0;
+ vm->fence = NULL;
+ vm->last_flush = NULL;
+ vm->last_id_use = NULL;
+ mutex_init(&vm->mutex);
+ INIT_LIST_HEAD(&vm->va);
+
+ pd_size = radeon_vm_directory_size(rdev);
+ pd_entries = radeon_vm_num_pdes(rdev);
+
+ /* allocate page table array */
+ pts_size = pd_entries * sizeof(struct radeon_vm_pt);
+ vm->page_tables = kzalloc(pts_size, GFP_KERNEL);
+ if (vm->page_tables == NULL) {
+ DRM_ERROR("Cannot allocate memory for page table array\n");
+ return -ENOMEM;
+ }
+
+ r = radeon_bo_create(rdev, pd_size, RADEON_VM_PTB_ALIGN_SIZE, false,
+ RADEON_GEM_DOMAIN_VRAM, NULL,
+ &vm->page_directory);
+ if (r)
+ return r;
+
+ r = radeon_vm_clear_bo(rdev, vm->page_directory);
+ if (r) {
+ radeon_bo_unref(&vm->page_directory);
+ vm->page_directory = NULL;
+ return r;
+ }
+
+ return 0;
+}
+
+/**
+ * radeon_vm_fini - tear down a vm instance
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ *
+ * Tear down @vm (cayman+).
+ * Unbind the VM and remove all bos from the vm bo list
+ */
+void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm)
+{
+ struct radeon_bo_va *bo_va, *tmp;
+ int i, r;
+
+ if (!list_empty(&vm->va)) {
+ dev_err(rdev->dev, "still active bo inside vm\n");
+ }
+ list_for_each_entry_safe(bo_va, tmp, &vm->va, vm_list) {
+ list_del_init(&bo_va->vm_list);
+ r = radeon_bo_reserve(bo_va->bo, false);
+ if (!r) {
+ list_del_init(&bo_va->bo_list);
+ radeon_bo_unreserve(bo_va->bo);
+ kfree(bo_va);
+ }
+ }
+
+
+ for (i = 0; i < radeon_vm_num_pdes(rdev); i++)
+ radeon_bo_unref(&vm->page_tables[i].bo);
+ kfree(vm->page_tables);
+
+ radeon_bo_unref(&vm->page_directory);
+
+ radeon_fence_unref(&vm->fence);
+ radeon_fence_unref(&vm->last_flush);
+ radeon_fence_unref(&vm->last_id_use);
+
+ mutex_destroy(&vm->mutex);
+}
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c
index 8512085b0aef..02f7710de470 100644
--- a/drivers/gpu/drm/radeon/rs780_dpm.c
+++ b/drivers/gpu/drm/radeon/rs780_dpm.c
@@ -807,9 +807,6 @@ static int rs780_parse_power_table(struct radeon_device *rdev)
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
@@ -859,6 +856,10 @@ int rs780_dpm_init(struct radeon_device *rdev)
return -ENOMEM;
rdev->pm.dpm.priv = pi;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = rs780_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c
index bebf31c4d841..e7045b085715 100644
--- a/drivers/gpu/drm/radeon/rv6xx_dpm.c
+++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c
@@ -1891,9 +1891,6 @@ static int rv6xx_parse_power_table(struct radeon_device *rdev)
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
@@ -1943,6 +1940,10 @@ int rv6xx_dpm_init(struct radeon_device *rdev)
return -ENOMEM;
rdev->pm.dpm.priv = pi;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = rv6xx_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c
index b5f63f5e22a3..da041a43d82e 100644
--- a/drivers/gpu/drm/radeon/rv770_dpm.c
+++ b/drivers/gpu/drm/radeon/rv770_dpm.c
@@ -2281,9 +2281,6 @@ int rv7xx_parse_power_table(struct radeon_device *rdev)
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
@@ -2361,6 +2358,10 @@ int rv770_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = rv7xx_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 9a124d0608b3..d589475fe9e6 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -3434,8 +3434,6 @@ static int si_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB0_BASE, ring->gpu_addr >> 8);
- ring->rptr = RREG32(CP_RB0_RPTR);
-
/* ring1 - compute only */
/* Set ring buffer size */
ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
@@ -3460,8 +3458,6 @@ static int si_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB1_BASE, ring->gpu_addr >> 8);
- ring->rptr = RREG32(CP_RB1_RPTR);
-
/* ring2 - compute only */
/* Set ring buffer size */
ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
@@ -3486,8 +3482,6 @@ static int si_cp_resume(struct radeon_device *rdev)
WREG32(CP_RB2_BASE, ring->gpu_addr >> 8);
- ring->rptr = RREG32(CP_RB2_RPTR);
-
/* start the rings */
si_cp_start(rdev);
rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true;
@@ -3872,11 +3866,9 @@ bool si_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
if (!(reset_mask & (RADEON_RESET_GFX |
RADEON_RESET_COMPUTE |
RADEON_RESET_CP))) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force CP activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c
index 59be2cfcbb47..cf0fdad8c278 100644
--- a/drivers/gpu/drm/radeon/si_dma.c
+++ b/drivers/gpu/drm/radeon/si_dma.c
@@ -49,11 +49,9 @@ bool si_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
mask = RADEON_RESET_DMA1;
if (!(reset_mask & mask)) {
- radeon_ring_lockup_update(ring);
+ radeon_ring_lockup_update(rdev, ring);
return false;
}
- /* force ring activities */
- radeon_ring_force_activity(rdev, ring);
return radeon_ring_test_lockup(rdev, ring);
}
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 0a2f5b4bca43..9a3567bedaae 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -6271,9 +6271,6 @@ static int si_parse_power_table(struct radeon_device *rdev)
if (!rdev->pm.dpm.ps)
return -ENOMEM;
power_state_offset = (u8 *)state_array->states;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < state_array->ucNumEntries; i++) {
u8 *idx;
power_state = (union pplib_power_state *)power_state_offset;
@@ -6350,6 +6347,10 @@ int si_dpm_init(struct radeon_device *rdev)
pi->min_vddc_in_table = 0;
pi->max_vddc_in_table = 0;
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = si_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index 9239a6d29128..683532f84931 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -1798,4 +1798,51 @@
#define DMA_PACKET_CONSTANT_FILL 0xd
#define DMA_PACKET_NOP 0xf
+#define VCE_STATUS 0x20004
+#define VCE_VCPU_CNTL 0x20014
+#define VCE_CLK_EN (1 << 0)
+#define VCE_VCPU_CACHE_OFFSET0 0x20024
+#define VCE_VCPU_CACHE_SIZE0 0x20028
+#define VCE_VCPU_CACHE_OFFSET1 0x2002c
+#define VCE_VCPU_CACHE_SIZE1 0x20030
+#define VCE_VCPU_CACHE_OFFSET2 0x20034
+#define VCE_VCPU_CACHE_SIZE2 0x20038
+#define VCE_SOFT_RESET 0x20120
+#define VCE_ECPU_SOFT_RESET (1 << 0)
+#define VCE_FME_SOFT_RESET (1 << 2)
+#define VCE_RB_BASE_LO2 0x2016c
+#define VCE_RB_BASE_HI2 0x20170
+#define VCE_RB_SIZE2 0x20174
+#define VCE_RB_RPTR2 0x20178
+#define VCE_RB_WPTR2 0x2017c
+#define VCE_RB_BASE_LO 0x20180
+#define VCE_RB_BASE_HI 0x20184
+#define VCE_RB_SIZE 0x20188
+#define VCE_RB_RPTR 0x2018c
+#define VCE_RB_WPTR 0x20190
+#define VCE_CLOCK_GATING_A 0x202f8
+#define VCE_CLOCK_GATING_B 0x202fc
+#define VCE_UENC_CLOCK_GATING 0x205bc
+#define VCE_UENC_REG_CLOCK_GATING 0x205c0
+#define VCE_FW_REG_STATUS 0x20e10
+# define VCE_FW_REG_STATUS_BUSY (1 << 0)
+# define VCE_FW_REG_STATUS_PASS (1 << 3)
+# define VCE_FW_REG_STATUS_DONE (1 << 11)
+#define VCE_LMI_FW_START_KEYSEL 0x20e18
+#define VCE_LMI_FW_PERIODIC_CTRL 0x20e20
+#define VCE_LMI_CTRL2 0x20e74
+#define VCE_LMI_CTRL 0x20e98
+#define VCE_LMI_VM_CTRL 0x20ea0
+#define VCE_LMI_SWAP_CNTL 0x20eb4
+#define VCE_LMI_SWAP_CNTL1 0x20eb8
+#define VCE_LMI_CACHE_CTRL 0x20ef4
+
+#define VCE_CMD_NO_OP 0x00000000
+#define VCE_CMD_END 0x00000001
+#define VCE_CMD_IB 0x00000002
+#define VCE_CMD_FENCE 0x00000003
+#define VCE_CMD_TRAP 0x00000004
+#define VCE_CMD_IB_AUTO 0x00000005
+#define VCE_CMD_SEMAPHORE 0x00000006
+
#endif
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c
index 8b47b3cd0357..3f0e8d7b8dbe 100644
--- a/drivers/gpu/drm/radeon/sumo_dpm.c
+++ b/drivers/gpu/drm/radeon/sumo_dpm.c
@@ -1484,9 +1484,6 @@ static int sumo_parse_power_table(struct radeon_device *rdev)
if (!rdev->pm.dpm.ps)
return -ENOMEM;
power_state_offset = (u8 *)state_array->states;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < state_array->ucNumEntries; i++) {
u8 *idx;
power_state = (union pplib_power_state *)power_state_offset;
@@ -1772,6 +1769,10 @@ int sumo_dpm_init(struct radeon_device *rdev)
sumo_construct_boot_and_acpi_state(rdev);
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = sumo_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c
index 2da0e17eb960..2a2822c03329 100644
--- a/drivers/gpu/drm/radeon/trinity_dpm.c
+++ b/drivers/gpu/drm/radeon/trinity_dpm.c
@@ -1694,9 +1694,6 @@ static int trinity_parse_power_table(struct radeon_device *rdev)
if (!rdev->pm.dpm.ps)
return -ENOMEM;
power_state_offset = (u8 *)state_array->states;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < state_array->ucNumEntries; i++) {
u8 *idx;
power_state = (union pplib_power_state *)power_state_offset;
@@ -1895,6 +1892,10 @@ int trinity_dpm_init(struct radeon_device *rdev)
trinity_construct_boot_state(rdev);
+ ret = r600_get_platform_caps(rdev);
+ if (ret)
+ return ret;
+
ret = trinity_parse_power_table(rdev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/radeon/uvd_v1_0.c b/drivers/gpu/drm/radeon/uvd_v1_0.c
index d4a68af1a279..0a243f0e5d68 100644
--- a/drivers/gpu/drm/radeon/uvd_v1_0.c
+++ b/drivers/gpu/drm/radeon/uvd_v1_0.c
@@ -262,7 +262,7 @@ int uvd_v1_0_start(struct radeon_device *rdev)
/* Initialize the ring buffer's read and write pointers */
WREG32(UVD_RBC_RB_RPTR, 0x0);
- ring->wptr = ring->rptr = RREG32(UVD_RBC_RB_RPTR);
+ ring->wptr = RREG32(UVD_RBC_RB_RPTR);
WREG32(UVD_RBC_RB_WPTR, ring->wptr);
/* set the ring address */
diff --git a/drivers/gpu/drm/radeon/vce_v1_0.c b/drivers/gpu/drm/radeon/vce_v1_0.c
new file mode 100644
index 000000000000..b44d9c842f7b
--- /dev/null
+++ b/drivers/gpu/drm/radeon/vce_v1_0.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Christian König <christian.koenig@amd.com>
+ */
+
+#include <linux/firmware.h>
+#include <drm/drmP.h>
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "sid.h"
+
+/**
+ * vce_v1_0_get_rptr - get read pointer
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring pointer
+ *
+ * Returns the current hardware read pointer
+ */
+uint32_t vce_v1_0_get_rptr(struct radeon_device *rdev,
+ struct radeon_ring *ring)
+{
+ if (ring->idx == TN_RING_TYPE_VCE1_INDEX)
+ return RREG32(VCE_RB_RPTR);
+ else
+ return RREG32(VCE_RB_RPTR2);
+}
+
+/**
+ * vce_v1_0_get_wptr - get write pointer
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring pointer
+ *
+ * Returns the current hardware write pointer
+ */
+uint32_t vce_v1_0_get_wptr(struct radeon_device *rdev,
+ struct radeon_ring *ring)
+{
+ if (ring->idx == TN_RING_TYPE_VCE1_INDEX)
+ return RREG32(VCE_RB_WPTR);
+ else
+ return RREG32(VCE_RB_WPTR2);
+}
+
+/**
+ * vce_v1_0_set_wptr - set write pointer
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring pointer
+ *
+ * Commits the write pointer to the hardware
+ */
+void vce_v1_0_set_wptr(struct radeon_device *rdev,
+ struct radeon_ring *ring)
+{
+ if (ring->idx == TN_RING_TYPE_VCE1_INDEX)
+ WREG32(VCE_RB_WPTR, ring->wptr);
+ else
+ WREG32(VCE_RB_WPTR2, ring->wptr);
+}
+
+/**
+ * vce_v1_0_start - start VCE block
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Setup and start the VCE block
+ */
+int vce_v1_0_start(struct radeon_device *rdev)
+{
+ struct radeon_ring *ring;
+ int i, j, r;
+
+ /* set BUSY flag */
+ WREG32_P(VCE_STATUS, 1, ~1);
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
+ WREG32(VCE_RB_RPTR, ring->wptr);
+ WREG32(VCE_RB_WPTR, ring->wptr);
+ WREG32(VCE_RB_BASE_LO, ring->gpu_addr);
+ WREG32(VCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
+ WREG32(VCE_RB_SIZE, ring->ring_size / 4);
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
+ WREG32(VCE_RB_RPTR2, ring->wptr);
+ WREG32(VCE_RB_WPTR2, ring->wptr);
+ WREG32(VCE_RB_BASE_LO2, ring->gpu_addr);
+ WREG32(VCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
+ WREG32(VCE_RB_SIZE2, ring->ring_size / 4);
+
+ WREG32_P(VCE_VCPU_CNTL, VCE_CLK_EN, ~VCE_CLK_EN);
+
+ WREG32_P(VCE_SOFT_RESET,
+ VCE_ECPU_SOFT_RESET |
+ VCE_FME_SOFT_RESET, ~(
+ VCE_ECPU_SOFT_RESET |
+ VCE_FME_SOFT_RESET));
+
+ mdelay(100);
+
+ WREG32_P(VCE_SOFT_RESET, 0, ~(
+ VCE_ECPU_SOFT_RESET |
+ VCE_FME_SOFT_RESET));
+
+ for (i = 0; i < 10; ++i) {
+ uint32_t status;
+ for (j = 0; j < 100; ++j) {
+ status = RREG32(VCE_STATUS);
+ if (status & 2)
+ break;
+ mdelay(10);
+ }
+ r = 0;
+ if (status & 2)
+ break;
+
+ DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n");
+ WREG32_P(VCE_SOFT_RESET, VCE_ECPU_SOFT_RESET, ~VCE_ECPU_SOFT_RESET);
+ mdelay(10);
+ WREG32_P(VCE_SOFT_RESET, 0, ~VCE_ECPU_SOFT_RESET);
+ mdelay(10);
+ r = -1;
+ }
+
+ /* clear BUSY flag */
+ WREG32_P(VCE_STATUS, 0, ~1);
+
+ if (r) {
+ DRM_ERROR("VCE not responding, giving up!!!\n");
+ return r;
+ }
+
+ return 0;
+}
+
+int vce_v1_0_init(struct radeon_device *rdev)
+{
+ struct radeon_ring *ring;
+ int r;
+
+ r = vce_v1_0_start(rdev);
+ if (r)
+ return r;
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE1_INDEX];
+ ring->ready = true;
+ r = radeon_ring_test(rdev, TN_RING_TYPE_VCE1_INDEX, ring);
+ if (r) {
+ ring->ready = false;
+ return r;
+ }
+
+ ring = &rdev->ring[TN_RING_TYPE_VCE2_INDEX];
+ ring->ready = true;
+ r = radeon_ring_test(rdev, TN_RING_TYPE_VCE2_INDEX, ring);
+ if (r) {
+ ring->ready = false;
+ return r;
+ }
+
+ DRM_INFO("VCE initialized successfully.\n");
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/radeon/vce_v2_0.c b/drivers/gpu/drm/radeon/vce_v2_0.c
new file mode 100644
index 000000000000..1ac7bb825a1b
--- /dev/null
+++ b/drivers/gpu/drm/radeon/vce_v2_0.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Christian König <christian.koenig@amd.com>
+ */
+
+#include <linux/firmware.h>
+#include <drm/drmP.h>
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "cikd.h"
+
+static void vce_v2_0_set_sw_cg(struct radeon_device *rdev, bool gated)
+{
+ u32 tmp;
+
+ if (gated) {
+ tmp = RREG32(VCE_CLOCK_GATING_B);
+ tmp |= 0xe70000;
+ WREG32(VCE_CLOCK_GATING_B, tmp);
+
+ tmp = RREG32(VCE_UENC_CLOCK_GATING);
+ tmp |= 0xff000000;
+ WREG32(VCE_UENC_CLOCK_GATING, tmp);
+
+ tmp = RREG32(VCE_UENC_REG_CLOCK_GATING);
+ tmp &= ~0x3fc;
+ WREG32(VCE_UENC_REG_CLOCK_GATING, tmp);
+
+ WREG32(VCE_CGTT_CLK_OVERRIDE, 0);
+ } else {
+ tmp = RREG32(VCE_CLOCK_GATING_B);
+ tmp |= 0xe7;
+ tmp &= ~0xe70000;
+ WREG32(VCE_CLOCK_GATING_B, tmp);
+
+ tmp = RREG32(VCE_UENC_CLOCK_GATING);
+ tmp |= 0x1fe000;
+ tmp &= ~0xff000000;
+ WREG32(VCE_UENC_CLOCK_GATING, tmp);
+
+ tmp = RREG32(VCE_UENC_REG_CLOCK_GATING);
+ tmp |= 0x3fc;
+ WREG32(VCE_UENC_REG_CLOCK_GATING, tmp);
+ }
+}
+
+static void vce_v2_0_set_dyn_cg(struct radeon_device *rdev, bool gated)
+{
+ u32 orig, tmp;
+
+ tmp = RREG32(VCE_CLOCK_GATING_B);
+ tmp &= ~0x00060006;
+ if (gated) {
+ tmp |= 0xe10000;
+ } else {
+ tmp |= 0xe1;
+ tmp &= ~0xe10000;
+ }
+ WREG32(VCE_CLOCK_GATING_B, tmp);
+
+ orig = tmp = RREG32(VCE_UENC_CLOCK_GATING);
+ tmp &= ~0x1fe000;
+ tmp &= ~0xff000000;
+ if (tmp != orig)
+ WREG32(VCE_UENC_CLOCK_GATING, tmp);
+
+ orig = tmp = RREG32(VCE_UENC_REG_CLOCK_GATING);
+ tmp &= ~0x3fc;
+ if (tmp != orig)
+ WREG32(VCE_UENC_REG_CLOCK_GATING, tmp);
+
+ if (gated)
+ WREG32(VCE_CGTT_CLK_OVERRIDE, 0);
+}
+
+static void vce_v2_0_disable_cg(struct radeon_device *rdev)
+{
+ WREG32(VCE_CGTT_CLK_OVERRIDE, 7);
+}
+
+void vce_v2_0_enable_mgcg(struct radeon_device *rdev, bool enable)
+{
+ bool sw_cg = false;
+
+ if (enable && (rdev->cg_flags & RADEON_CG_SUPPORT_VCE_MGCG)) {
+ if (sw_cg)
+ vce_v2_0_set_sw_cg(rdev, true);
+ else
+ vce_v2_0_set_dyn_cg(rdev, true);
+ } else {
+ vce_v2_0_disable_cg(rdev);
+
+ if (sw_cg)
+ vce_v2_0_set_sw_cg(rdev, false);
+ else
+ vce_v2_0_set_dyn_cg(rdev, false);
+ }
+}
+
+static void vce_v2_0_init_cg(struct radeon_device *rdev)
+{
+ u32 tmp;
+
+ tmp = RREG32(VCE_CLOCK_GATING_A);
+ tmp &= ~(CGC_CLK_GATE_DLY_TIMER_MASK | CGC_CLK_GATER_OFF_DLY_TIMER_MASK);
+ tmp |= (CGC_CLK_GATE_DLY_TIMER(0) | CGC_CLK_GATER_OFF_DLY_TIMER(4));
+ tmp |= CGC_UENC_WAIT_AWAKE;
+ WREG32(VCE_CLOCK_GATING_A, tmp);
+
+ tmp = RREG32(VCE_UENC_CLOCK_GATING);
+ tmp &= ~(CLOCK_ON_DELAY_MASK | CLOCK_OFF_DELAY_MASK);
+ tmp |= (CLOCK_ON_DELAY(0) | CLOCK_OFF_DELAY(4));
+ WREG32(VCE_UENC_CLOCK_GATING, tmp);
+
+ tmp = RREG32(VCE_CLOCK_GATING_B);
+ tmp |= 0x10;
+ tmp &= ~0x100000;
+ WREG32(VCE_CLOCK_GATING_B, tmp);
+}
+
+int vce_v2_0_resume(struct radeon_device *rdev)
+{
+ uint64_t addr = rdev->vce.gpu_addr;
+ uint32_t size;
+
+ WREG32_P(VCE_CLOCK_GATING_A, 0, ~(1 << 16));
+ WREG32_P(VCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000);
+ WREG32_P(VCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F);
+ WREG32(VCE_CLOCK_GATING_B, 0xf7);
+
+ WREG32(VCE_LMI_CTRL, 0x00398000);
+ WREG32_P(VCE_LMI_CACHE_CTRL, 0x0, ~0x1);
+ WREG32(VCE_LMI_SWAP_CNTL, 0);
+ WREG32(VCE_LMI_SWAP_CNTL1, 0);
+ WREG32(VCE_LMI_VM_CTRL, 0);
+
+ size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size);
+ WREG32(VCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff);
+ WREG32(VCE_VCPU_CACHE_SIZE0, size);
+
+ addr += size;
+ size = RADEON_VCE_STACK_SIZE;
+ WREG32(VCE_VCPU_CACHE_OFFSET1, addr & 0x7fffffff);
+ WREG32(VCE_VCPU_CACHE_SIZE1, size);
+
+ addr += size;
+ size = RADEON_VCE_HEAP_SIZE;
+ WREG32(VCE_VCPU_CACHE_OFFSET2, addr & 0x7fffffff);
+ WREG32(VCE_VCPU_CACHE_SIZE2, size);
+
+ WREG32_P(VCE_LMI_CTRL2, 0x0, ~0x100);
+
+ WREG32_P(VCE_SYS_INT_EN, VCE_SYS_INT_TRAP_INTERRUPT_EN,
+ ~VCE_SYS_INT_TRAP_INTERRUPT_EN);
+
+ vce_v2_0_init_cg(rdev);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index fbf4be316d0b..299267db2898 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -299,7 +299,7 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
{
struct drm_crtc *crtc = &rcrtc->crtc;
- rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+ rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
rcar_du_plane_update_base(rcrtc->plane);
}
@@ -358,10 +358,10 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
const struct rcar_du_format_info *format;
int ret;
- format = rcar_du_format_info(crtc->fb->pixel_format);
+ format = rcar_du_format_info(crtc->primary->fb->pixel_format);
if (format == NULL) {
dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
- crtc->fb->pixel_format);
+ crtc->primary->fb->pixel_format);
ret = -EINVAL;
goto error;
}
@@ -377,7 +377,7 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
rcrtc->plane->width = mode->hdisplay;
rcrtc->plane->height = mode->vdisplay;
- rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+ rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
rcrtc->outputs = 0;
@@ -510,7 +510,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
}
spin_unlock_irqrestore(&dev->event_lock, flags);
- crtc->fb = fb;
+ crtc->primary->fb = fb;
rcar_du_crtc_update_base(rcrtc);
if (event) {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index fbeabd9a281f..a87edfac111f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -248,7 +248,10 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
continue;
}
- rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata);
+ ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
+ pdata);
+ if (ret < 0)
+ return ret;
}
/* Set the possible CRTCs and possible clones. There's always at least
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index 0428076f1ce8..e9e5e6d368cc 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -173,7 +173,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
if (scrtc->started)
return;
- format = shmob_drm_format_info(crtc->fb->pixel_format);
+ format = shmob_drm_format_info(crtc->primary->fb->pixel_format);
if (WARN_ON(format == NULL))
return;
@@ -247,7 +247,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
lcdc_write(sdev, LDDDSR, value);
/* Setup planes. */
- list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
if (plane->crtc == crtc)
shmob_drm_plane_setup(plane);
}
@@ -303,7 +303,7 @@ static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
int x, int y)
{
struct drm_crtc *crtc = &scrtc->crtc;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
struct shmob_drm_device *sdev = crtc->dev->dev_private;
struct drm_gem_cma_object *gem;
unsigned int bpp;
@@ -382,15 +382,15 @@ static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc,
const struct shmob_drm_format_info *format;
void *cache;
- format = shmob_drm_format_info(crtc->fb->pixel_format);
+ format = shmob_drm_format_info(crtc->primary->fb->pixel_format);
if (format == NULL) {
dev_dbg(sdev->dev, "mode_set: unsupported format %08x\n",
- crtc->fb->pixel_format);
+ crtc->primary->fb->pixel_format);
return -EINVAL;
}
scrtc->format = format;
- scrtc->line_size = crtc->fb->pitches[0];
+ scrtc->line_size = crtc->primary->fb->pitches[0];
if (sdev->meram) {
/* Enable MERAM cache if configured. We need to de-init
@@ -402,7 +402,7 @@ static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc,
}
cache = sh_mobile_meram_cache_alloc(sdev->meram, mdata,
- crtc->fb->pitches[0],
+ crtc->primary->fb->pitches[0],
adjusted_mode->vdisplay,
format->meram,
&scrtc->line_size);
@@ -489,7 +489,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
}
spin_unlock_irqrestore(&dev->event_lock, flags);
- crtc->fb = fb;
+ crtc->primary->fb = fb;
shmob_drm_crtc_update_base(scrtc);
if (event) {
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index 8d220afbd85f..d43f21bb4596 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -11,6 +11,8 @@ tegra-drm-y := \
hdmi.o \
mipi-phy.o \
dsi.o \
+ sor.o \
+ dpaux.o \
gr2d.o \
gr3d.o
diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c
index e38e5967d77b..71cef5c13dc8 100644
--- a/drivers/gpu/drm/tegra/bus.c
+++ b/drivers/gpu/drm/tegra/bus.c
@@ -63,7 +63,7 @@ int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
return 0;
err_free:
- drm_dev_free(drm);
+ drm_dev_unref(drm);
return ret;
}
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 9336006b475d..36c717af6cf9 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -235,14 +235,14 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
if (!dc->event)
return;
- bo = tegra_fb_get_plane(crtc->fb, 0);
+ bo = tegra_fb_get_plane(crtc->primary->fb, 0);
/* check if new start address has been latched */
tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
- if (base == bo->paddr + crtc->fb->offsets[0]) {
+ if (base == bo->paddr + crtc->primary->fb->offsets[0]) {
spin_lock_irqsave(&drm->event_lock, flags);
drm_send_vblank_event(drm, dc->pipe, dc->event);
drm_vblank_put(drm, dc->pipe);
@@ -284,7 +284,7 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
}
tegra_dc_set_base(dc, 0, 0, fb);
- crtc->fb = fb;
+ crtc->primary->fb = fb;
return 0;
}
@@ -645,7 +645,7 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *adjusted,
int x, int y, struct drm_framebuffer *old_fb)
{
- struct tegra_bo *bo = tegra_fb_get_plane(crtc->fb, 0);
+ struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_dc_window window;
unsigned long div, value;
@@ -682,9 +682,9 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
window.dst.y = 0;
window.dst.w = mode->hdisplay;
window.dst.h = mode->vdisplay;
- window.format = tegra_dc_format(crtc->fb->pixel_format);
- window.bits_per_pixel = crtc->fb->bits_per_pixel;
- window.stride[0] = crtc->fb->pitches[0];
+ window.format = tegra_dc_format(crtc->primary->fb->pixel_format);
+ window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
+ window.stride[0] = crtc->primary->fb->pitches[0];
window.base[0] = bo->paddr;
err = tegra_dc_setup_window(dc, 0, &window);
@@ -699,7 +699,7 @@ static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
{
struct tegra_dc *dc = to_tegra_dc(crtc);
- return tegra_dc_set_base(dc, x, y, crtc->fb);
+ return tegra_dc_set_base(dc, x, y, crtc->primary->fb);
}
static void tegra_crtc_prepare(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 3c2c0ea1cd87..c94101494826 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -118,6 +118,7 @@
#define DC_DISP_DISP_WIN_OPTIONS 0x402
#define HDMI_ENABLE (1 << 30)
#define DSI_ENABLE (1 << 29)
+#define SOR_ENABLE (1 << 25)
#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403
#define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24)
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c
new file mode 100644
index 000000000000..d536ed381fbd
--- /dev/null
+++ b/drivers/gpu/drm/tegra/dpaux.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+
+#include "dpaux.h"
+#include "drm.h"
+
+static DEFINE_MUTEX(dpaux_lock);
+static LIST_HEAD(dpaux_list);
+
+struct tegra_dpaux {
+ struct drm_dp_aux aux;
+ struct device *dev;
+
+ void __iomem *regs;
+ int irq;
+
+ struct tegra_output *output;
+
+ struct reset_control *rst;
+ struct clk *clk_parent;
+ struct clk *clk;
+
+ struct regulator *vdd;
+
+ struct completion complete;
+ struct list_head list;
+};
+
+static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
+{
+ return container_of(aux, struct tegra_dpaux, aux);
+}
+
+static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux,
+ unsigned long offset)
+{
+ return readl(dpaux->regs + (offset << 2));
+}
+
+static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux,
+ unsigned long value,
+ unsigned long offset)
+{
+ writel(value, dpaux->regs + (offset << 2));
+}
+
+static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer,
+ size_t size)
+{
+ unsigned long offset = DPAUX_DP_AUXDATA_WRITE(0);
+ size_t i, j;
+
+ for (i = 0; i < size; i += 4) {
+ size_t num = min_t(size_t, size - i, 4);
+ unsigned long value = 0;
+
+ for (j = 0; j < num; j++)
+ value |= buffer[i + j] << (j * 8);
+
+ tegra_dpaux_writel(dpaux, value, offset++);
+ }
+}
+
+static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
+ size_t size)
+{
+ unsigned long offset = DPAUX_DP_AUXDATA_READ(0);
+ size_t i, j;
+
+ for (i = 0; i < size; i += 4) {
+ size_t num = min_t(size_t, size - i, 4);
+ unsigned long value;
+
+ value = tegra_dpaux_readl(dpaux, offset++);
+
+ for (j = 0; j < num; j++)
+ buffer[i + j] = value >> (j * 8);
+ }
+}
+
+static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ unsigned long value = DPAUX_DP_AUXCTL_TRANSACTREQ;
+ unsigned long timeout = msecs_to_jiffies(250);
+ struct tegra_dpaux *dpaux = to_dpaux(aux);
+ unsigned long status;
+ ssize_t ret = 0;
+
+ if (msg->size < 1 || msg->size > 16)
+ return -EINVAL;
+
+ tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
+
+ switch (msg->request & ~DP_AUX_I2C_MOT) {
+ case DP_AUX_I2C_WRITE:
+ if (msg->request & DP_AUX_I2C_MOT)
+ value = DPAUX_DP_AUXCTL_CMD_MOT_WR;
+ else
+ value = DPAUX_DP_AUXCTL_CMD_I2C_WR;
+
+ break;
+
+ case DP_AUX_I2C_READ:
+ if (msg->request & DP_AUX_I2C_MOT)
+ value = DPAUX_DP_AUXCTL_CMD_MOT_RD;
+ else
+ value = DPAUX_DP_AUXCTL_CMD_I2C_RD;
+
+ break;
+
+ case DP_AUX_I2C_STATUS:
+ if (msg->request & DP_AUX_I2C_MOT)
+ value = DPAUX_DP_AUXCTL_CMD_MOT_RQ;
+ else
+ value = DPAUX_DP_AUXCTL_CMD_I2C_RQ;
+
+ break;
+
+ case DP_AUX_NATIVE_WRITE:
+ value = DPAUX_DP_AUXCTL_CMD_AUX_WR;
+ break;
+
+ case DP_AUX_NATIVE_READ:
+ value = DPAUX_DP_AUXCTL_CMD_AUX_RD;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ value |= DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
+ tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
+
+ if ((msg->request & DP_AUX_I2C_READ) == 0) {
+ tegra_dpaux_write_fifo(dpaux, msg->buffer, msg->size);
+ ret = msg->size;
+ }
+
+ /* start transaction */
+ value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXCTL);
+ value |= DPAUX_DP_AUXCTL_TRANSACTREQ;
+ tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
+
+ status = wait_for_completion_timeout(&dpaux->complete, timeout);
+ if (!status)
+ return -ETIMEDOUT;
+
+ /* read status and clear errors */
+ value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
+ tegra_dpaux_writel(dpaux, 0xf00, DPAUX_DP_AUXSTAT);
+
+ if (value & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR)
+ return -ETIMEDOUT;
+
+ if ((value & DPAUX_DP_AUXSTAT_RX_ERROR) ||
+ (value & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR) ||
+ (value & DPAUX_DP_AUXSTAT_NO_STOP_ERROR))
+ return -EIO;
+
+ switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) {
+ case 0x00:
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ break;
+
+ case 0x01:
+ msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+ break;
+
+ case 0x02:
+ msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+ break;
+
+ case 0x04:
+ msg->reply = DP_AUX_I2C_REPLY_NACK;
+ break;
+
+ case 0x08:
+ msg->reply = DP_AUX_I2C_REPLY_DEFER;
+ break;
+ }
+
+ if (msg->reply == DP_AUX_NATIVE_REPLY_ACK) {
+ if (msg->request & DP_AUX_I2C_READ) {
+ size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;
+
+ if (WARN_ON(count != msg->size))
+ count = min_t(size_t, count, msg->size);
+
+ tegra_dpaux_read_fifo(dpaux, msg->buffer, count);
+ ret = count;
+ }
+ }
+
+ return ret;
+}
+
+static irqreturn_t tegra_dpaux_irq(int irq, void *data)
+{
+ struct tegra_dpaux *dpaux = data;
+ irqreturn_t ret = IRQ_HANDLED;
+ unsigned long value;
+
+ /* clear interrupts */
+ value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
+ tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
+
+ if (value & DPAUX_INTR_PLUG_EVENT) {
+ if (dpaux->output) {
+ drm_helper_hpd_irq_event(dpaux->output->connector.dev);
+ }
+ }
+
+ if (value & DPAUX_INTR_UNPLUG_EVENT) {
+ if (dpaux->output)
+ drm_helper_hpd_irq_event(dpaux->output->connector.dev);
+ }
+
+ if (value & DPAUX_INTR_IRQ_EVENT) {
+ /* TODO: handle this */
+ }
+
+ if (value & DPAUX_INTR_AUX_DONE)
+ complete(&dpaux->complete);
+
+ return ret;
+}
+
+static int tegra_dpaux_probe(struct platform_device *pdev)
+{
+ struct tegra_dpaux *dpaux;
+ struct resource *regs;
+ unsigned long value;
+ int err;
+
+ dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL);
+ if (!dpaux)
+ return -ENOMEM;
+
+ init_completion(&dpaux->complete);
+ INIT_LIST_HEAD(&dpaux->list);
+ dpaux->dev = &pdev->dev;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dpaux->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(dpaux->regs))
+ return PTR_ERR(dpaux->regs);
+
+ dpaux->irq = platform_get_irq(pdev, 0);
+ if (dpaux->irq < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ\n");
+ return -ENXIO;
+ }
+
+ dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
+ if (IS_ERR(dpaux->rst))
+ return PTR_ERR(dpaux->rst);
+
+ dpaux->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dpaux->clk))
+ return PTR_ERR(dpaux->clk);
+
+ err = clk_prepare_enable(dpaux->clk);
+ if (err < 0)
+ return err;
+
+ reset_control_deassert(dpaux->rst);
+
+ dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
+ if (IS_ERR(dpaux->clk_parent))
+ return PTR_ERR(dpaux->clk_parent);
+
+ err = clk_prepare_enable(dpaux->clk_parent);
+ if (err < 0)
+ return err;
+
+ err = clk_set_rate(dpaux->clk_parent, 270000000);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
+ err);
+ return err;
+ }
+
+ dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(dpaux->vdd))
+ return PTR_ERR(dpaux->vdd);
+
+ err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
+ dev_name(dpaux->dev), dpaux);
+ if (err < 0) {
+ dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
+ dpaux->irq, err);
+ return err;
+ }
+
+ dpaux->aux.transfer = tegra_dpaux_transfer;
+ dpaux->aux.dev = &pdev->dev;
+
+ err = drm_dp_aux_register_i2c_bus(&dpaux->aux);
+ if (err < 0)
+ return err;
+
+ /* enable and clear all interrupts */
+ value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
+ DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
+ tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX);
+ tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
+
+ mutex_lock(&dpaux_lock);
+ list_add_tail(&dpaux->list, &dpaux_list);
+ mutex_unlock(&dpaux_lock);
+
+ platform_set_drvdata(pdev, dpaux);
+
+ return 0;
+}
+
+static int tegra_dpaux_remove(struct platform_device *pdev)
+{
+ struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
+
+ drm_dp_aux_unregister_i2c_bus(&dpaux->aux);
+
+ mutex_lock(&dpaux_lock);
+ list_del(&dpaux->list);
+ mutex_unlock(&dpaux_lock);
+
+ clk_disable_unprepare(dpaux->clk_parent);
+ reset_control_assert(dpaux->rst);
+ clk_disable_unprepare(dpaux->clk);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_dpaux_of_match[] = {
+ { .compatible = "nvidia,tegra124-dpaux", },
+ { },
+};
+
+struct platform_driver tegra_dpaux_driver = {
+ .driver = {
+ .name = "tegra-dpaux",
+ .of_match_table = tegra_dpaux_of_match,
+ },
+ .probe = tegra_dpaux_probe,
+ .remove = tegra_dpaux_remove,
+};
+
+struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np)
+{
+ struct tegra_dpaux *dpaux;
+
+ mutex_lock(&dpaux_lock);
+
+ list_for_each_entry(dpaux, &dpaux_list, list)
+ if (np == dpaux->dev->of_node) {
+ mutex_unlock(&dpaux_lock);
+ return dpaux;
+ }
+
+ mutex_unlock(&dpaux_lock);
+
+ return NULL;
+}
+
+int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output)
+{
+ unsigned long timeout;
+ int err;
+
+ dpaux->output = output;
+
+ err = regulator_enable(dpaux->vdd);
+ if (err < 0)
+ return err;
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ enum drm_connector_status status;
+
+ status = tegra_dpaux_detect(dpaux);
+ if (status == connector_status_connected)
+ return 0;
+
+ usleep_range(1000, 2000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+int tegra_dpaux_detach(struct tegra_dpaux *dpaux)
+{
+ unsigned long timeout;
+ int err;
+
+ err = regulator_disable(dpaux->vdd);
+ if (err < 0)
+ return err;
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ enum drm_connector_status status;
+
+ status = tegra_dpaux_detect(dpaux);
+ if (status == connector_status_disconnected) {
+ dpaux->output = NULL;
+ return 0;
+ }
+
+ usleep_range(1000, 2000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux)
+{
+ unsigned long value;
+
+ value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
+
+ if (value & DPAUX_DP_AUXSTAT_HPD_STATUS)
+ return connector_status_connected;
+
+ return connector_status_disconnected;
+}
+
+int tegra_dpaux_enable(struct tegra_dpaux *dpaux)
+{
+ unsigned long value;
+
+ value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
+ DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
+ DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
+ DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
+ DPAUX_HYBRID_PADCTL_MODE_AUX;
+ tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);
+
+ value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
+ value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
+ tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
+
+ return 0;
+}
+
+int tegra_dpaux_disable(struct tegra_dpaux *dpaux)
+{
+ unsigned long value;
+
+ value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
+ value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
+ tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
+
+ return 0;
+}
+
+int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding)
+{
+ int err;
+
+ err = drm_dp_dpcd_writeb(&dpaux->aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
+ encoding);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link,
+ u8 pattern)
+{
+ u8 tp = pattern & DP_TRAINING_PATTERN_MASK;
+ u8 status[DP_LINK_STATUS_SIZE], values[4];
+ unsigned int i;
+ int err;
+
+ err = drm_dp_dpcd_writeb(&dpaux->aux, DP_TRAINING_PATTERN_SET, pattern);
+ if (err < 0)
+ return err;
+
+ if (tp == DP_TRAINING_PATTERN_DISABLE)
+ return 0;
+
+ for (i = 0; i < link->num_lanes; i++)
+ values[i] = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED |
+ DP_TRAIN_PRE_EMPHASIS_0 |
+ DP_TRAIN_MAX_SWING_REACHED |
+ DP_TRAIN_VOLTAGE_SWING_400;
+
+ err = drm_dp_dpcd_write(&dpaux->aux, DP_TRAINING_LANE0_SET, values,
+ link->num_lanes);
+ if (err < 0)
+ return err;
+
+ usleep_range(500, 1000);
+
+ err = drm_dp_dpcd_read_link_status(&dpaux->aux, status);
+ if (err < 0)
+ return err;
+
+ switch (tp) {
+ case DP_TRAINING_PATTERN_1:
+ if (!drm_dp_clock_recovery_ok(status, link->num_lanes))
+ return -EAGAIN;
+
+ break;
+
+ case DP_TRAINING_PATTERN_2:
+ if (!drm_dp_channel_eq_ok(status, link->num_lanes))
+ return -EAGAIN;
+
+ break;
+
+ default:
+ dev_err(dpaux->dev, "unsupported training pattern %u\n", tp);
+ return -EINVAL;
+ }
+
+ err = drm_dp_dpcd_writeb(&dpaux->aux, DP_EDP_CONFIGURATION_SET, 0);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/tegra/dpaux.h b/drivers/gpu/drm/tegra/dpaux.h
new file mode 100644
index 000000000000..4f5bf10fdff9
--- /dev/null
+++ b/drivers/gpu/drm/tegra/dpaux.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DRM_TEGRA_DPAUX_H
+#define DRM_TEGRA_DPAUX_H
+
+#define DPAUX_CTXSW 0x00
+
+#define DPAUX_INTR_EN_AUX 0x01
+#define DPAUX_INTR_AUX 0x05
+#define DPAUX_INTR_AUX_DONE (1 << 3)
+#define DPAUX_INTR_IRQ_EVENT (1 << 2)
+#define DPAUX_INTR_UNPLUG_EVENT (1 << 1)
+#define DPAUX_INTR_PLUG_EVENT (1 << 0)
+
+#define DPAUX_DP_AUXDATA_WRITE(x) (0x09 + ((x) << 2))
+#define DPAUX_DP_AUXDATA_READ(x) (0x19 + ((x) << 2))
+#define DPAUX_DP_AUXADDR 0x29
+
+#define DPAUX_DP_AUXCTL 0x2d
+#define DPAUX_DP_AUXCTL_TRANSACTREQ (1 << 16)
+#define DPAUX_DP_AUXCTL_CMD_AUX_RD (9 << 12)
+#define DPAUX_DP_AUXCTL_CMD_AUX_WR (8 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOT_RQ (6 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOT_RD (5 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOT_WR (4 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2C_RQ (2 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2C_RD (1 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2C_WR (0 << 12)
+#define DPAUX_DP_AUXCTL_CMDLEN(x) ((x) & 0xff)
+
+#define DPAUX_DP_AUXSTAT 0x31
+#define DPAUX_DP_AUXSTAT_HPD_STATUS (1 << 28)
+#define DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK (0xf0000)
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR (1 << 11)
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR (1 << 10)
+#define DPAUX_DP_AUXSTAT_RX_ERROR (1 << 9)
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR (1 << 8)
+#define DPAUX_DP_AUXSTAT_REPLY_MASK (0xff)
+
+#define DPAUX_DP_AUX_SINKSTAT_LO 0x35
+#define DPAUX_DP_AUX_SINKSTAT_HI 0x39
+
+#define DPAUX_HPD_CONFIG 0x3d
+#define DPAUX_HPD_CONFIG_UNPLUG_MIN_TIME(x) (((x) & 0xffff) << 16)
+#define DPAUX_HPD_CONFIG_PLUG_MIN_TIME(x) ((x) & 0xffff)
+
+#define DPAUX_HPD_IRQ_CONFIG 0x41
+#define DPAUX_HPD_IRQ_CONFIG_MIN_LOW_TIME(x) ((x) & 0xffff)
+
+#define DPAUX_DP_AUX_CONFIG 0x45
+
+#define DPAUX_HYBRID_PADCTL 0x49
+#define DPAUX_HYBRID_PADCTL_AUX_CMH(x) (((x) & 0x3) << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ(x) (((x) & 0x7) << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVI(x) (((x) & 0x3f) << 2)
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV (1 << 1)
+#define DPAUX_HYBRID_PADCTL_MODE_I2C (1 << 0)
+#define DPAUX_HYBRID_PADCTL_MODE_AUX (0 << 0)
+
+#define DPAUX_HYBRID_SPARE 0x4d
+#define DPAUX_HYBRID_SPARE_PAD_POWER_DOWN (1 << 0)
+
+#define DPAUX_SCRATCH_REG0 0x51
+#define DPAUX_SCRATCH_REG1 0x55
+#define DPAUX_SCRATCH_REG2 0x59
+
+#endif
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index c71594754f46..6f5b6e2f552e 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -665,6 +665,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra114-hdmi", },
{ .compatible = "nvidia,tegra114-gr3d", },
{ .compatible = "nvidia,tegra124-dc", },
+ { .compatible = "nvidia,tegra124-sor", },
{ /* sentinel */ }
};
@@ -691,14 +692,22 @@ static int __init host1x_drm_init(void)
if (err < 0)
goto unregister_dc;
- err = platform_driver_register(&tegra_hdmi_driver);
+ err = platform_driver_register(&tegra_sor_driver);
if (err < 0)
goto unregister_dsi;
- err = platform_driver_register(&tegra_gr2d_driver);
+ err = platform_driver_register(&tegra_hdmi_driver);
+ if (err < 0)
+ goto unregister_sor;
+
+ err = platform_driver_register(&tegra_dpaux_driver);
if (err < 0)
goto unregister_hdmi;
+ err = platform_driver_register(&tegra_gr2d_driver);
+ if (err < 0)
+ goto unregister_dpaux;
+
err = platform_driver_register(&tegra_gr3d_driver);
if (err < 0)
goto unregister_gr2d;
@@ -707,8 +716,12 @@ static int __init host1x_drm_init(void)
unregister_gr2d:
platform_driver_unregister(&tegra_gr2d_driver);
+unregister_dpaux:
+ platform_driver_unregister(&tegra_dpaux_driver);
unregister_hdmi:
platform_driver_unregister(&tegra_hdmi_driver);
+unregister_sor:
+ platform_driver_unregister(&tegra_sor_driver);
unregister_dsi:
platform_driver_unregister(&tegra_dsi_driver);
unregister_dc:
@@ -723,7 +736,9 @@ static void __exit host1x_drm_exit(void)
{
platform_driver_unregister(&tegra_gr3d_driver);
platform_driver_unregister(&tegra_gr2d_driver);
+ platform_driver_unregister(&tegra_dpaux_driver);
platform_driver_unregister(&tegra_hdmi_driver);
+ platform_driver_unregister(&tegra_sor_driver);
platform_driver_unregister(&tegra_dsi_driver);
platform_driver_unregister(&tegra_dc_driver);
host1x_driver_unregister(&host1x_drm_driver);
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index bf1cac7658f8..126332c3ecbb 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -179,12 +179,14 @@ struct tegra_output_ops {
int (*check_mode)(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status);
+ enum drm_connector_status (*detect)(struct tegra_output *output);
};
enum tegra_output_type {
TEGRA_OUTPUT_RGB,
TEGRA_OUTPUT_HDMI,
TEGRA_OUTPUT_DSI,
+ TEGRA_OUTPUT_EDP,
};
struct tegra_output {
@@ -265,6 +267,22 @@ extern int tegra_output_remove(struct tegra_output *output);
extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
extern int tegra_output_exit(struct tegra_output *output);
+/* from dpaux.c */
+
+struct tegra_dpaux;
+struct drm_dp_link;
+struct drm_dp_aux;
+
+struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np);
+enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux);
+int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output);
+int tegra_dpaux_detach(struct tegra_dpaux *dpaux);
+int tegra_dpaux_enable(struct tegra_dpaux *dpaux);
+int tegra_dpaux_disable(struct tegra_dpaux *dpaux);
+int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding);
+int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link,
+ u8 pattern);
+
/* from fb.c */
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
unsigned int index);
@@ -278,7 +296,9 @@ extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_dsi_driver;
+extern struct platform_driver tegra_sor_driver;
extern struct platform_driver tegra_hdmi_driver;
+extern struct platform_driver tegra_dpaux_driver;
extern struct platform_driver tegra_gr2d_driver;
extern struct platform_driver tegra_gr3d_driver;
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index d452faab0235..0e599f0417c0 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1,23 +1,9 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. The copyright holders make no representations
- * about the suitability of this software for any purpose. It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include <linux/clk.h>
diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h
index 00e79c1f448c..1db5cc24ea91 100644
--- a/drivers/gpu/drm/tegra/dsi.h
+++ b/drivers/gpu/drm/tegra/dsi.h
@@ -1,23 +1,9 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. The copyright holders make no representations
- * about the suitability of this software for any purpose. It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#ifndef DRM_TEGRA_DSI_H
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index ef853e558036..bcf9895cef9f 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -8,14 +8,9 @@
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include <linux/dma-buf.h>
@@ -394,6 +389,18 @@ static int tegra_gem_prime_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
return -EINVAL;
}
+static void *tegra_gem_prime_vmap(struct dma_buf *buf)
+{
+ struct drm_gem_object *gem = buf->priv;
+ struct tegra_bo *bo = to_tegra_bo(gem);
+
+ return bo->vaddr;
+}
+
+static void tegra_gem_prime_vunmap(struct dma_buf *buf, void *vaddr)
+{
+}
+
static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = {
.map_dma_buf = tegra_gem_prime_map_dma_buf,
.unmap_dma_buf = tegra_gem_prime_unmap_dma_buf,
@@ -403,6 +410,8 @@ static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = {
.kmap = tegra_gem_prime_kmap,
.kunmap = tegra_gem_prime_kunmap,
.mmap = tegra_gem_prime_mmap,
+ .vmap = tegra_gem_prime_vmap,
+ .vunmap = tegra_gem_prime_vunmap,
};
struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h
index ffd4f792b410..2f3fe96c5154 100644
--- a/drivers/gpu/drm/tegra/gem.h
+++ b/drivers/gpu/drm/tegra/gem.h
@@ -3,17 +3,9 @@
*
* Copyright (c) 2012-2013, NVIDIA 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, see <http://www.gnu.org/licenses/>.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#ifndef __HOST1X_GEM_H
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index 7ec4259ffded..2c7ca748edf5 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -1,17 +1,9 @@
/*
* Copyright (c) 2012-2013, NVIDIA 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, see <http://www.gnu.org/licenses/>.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include <linux/clk.h>
diff --git a/drivers/gpu/drm/tegra/mipi-phy.c b/drivers/gpu/drm/tegra/mipi-phy.c
index e2c4aedaee78..486d19d589c8 100644
--- a/drivers/gpu/drm/tegra/mipi-phy.c
+++ b/drivers/gpu/drm/tegra/mipi-phy.c
@@ -1,23 +1,9 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. The copyright holders make no representations
- * about the suitability of this software for any purpose. It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include <linux/errno.h>
diff --git a/drivers/gpu/drm/tegra/mipi-phy.h b/drivers/gpu/drm/tegra/mipi-phy.h
index d3591694432d..012ea8ac36d7 100644
--- a/drivers/gpu/drm/tegra/mipi-phy.h
+++ b/drivers/gpu/drm/tegra/mipi-phy.h
@@ -1,23 +1,9 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. The copyright holders make no representations
- * about the suitability of this software for any purpose. It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#ifndef DRM_TEGRA_MIPI_PHY_H
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 57cecbd18ca8..a3e4f1eca6f7 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -77,6 +77,9 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
struct tegra_output *output = connector_to_output(connector);
enum drm_connector_status status = connector_status_unknown;
+ if (output->ops->detect)
+ return output->ops->detect(output);
+
if (gpio_is_valid(output->hpd_gpio)) {
if (gpio_get_value(output->hpd_gpio) == 0)
status = connector_status_disconnected;
@@ -292,6 +295,11 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
encoder = DRM_MODE_ENCODER_DSI;
break;
+ case TEGRA_OUTPUT_EDP:
+ connector = DRM_MODE_CONNECTOR_eDP;
+ encoder = DRM_MODE_ENCODER_TMDS;
+ break;
+
default:
connector = DRM_MODE_CONNECTOR_Unknown;
encoder = DRM_MODE_ENCODER_NONE;
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
new file mode 100644
index 000000000000..49ef5729f435
--- /dev/null
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -0,0 +1,1092 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/tegra-powergate.h>
+
+#include <drm/drm_dp_helper.h>
+
+#include "dc.h"
+#include "drm.h"
+#include "sor.h"
+
+struct tegra_sor {
+ struct host1x_client client;
+ struct tegra_output output;
+ struct device *dev;
+
+ void __iomem *regs;
+
+ struct reset_control *rst;
+ struct clk *clk_parent;
+ struct clk *clk_safe;
+ struct clk *clk_dp;
+ struct clk *clk;
+
+ struct tegra_dpaux *dpaux;
+
+ bool enabled;
+};
+
+static inline struct tegra_sor *
+host1x_client_to_sor(struct host1x_client *client)
+{
+ return container_of(client, struct tegra_sor, client);
+}
+
+static inline struct tegra_sor *to_sor(struct tegra_output *output)
+{
+ return container_of(output, struct tegra_sor, output);
+}
+
+static inline unsigned long tegra_sor_readl(struct tegra_sor *sor,
+ unsigned long offset)
+{
+ return readl(sor->regs + (offset << 2));
+}
+
+static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value,
+ unsigned long offset)
+{
+ writel(value, sor->regs + (offset << 2));
+}
+
+static int tegra_sor_dp_train_fast(struct tegra_sor *sor,
+ struct drm_dp_link *link)
+{
+ unsigned long value;
+ unsigned int i;
+ u8 pattern;
+ int err;
+
+ /* setup lane parameters */
+ value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) |
+ SOR_LANE_DRIVE_CURRENT_LANE2(0x40) |
+ SOR_LANE_DRIVE_CURRENT_LANE1(0x40) |
+ SOR_LANE_DRIVE_CURRENT_LANE0(0x40);
+ tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0);
+
+ value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) |
+ SOR_LANE_PREEMPHASIS_LANE2(0x0f) |
+ SOR_LANE_PREEMPHASIS_LANE1(0x0f) |
+ SOR_LANE_PREEMPHASIS_LANE0(0x0f);
+ tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0);
+
+ value = SOR_LANE_POST_CURSOR_LANE3(0x00) |
+ SOR_LANE_POST_CURSOR_LANE2(0x00) |
+ SOR_LANE_POST_CURSOR_LANE1(0x00) |
+ SOR_LANE_POST_CURSOR_LANE0(0x00);
+ tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0);
+
+ /* disable LVDS mode */
+ tegra_sor_writel(sor, 0, SOR_LVDS);
+
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value |= SOR_DP_PADCTL_TX_PU_ENABLE;
+ value &= ~SOR_DP_PADCTL_TX_PU_MASK;
+ value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
+ SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0;
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ usleep_range(10, 100);
+
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
+ SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0);
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B);
+ if (err < 0)
+ return err;
+
+ for (i = 0, value = 0; i < link->num_lanes; i++) {
+ unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
+ SOR_DP_TPG_SCRAMBLER_NONE |
+ SOR_DP_TPG_PATTERN_TRAIN1;
+ value = (value << 8) | lane;
+ }
+
+ tegra_sor_writel(sor, value, SOR_DP_TPG);
+
+ pattern = DP_TRAINING_PATTERN_1;
+
+ err = tegra_dpaux_train(sor->dpaux, link, pattern);
+ if (err < 0)
+ return err;
+
+ value = tegra_sor_readl(sor, SOR_DP_SPARE_0);
+ value |= SOR_DP_SPARE_SEQ_ENABLE;
+ value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
+ value |= SOR_DP_SPARE_MACRO_SOR_CLK;
+ tegra_sor_writel(sor, value, SOR_DP_SPARE_0);
+
+ for (i = 0, value = 0; i < link->num_lanes; i++) {
+ unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
+ SOR_DP_TPG_SCRAMBLER_NONE |
+ SOR_DP_TPG_PATTERN_TRAIN2;
+ value = (value << 8) | lane;
+ }
+
+ tegra_sor_writel(sor, value, SOR_DP_TPG);
+
+ pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2;
+
+ err = tegra_dpaux_train(sor->dpaux, link, pattern);
+ if (err < 0)
+ return err;
+
+ for (i = 0, value = 0; i < link->num_lanes; i++) {
+ unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
+ SOR_DP_TPG_SCRAMBLER_GALIOS |
+ SOR_DP_TPG_PATTERN_NONE;
+ value = (value << 8) | lane;
+ }
+
+ tegra_sor_writel(sor, value, SOR_DP_TPG);
+
+ pattern = DP_TRAINING_PATTERN_DISABLE;
+
+ err = tegra_dpaux_train(sor->dpaux, link, pattern);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void tegra_sor_super_update(struct tegra_sor *sor)
+{
+ tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0);
+ tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0);
+ tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0);
+}
+
+static void tegra_sor_update(struct tegra_sor *sor)
+{
+ tegra_sor_writel(sor, 0, SOR_STATE_0);
+ tegra_sor_writel(sor, 1, SOR_STATE_0);
+ tegra_sor_writel(sor, 0, SOR_STATE_0);
+}
+
+static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout)
+{
+ unsigned long value;
+
+ value = tegra_sor_readl(sor, SOR_PWM_DIV);
+ value &= ~SOR_PWM_DIV_MASK;
+ value |= 0x400; /* period */
+ tegra_sor_writel(sor, value, SOR_PWM_DIV);
+
+ value = tegra_sor_readl(sor, SOR_PWM_CTL);
+ value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK;
+ value |= 0x400; /* duty cycle */
+ value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */
+ value |= SOR_PWM_CTL_TRIGGER;
+ tegra_sor_writel(sor, value, SOR_PWM_CTL);
+
+ timeout = jiffies + msecs_to_jiffies(timeout);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_PWM_CTL);
+ if ((value & SOR_PWM_CTL_TRIGGER) == 0)
+ return 0;
+
+ usleep_range(25, 100);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int tegra_sor_attach(struct tegra_sor *sor)
+{
+ unsigned long value, timeout;
+
+ /* wake up in normal mode */
+ value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+ value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE;
+ value |= SOR_SUPER_STATE_MODE_NORMAL;
+ tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+ tegra_sor_super_update(sor);
+
+ /* attach */
+ value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+ value |= SOR_SUPER_STATE_ATTACHED;
+ tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+ tegra_sor_super_update(sor);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_TEST);
+ if ((value & SOR_TEST_ATTACHED) != 0)
+ return 0;
+
+ usleep_range(25, 100);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int tegra_sor_wakeup(struct tegra_sor *sor)
+{
+ struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
+ unsigned long value, timeout;
+
+ /* enable display controller outputs */
+ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+ value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+ PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+
+ tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ /* wait for head to wake up */
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_TEST);
+ value &= SOR_TEST_HEAD_MODE_MASK;
+
+ if (value == SOR_TEST_HEAD_MODE_AWAKE)
+ return 0;
+
+ usleep_range(25, 100);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
+{
+ unsigned long value;
+
+ value = tegra_sor_readl(sor, SOR_PWR);
+ value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU;
+ tegra_sor_writel(sor, value, SOR_PWR);
+
+ timeout = jiffies + msecs_to_jiffies(timeout);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_PWR);
+ if ((value & SOR_PWR_TRIGGER) == 0)
+ return 0;
+
+ usleep_range(25, 100);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int tegra_output_sor_enable(struct tegra_output *output)
+{
+ struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+ struct drm_display_mode *mode = &dc->base.mode;
+ unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
+ struct tegra_sor *sor = to_sor(output);
+ unsigned long value;
+ int err;
+
+ if (sor->enabled)
+ return 0;
+
+ err = clk_prepare_enable(sor->clk);
+ if (err < 0)
+ return err;
+
+ reset_control_deassert(sor->rst);
+
+ if (sor->dpaux) {
+ err = tegra_dpaux_enable(sor->dpaux);
+ if (err < 0)
+ dev_err(sor->dev, "failed to enable DP: %d\n", err);
+ }
+
+ err = clk_set_parent(sor->clk, sor->clk_safe);
+ if (err < 0)
+ dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
+
+ value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
+ value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
+ value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
+ tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
+
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value &= ~SOR_PLL_2_BANDGAP_POWERDOWN;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+ usleep_range(20, 100);
+
+ value = tegra_sor_readl(sor, SOR_PLL_3);
+ value |= SOR_PLL_3_PLL_VDD_MODE_V3_3;
+ tegra_sor_writel(sor, value, SOR_PLL_3);
+
+ value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST |
+ SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT;
+ tegra_sor_writel(sor, value, SOR_PLL_0);
+
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value |= SOR_PLL_2_SEQ_PLLCAPPD;
+ value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
+ value |= SOR_PLL_2_LVDS_ENABLE;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM;
+ tegra_sor_writel(sor, value, SOR_PLL_1);
+
+ while (true) {
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0)
+ break;
+
+ usleep_range(250, 1000);
+ }
+
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE;
+ value &= ~SOR_PLL_2_PORT_POWERDOWN;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ /*
+ * power up
+ */
+
+ /* set safe link bandwidth (1.62 Gbps) */
+ value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
+ value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
+ value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62;
+ tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
+
+ /* step 1 */
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN |
+ SOR_PLL_2_BANDGAP_POWERDOWN;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ value = tegra_sor_readl(sor, SOR_PLL_0);
+ value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF;
+ tegra_sor_writel(sor, value, SOR_PLL_0);
+
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ /* step 2 */
+ err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to power on I/O rail: %d\n", err);
+ return err;
+ }
+
+ usleep_range(5, 100);
+
+ /* step 3 */
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value &= ~SOR_PLL_2_BANDGAP_POWERDOWN;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ usleep_range(20, 100);
+
+ /* step 4 */
+ value = tegra_sor_readl(sor, SOR_PLL_0);
+ value &= ~SOR_PLL_0_POWER_OFF;
+ value &= ~SOR_PLL_0_VCOPD;
+ tegra_sor_writel(sor, value, SOR_PLL_0);
+
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ usleep_range(200, 1000);
+
+ /* step 5 */
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value &= ~SOR_PLL_2_PORT_POWERDOWN;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ /* switch to DP clock */
+ err = clk_set_parent(sor->clk, sor->clk_dp);
+ if (err < 0)
+ dev_err(sor->dev, "failed to set DP parent clock: %d\n", err);
+
+ /* power dplanes (XXX parameterize based on link?) */
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
+ SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2;
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
+ value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
+ value |= SOR_DP_LINKCTL_LANE_COUNT(4);
+ tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
+
+ /* start lane sequencer */
+ value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
+ SOR_LANE_SEQ_CTL_POWER_STATE_UP;
+ tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
+
+ while (true) {
+ value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
+ if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
+ break;
+
+ usleep_range(250, 1000);
+ }
+
+ /* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */
+ value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
+ value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
+ value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
+ tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
+
+ /* set linkctl */
+ value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
+ value |= SOR_DP_LINKCTL_ENABLE;
+
+ value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
+ value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */
+
+ value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
+ tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
+
+ for (i = 0, value = 0; i < 4; i++) {
+ unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
+ SOR_DP_TPG_SCRAMBLER_GALIOS |
+ SOR_DP_TPG_PATTERN_NONE;
+ value = (value << 8) | lane;
+ }
+
+ tegra_sor_writel(sor, value, SOR_DP_TPG);
+
+ value = tegra_sor_readl(sor, SOR_DP_CONFIG_0);
+ value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
+ value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */
+
+ value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
+ value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */
+
+ value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
+ value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */
+
+ value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */
+
+ value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
+ value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */
+ tegra_sor_writel(sor, value, SOR_DP_CONFIG_0);
+
+ value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
+ value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
+ value |= 137; /* XXX: don't hardcode? */
+ tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
+
+ value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
+ value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
+ value |= 2368; /* XXX: don't hardcode? */
+ tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
+
+ /* enable pad calibration logic */
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value |= SOR_DP_PADCTL_PAD_CAL_PD;
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ if (sor->dpaux) {
+ /* FIXME: properly convert to struct drm_dp_aux */
+ struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux;
+ struct drm_dp_link link;
+ u8 rate, lanes;
+
+ err = drm_dp_link_probe(aux, &link);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to probe eDP link: %d\n",
+ err);
+ return err;
+ }
+
+ err = drm_dp_link_power_up(aux, &link);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to power up eDP link: %d\n",
+ err);
+ return err;
+ }
+
+ err = drm_dp_link_configure(aux, &link);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to configure eDP link: %d\n",
+ err);
+ return err;
+ }
+
+ rate = drm_dp_link_rate_to_bw_code(link.rate);
+ lanes = link.num_lanes;
+
+ value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
+ value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
+ value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate);
+ tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
+
+ value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
+ value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
+ value |= SOR_DP_LINKCTL_LANE_COUNT(lanes);
+
+ if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+ value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
+
+ tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
+
+ /* disable training pattern generator */
+
+ for (i = 0; i < link.num_lanes; i++) {
+ unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
+ SOR_DP_TPG_SCRAMBLER_GALIOS |
+ SOR_DP_TPG_PATTERN_NONE;
+ value = (value << 8) | lane;
+ }
+
+ tegra_sor_writel(sor, value, SOR_DP_TPG);
+
+ err = tegra_sor_dp_train_fast(sor, &link);
+ if (err < 0) {
+ dev_err(sor->dev, "DP fast link training failed: %d\n",
+ err);
+ return err;
+ }
+
+ dev_dbg(sor->dev, "fast link training succeeded\n");
+ }
+
+ err = tegra_sor_power_up(sor, 250);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to power up SOR: %d\n", err);
+ return err;
+ }
+
+ /* start display controller in continuous mode */
+ value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
+ value |= WRITE_MUX;
+ tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
+
+ tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS);
+ tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
+
+ value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
+ value &= ~WRITE_MUX;
+ tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS);
+
+ /*
+ * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
+ * raster, associate with display controller)
+ */
+ value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 |
+ SOR_STATE_ASY_VSYNCPOL |
+ SOR_STATE_ASY_HSYNCPOL |
+ SOR_STATE_ASY_PROTOCOL_DP_A |
+ SOR_STATE_ASY_CRC_MODE_COMPLETE |
+ SOR_STATE_ASY_OWNER(dc->pipe + 1);
+ tegra_sor_writel(sor, value, SOR_STATE_1);
+
+ /*
+ * TODO: The video timing programming below doesn't seem to match the
+ * register definitions.
+ */
+
+ value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
+ tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0));
+
+ vse = mode->vsync_end - mode->vsync_start - 1;
+ hse = mode->hsync_end - mode->hsync_start - 1;
+
+ value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
+ tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0));
+
+ vbe = vse + (mode->vsync_start - mode->vdisplay);
+ hbe = hse + (mode->hsync_start - mode->hdisplay);
+
+ value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
+ tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0));
+
+ vbs = vbe + mode->vdisplay;
+ hbs = hbe + mode->hdisplay;
+
+ value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
+ tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
+
+ /* XXX interlaced mode */
+ tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0));
+
+ /* CSTM (LVDS, link A/B, upper) */
+ value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B |
+ SOR_CSTM_UPPER;
+ tegra_sor_writel(sor, value, SOR_CSTM);
+
+ /* PWM setup */
+ err = tegra_sor_setup_pwm(sor, 250);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to setup PWM: %d\n", err);
+ return err;
+ }
+
+ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+ value |= SOR_ENABLE;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+ tegra_sor_update(sor);
+
+ err = tegra_sor_attach(sor);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to attach SOR: %d\n", err);
+ return err;
+ }
+
+ err = tegra_sor_wakeup(sor);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to enable DC: %d\n", err);
+ return err;
+ }
+
+ sor->enabled = true;
+
+ return 0;
+}
+
+static int tegra_sor_detach(struct tegra_sor *sor)
+{
+ unsigned long value, timeout;
+
+ /* switch to safe mode */
+ value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+ value &= ~SOR_SUPER_STATE_MODE_NORMAL;
+ tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+ tegra_sor_super_update(sor);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_PWR);
+ if (value & SOR_PWR_MODE_SAFE)
+ break;
+ }
+
+ if ((value & SOR_PWR_MODE_SAFE) == 0)
+ return -ETIMEDOUT;
+
+ /* go to sleep */
+ value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+ value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
+ tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+ tegra_sor_super_update(sor);
+
+ /* detach */
+ value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
+ value &= ~SOR_SUPER_STATE_ATTACHED;
+ tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
+ tegra_sor_super_update(sor);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_TEST);
+ if ((value & SOR_TEST_ATTACHED) == 0)
+ break;
+
+ usleep_range(25, 100);
+ }
+
+ if ((value & SOR_TEST_ATTACHED) != 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int tegra_sor_power_down(struct tegra_sor *sor)
+{
+ unsigned long value, timeout;
+ int err;
+
+ value = tegra_sor_readl(sor, SOR_PWR);
+ value &= ~SOR_PWR_NORMAL_STATE_PU;
+ value |= SOR_PWR_TRIGGER;
+ tegra_sor_writel(sor, value, SOR_PWR);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_PWR);
+ if ((value & SOR_PWR_TRIGGER) == 0)
+ return 0;
+
+ usleep_range(25, 100);
+ }
+
+ if ((value & SOR_PWR_TRIGGER) != 0)
+ return -ETIMEDOUT;
+
+ err = clk_set_parent(sor->clk, sor->clk_safe);
+ if (err < 0)
+ dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
+
+ value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
+ value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
+ SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
+ tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
+
+ /* stop lane sequencer */
+ value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
+ SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
+ tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
+
+ timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
+ if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
+ break;
+
+ usleep_range(25, 100);
+ }
+
+ if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
+ return -ETIMEDOUT;
+
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value |= SOR_PLL_2_PORT_POWERDOWN;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ usleep_range(20, 100);
+
+ value = tegra_sor_readl(sor, SOR_PLL_0);
+ value |= SOR_PLL_0_POWER_OFF;
+ value |= SOR_PLL_0_VCOPD;
+ tegra_sor_writel(sor, value, SOR_PLL_0);
+
+ value = tegra_sor_readl(sor, SOR_PLL_2);
+ value |= SOR_PLL_2_SEQ_PLLCAPPD;
+ value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
+ tegra_sor_writel(sor, value, SOR_PLL_2);
+
+ usleep_range(20, 100);
+
+ return 0;
+}
+
+static int tegra_output_sor_disable(struct tegra_output *output)
+{
+ struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+ struct tegra_sor *sor = to_sor(output);
+ unsigned long value;
+ int err;
+
+ if (!sor->enabled)
+ return 0;
+
+ err = tegra_sor_detach(sor);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to detach SOR: %d\n", err);
+ return err;
+ }
+
+ tegra_sor_writel(sor, 0, SOR_STATE_1);
+ tegra_sor_update(sor);
+
+ /*
+ * The following accesses registers of the display controller, so make
+ * sure it's only executed when the output is attached to one.
+ */
+ if (dc) {
+ /*
+ * XXX: We can't do this here because it causes the SOR to go
+ * into an erroneous state and the output will look scrambled
+ * the next time it is enabled. Presumably this is because we
+ * should be doing this only on the next VBLANK. A possible
+ * solution would be to queue a "power-off" event to trigger
+ * this code to be run during the next VBLANK.
+ */
+ /*
+ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
+ value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+ PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+ */
+
+ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+ value &= ~DISP_CTRL_MODE_MASK;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+ value &= ~SOR_ENABLE;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+ tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+ }
+
+ err = tegra_sor_power_down(sor);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to power down SOR: %d\n", err);
+ return err;
+ }
+
+ if (sor->dpaux) {
+ err = tegra_dpaux_disable(sor->dpaux);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to disable DP: %d\n", err);
+ return err;
+ }
+ }
+
+ err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to power off I/O rail: %d\n", err);
+ return err;
+ }
+
+ reset_control_assert(sor->rst);
+ clk_disable_unprepare(sor->clk);
+
+ sor->enabled = false;
+
+ return 0;
+}
+
+static int tegra_output_sor_setup_clock(struct tegra_output *output,
+ struct clk *clk, unsigned long pclk)
+{
+ struct tegra_sor *sor = to_sor(output);
+ int err;
+
+ /* round to next MHz */
+ pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000;
+
+ err = clk_set_parent(clk, sor->clk_parent);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to set parent clock: %d\n", err);
+ return err;
+ }
+
+ err = clk_set_rate(sor->clk_parent, pclk);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n",
+ pclk * 2);
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_output_sor_check_mode(struct tegra_output *output,
+ struct drm_display_mode *mode,
+ enum drm_mode_status *status)
+{
+ /*
+ * FIXME: For now, always assume that the mode is okay.
+ */
+
+ *status = MODE_OK;
+
+ return 0;
+}
+
+static enum drm_connector_status
+tegra_output_sor_detect(struct tegra_output *output)
+{
+ struct tegra_sor *sor = to_sor(output);
+
+ if (sor->dpaux)
+ return tegra_dpaux_detect(sor->dpaux);
+
+ return connector_status_unknown;
+}
+
+static const struct tegra_output_ops sor_ops = {
+ .enable = tegra_output_sor_enable,
+ .disable = tegra_output_sor_disable,
+ .setup_clock = tegra_output_sor_setup_clock,
+ .check_mode = tegra_output_sor_check_mode,
+ .detect = tegra_output_sor_detect,
+};
+
+static int tegra_sor_init(struct host1x_client *client)
+{
+ struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct tegra_sor *sor = host1x_client_to_sor(client);
+ int err;
+
+ if (!sor->dpaux)
+ return -ENODEV;
+
+ sor->output.type = TEGRA_OUTPUT_EDP;
+
+ sor->output.dev = sor->dev;
+ sor->output.ops = &sor_ops;
+
+ err = tegra_output_init(tegra->drm, &sor->output);
+ if (err < 0) {
+ dev_err(sor->dev, "output setup failed: %d\n", err);
+ return err;
+ }
+
+ if (sor->dpaux) {
+ err = tegra_dpaux_attach(sor->dpaux, &sor->output);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to attach DP: %d\n", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int tegra_sor_exit(struct host1x_client *client)
+{
+ struct tegra_sor *sor = host1x_client_to_sor(client);
+ int err;
+
+ err = tegra_output_disable(&sor->output);
+ if (err < 0) {
+ dev_err(sor->dev, "output failed to disable: %d\n", err);
+ return err;
+ }
+
+ if (sor->dpaux) {
+ err = tegra_dpaux_detach(sor->dpaux);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to detach DP: %d\n", err);
+ return err;
+ }
+ }
+
+ err = tegra_output_exit(&sor->output);
+ if (err < 0) {
+ dev_err(sor->dev, "output cleanup failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct host1x_client_ops sor_client_ops = {
+ .init = tegra_sor_init,
+ .exit = tegra_sor_exit,
+};
+
+static int tegra_sor_probe(struct platform_device *pdev)
+{
+ struct device_node *np;
+ struct tegra_sor *sor;
+ struct resource *regs;
+ int err;
+
+ sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL);
+ if (!sor)
+ return -ENOMEM;
+
+ sor->output.dev = sor->dev = &pdev->dev;
+
+ np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
+ if (np) {
+ sor->dpaux = tegra_dpaux_find_by_of_node(np);
+ of_node_put(np);
+
+ if (!sor->dpaux)
+ return -EPROBE_DEFER;
+ }
+
+ err = tegra_output_probe(&sor->output);
+ if (err < 0)
+ return err;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sor->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(sor->regs))
+ return PTR_ERR(sor->regs);
+
+ sor->rst = devm_reset_control_get(&pdev->dev, "sor");
+ if (IS_ERR(sor->rst))
+ return PTR_ERR(sor->rst);
+
+ sor->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sor->clk))
+ return PTR_ERR(sor->clk);
+
+ sor->clk_parent = devm_clk_get(&pdev->dev, "parent");
+ if (IS_ERR(sor->clk_parent))
+ return PTR_ERR(sor->clk_parent);
+
+ err = clk_prepare_enable(sor->clk_parent);
+ if (err < 0)
+ return err;
+
+ sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
+ if (IS_ERR(sor->clk_safe))
+ return PTR_ERR(sor->clk_safe);
+
+ err = clk_prepare_enable(sor->clk_safe);
+ if (err < 0)
+ return err;
+
+ sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
+ if (IS_ERR(sor->clk_dp))
+ return PTR_ERR(sor->clk_dp);
+
+ err = clk_prepare_enable(sor->clk_dp);
+ if (err < 0)
+ return err;
+
+ INIT_LIST_HEAD(&sor->client.list);
+ sor->client.ops = &sor_client_ops;
+ sor->client.dev = &pdev->dev;
+
+ err = host1x_client_register(&sor->client);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register host1x client: %d\n",
+ err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, sor);
+
+ return 0;
+}
+
+static int tegra_sor_remove(struct platform_device *pdev)
+{
+ struct tegra_sor *sor = platform_get_drvdata(pdev);
+ int err;
+
+ err = host1x_client_unregister(&sor->client);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+ err);
+ return err;
+ }
+
+ clk_disable_unprepare(sor->clk_parent);
+ clk_disable_unprepare(sor->clk_safe);
+ clk_disable_unprepare(sor->clk_dp);
+ clk_disable_unprepare(sor->clk);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_sor_of_match[] = {
+ { .compatible = "nvidia,tegra124-sor", },
+ { },
+};
+
+struct platform_driver tegra_sor_driver = {
+ .driver = {
+ .name = "tegra-sor",
+ .of_match_table = tegra_sor_of_match,
+ },
+ .probe = tegra_sor_probe,
+ .remove = tegra_sor_remove,
+};
diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h
new file mode 100644
index 000000000000..f4156d54cd05
--- /dev/null
+++ b/drivers/gpu/drm/tegra/sor.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DRM_TEGRA_SOR_H
+#define DRM_TEGRA_SOR_H
+
+#define SOR_CTXSW 0x00
+
+#define SOR_SUPER_STATE_0 0x01
+
+#define SOR_SUPER_STATE_1 0x02
+#define SOR_SUPER_STATE_ATTACHED (1 << 3)
+#define SOR_SUPER_STATE_MODE_NORMAL (1 << 2)
+#define SOR_SUPER_STATE_HEAD_MODE_MASK (3 << 0)
+#define SOR_SUPER_STATE_HEAD_MODE_AWAKE (2 << 0)
+#define SOR_SUPER_STATE_HEAD_MODE_SNOOZE (1 << 0)
+#define SOR_SUPER_STATE_HEAD_MODE_SLEEP (0 << 0)
+
+#define SOR_STATE_0 0x03
+
+#define SOR_STATE_1 0x04
+#define SOR_STATE_ASY_PIXELDEPTH_MASK (0xf << 17)
+#define SOR_STATE_ASY_PIXELDEPTH_BPP_18_444 (0x2 << 17)
+#define SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 (0x5 << 17)
+#define SOR_STATE_ASY_VSYNCPOL (1 << 13)
+#define SOR_STATE_ASY_HSYNCPOL (1 << 12)
+#define SOR_STATE_ASY_PROTOCOL_MASK (0xf << 8)
+#define SOR_STATE_ASY_PROTOCOL_CUSTOM (0xf << 8)
+#define SOR_STATE_ASY_PROTOCOL_DP_A (0x8 << 8)
+#define SOR_STATE_ASY_PROTOCOL_DP_B (0x9 << 8)
+#define SOR_STATE_ASY_PROTOCOL_LVDS (0x0 << 8)
+#define SOR_STATE_ASY_CRC_MODE_MASK (0x3 << 6)
+#define SOR_STATE_ASY_CRC_MODE_NON_ACTIVE (0x2 << 6)
+#define SOR_STATE_ASY_CRC_MODE_COMPLETE (0x1 << 6)
+#define SOR_STATE_ASY_CRC_MODE_ACTIVE (0x0 << 6)
+#define SOR_STATE_ASY_OWNER(x) (((x) & 0xf) << 0)
+
+#define SOR_HEAD_STATE_0(x) (0x05 + (x))
+#define SOR_HEAD_STATE_1(x) (0x07 + (x))
+#define SOR_HEAD_STATE_2(x) (0x09 + (x))
+#define SOR_HEAD_STATE_3(x) (0x0b + (x))
+#define SOR_HEAD_STATE_4(x) (0x0d + (x))
+#define SOR_HEAD_STATE_5(x) (0x0f + (x))
+#define SOR_CRC_CNTRL 0x11
+#define SOR_DP_DEBUG_MVID 0x12
+
+#define SOR_CLK_CNTRL 0x13
+#define SOR_CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2)
+#define SOR_CLK_CNTRL_DP_LINK_SPEED(x) (((x) & 0x1f) << 2)
+#define SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62 (0x06 << 2)
+#define SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70 (0x0a << 2)
+#define SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40 (0x14 << 2)
+#define SOR_CLK_CNTRL_DP_CLK_SEL_MASK (3 << 0)
+#define SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK (0 << 0)
+#define SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK (1 << 0)
+#define SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK (2 << 0)
+#define SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK (3 << 0)
+
+#define SOR_CAP 0x14
+
+#define SOR_PWR 0x15
+#define SOR_PWR_TRIGGER (1 << 31)
+#define SOR_PWR_MODE_SAFE (1 << 28)
+#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
+
+#define SOR_TEST 0x16
+#define SOR_TEST_ATTACHED (1 << 10)
+#define SOR_TEST_HEAD_MODE_MASK (3 << 8)
+#define SOR_TEST_HEAD_MODE_AWAKE (2 << 8)
+
+#define SOR_PLL_0 0x17
+#define SOR_PLL_0_ICHPMP_MASK (0xf << 24)
+#define SOR_PLL_0_ICHPMP(x) (((x) & 0xf) << 24)
+#define SOR_PLL_0_VCOCAP_MASK (0xf << 8)
+#define SOR_PLL_0_VCOCAP(x) (((x) & 0xf) << 8)
+#define SOR_PLL_0_VCOCAP_RST SOR_PLL_0_VCOCAP(3)
+#define SOR_PLL_0_PLLREG_MASK (0x3 << 6)
+#define SOR_PLL_0_PLLREG_LEVEL(x) (((x) & 0x3) << 6)
+#define SOR_PLL_0_PLLREG_LEVEL_V25 SOR_PLL_0_PLLREG_LEVEL(0)
+#define SOR_PLL_0_PLLREG_LEVEL_V15 SOR_PLL_0_PLLREG_LEVEL(1)
+#define SOR_PLL_0_PLLREG_LEVEL_V35 SOR_PLL_0_PLLREG_LEVEL(2)
+#define SOR_PLL_0_PLLREG_LEVEL_V45 SOR_PLL_0_PLLREG_LEVEL(3)
+#define SOR_PLL_0_PULLDOWN (1 << 5)
+#define SOR_PLL_0_RESISTOR_EXT (1 << 4)
+#define SOR_PLL_0_VCOPD (1 << 2)
+#define SOR_PLL_0_POWER_OFF (1 << 0)
+
+#define SOR_PLL_1 0x18
+/* XXX: read-only bit? */
+#define SOR_PLL_1_TERM_COMPOUT (1 << 15)
+#define SOR_PLL_1_TMDS_TERM (1 << 8)
+
+#define SOR_PLL_2 0x19
+#define SOR_PLL_2_LVDS_ENABLE (1 << 25)
+#define SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE (1 << 24)
+#define SOR_PLL_2_PORT_POWERDOWN (1 << 23)
+#define SOR_PLL_2_BANDGAP_POWERDOWN (1 << 22)
+#define SOR_PLL_2_POWERDOWN_OVERRIDE (1 << 18)
+#define SOR_PLL_2_SEQ_PLLCAPPD (1 << 17)
+
+#define SOR_PLL_3 0x1a
+#define SOR_PLL_3_PLL_VDD_MODE_V1_8 (0 << 13)
+#define SOR_PLL_3_PLL_VDD_MODE_V3_3 (1 << 13)
+
+#define SOR_CSTM 0x1b
+#define SOR_CSTM_LVDS (1 << 16)
+#define SOR_CSTM_LINK_ACT_B (1 << 15)
+#define SOR_CSTM_LINK_ACT_A (1 << 14)
+#define SOR_CSTM_UPPER (1 << 11)
+
+#define SOR_LVDS 0x1c
+#define SOR_CRC_A 0x1d
+#define SOR_CRC_B 0x1e
+#define SOR_BLANK 0x1f
+#define SOR_SEQ_CTL 0x20
+
+#define SOR_LANE_SEQ_CTL 0x21
+#define SOR_LANE_SEQ_CTL_TRIGGER (1 << 31)
+#define SOR_LANE_SEQ_CTL_SEQUENCE_UP (0 << 20)
+#define SOR_LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20)
+#define SOR_LANE_SEQ_CTL_POWER_STATE_UP (0 << 16)
+#define SOR_LANE_SEQ_CTL_POWER_STATE_DOWN (1 << 16)
+
+#define SOR_SEQ_INST(x) (0x22 + (x))
+
+#define SOR_PWM_DIV 0x32
+#define SOR_PWM_DIV_MASK 0xffffff
+
+#define SOR_PWM_CTL 0x33
+#define SOR_PWM_CTL_TRIGGER (1 << 31)
+#define SOR_PWM_CTL_CLK_SEL (1 << 30)
+#define SOR_PWM_CTL_DUTY_CYCLE_MASK 0xffffff
+
+#define SOR_VCRC_A_0 0x34
+#define SOR_VCRC_A_1 0x35
+#define SOR_VCRC_B_0 0x36
+#define SOR_VCRC_B_1 0x37
+#define SOR_CCRC_A_0 0x38
+#define SOR_CCRC_A_1 0x39
+#define SOR_CCRC_B_0 0x3a
+#define SOR_CCRC_B_1 0x3b
+#define SOR_EDATA_A_0 0x3c
+#define SOR_EDATA_A_1 0x3d
+#define SOR_EDATA_B_0 0x3e
+#define SOR_EDATA_B_1 0x3f
+#define SOR_COUNT_A_0 0x40
+#define SOR_COUNT_A_1 0x41
+#define SOR_COUNT_B_0 0x42
+#define SOR_COUNT_B_1 0x43
+#define SOR_DEBUG_A_0 0x44
+#define SOR_DEBUG_A_1 0x45
+#define SOR_DEBUG_B_0 0x46
+#define SOR_DEBUG_B_1 0x47
+#define SOR_TRIG 0x48
+#define SOR_MSCHECK 0x49
+#define SOR_XBAR_CTRL 0x4a
+#define SOR_XBAR_POL 0x4b
+
+#define SOR_DP_LINKCTL_0 0x4c
+#define SOR_DP_LINKCTL_LANE_COUNT_MASK (0x1f << 16)
+#define SOR_DP_LINKCTL_LANE_COUNT(x) (((1 << (x)) - 1) << 16)
+#define SOR_DP_LINKCTL_ENHANCED_FRAME (1 << 14)
+#define SOR_DP_LINKCTL_TU_SIZE_MASK (0x7f << 2)
+#define SOR_DP_LINKCTL_TU_SIZE(x) (((x) & 0x7f) << 2)
+#define SOR_DP_LINKCTL_ENABLE (1 << 0)
+
+#define SOR_DP_LINKCTL_1 0x4d
+
+#define SOR_LANE_DRIVE_CURRENT_0 0x4e
+#define SOR_LANE_DRIVE_CURRENT_1 0x4f
+#define SOR_LANE4_DRIVE_CURRENT_0 0x50
+#define SOR_LANE4_DRIVE_CURRENT_1 0x51
+#define SOR_LANE_DRIVE_CURRENT_LANE3(x) (((x) & 0xff) << 24)
+#define SOR_LANE_DRIVE_CURRENT_LANE2(x) (((x) & 0xff) << 16)
+#define SOR_LANE_DRIVE_CURRENT_LANE1(x) (((x) & 0xff) << 8)
+#define SOR_LANE_DRIVE_CURRENT_LANE0(x) (((x) & 0xff) << 0)
+
+#define SOR_LANE_PREEMPHASIS_0 0x52
+#define SOR_LANE_PREEMPHASIS_1 0x53
+#define SOR_LANE4_PREEMPHASIS_0 0x54
+#define SOR_LANE4_PREEMPHASIS_1 0x55
+#define SOR_LANE_PREEMPHASIS_LANE3(x) (((x) & 0xff) << 24)
+#define SOR_LANE_PREEMPHASIS_LANE2(x) (((x) & 0xff) << 16)
+#define SOR_LANE_PREEMPHASIS_LANE1(x) (((x) & 0xff) << 8)
+#define SOR_LANE_PREEMPHASIS_LANE0(x) (((x) & 0xff) << 0)
+
+#define SOR_LANE_POST_CURSOR_0 0x56
+#define SOR_LANE_POST_CURSOR_1 0x57
+#define SOR_LANE_POST_CURSOR_LANE3(x) (((x) & 0xff) << 24)
+#define SOR_LANE_POST_CURSOR_LANE2(x) (((x) & 0xff) << 16)
+#define SOR_LANE_POST_CURSOR_LANE1(x) (((x) & 0xff) << 8)
+#define SOR_LANE_POST_CURSOR_LANE0(x) (((x) & 0xff) << 0)
+
+#define SOR_DP_CONFIG_0 0x58
+#define SOR_DP_CONFIG_DISPARITY_NEGATIVE (1 << 31)
+#define SOR_DP_CONFIG_ACTIVE_SYM_ENABLE (1 << 26)
+#define SOR_DP_CONFIG_ACTIVE_SYM_POLARITY (1 << 24)
+#define SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK (0xf << 16)
+#define SOR_DP_CONFIG_ACTIVE_SYM_FRAC(x) (((x) & 0xf) << 16)
+#define SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK (0x7f << 8)
+#define SOR_DP_CONFIG_ACTIVE_SYM_COUNT(x) (((x) & 0x7f) << 8)
+#define SOR_DP_CONFIG_WATERMARK_MASK (0x3f << 0)
+#define SOR_DP_CONFIG_WATERMARK(x) (((x) & 0x3f) << 0)
+
+#define SOR_DP_CONFIG_1 0x59
+#define SOR_DP_MN_0 0x5a
+#define SOR_DP_MN_1 0x5b
+
+#define SOR_DP_PADCTL_0 0x5c
+#define SOR_DP_PADCTL_PAD_CAL_PD (1 << 23)
+#define SOR_DP_PADCTL_TX_PU_ENABLE (1 << 22)
+#define SOR_DP_PADCTL_TX_PU_MASK (0xff << 8)
+#define SOR_DP_PADCTL_TX_PU(x) (((x) & 0xff) << 8)
+#define SOR_DP_PADCTL_CM_TXD_3 (1 << 7)
+#define SOR_DP_PADCTL_CM_TXD_2 (1 << 6)
+#define SOR_DP_PADCTL_CM_TXD_1 (1 << 5)
+#define SOR_DP_PADCTL_CM_TXD_0 (1 << 4)
+#define SOR_DP_PADCTL_PD_TXD_3 (1 << 3)
+#define SOR_DP_PADCTL_PD_TXD_0 (1 << 2)
+#define SOR_DP_PADCTL_PD_TXD_1 (1 << 1)
+#define SOR_DP_PADCTL_PD_TXD_2 (1 << 0)
+
+#define SOR_DP_PADCTL_1 0x5d
+
+#define SOR_DP_DEBUG_0 0x5e
+#define SOR_DP_DEBUG_1 0x5f
+
+#define SOR_DP_SPARE_0 0x60
+#define SOR_DP_SPARE_MACRO_SOR_CLK (1 << 2)
+#define SOR_DP_SPARE_PANEL_INTERNAL (1 << 1)
+#define SOR_DP_SPARE_SEQ_ENABLE (1 << 0)
+
+#define SOR_DP_SPARE_1 0x61
+#define SOR_DP_AUDIO_CTRL 0x62
+
+#define SOR_DP_AUDIO_HBLANK_SYMBOLS 0x63
+#define SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK (0x01ffff << 0)
+
+#define SOR_DP_AUDIO_VBLANK_SYMBOLS 0x64
+#define SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK (0x1fffff << 0)
+
+#define SOR_DP_GENERIC_INFOFRAME_HEADER 0x65
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_0 0x66
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_1 0x67
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_2 0x68
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_3 0x69
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_4 0x6a
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_5 0x6b
+#define SOR_DP_GENERIC_INFOFRAME_SUBPACK_6 0x6c
+
+#define SOR_DP_TPG 0x6d
+#define SOR_DP_TPG_CHANNEL_CODING (1 << 6)
+#define SOR_DP_TPG_SCRAMBLER_MASK (3 << 4)
+#define SOR_DP_TPG_SCRAMBLER_FIBONACCI (2 << 4)
+#define SOR_DP_TPG_SCRAMBLER_GALIOS (1 << 4)
+#define SOR_DP_TPG_SCRAMBLER_NONE (0 << 4)
+#define SOR_DP_TPG_PATTERN_MASK (0xf << 0)
+#define SOR_DP_TPG_PATTERN_HBR2 (0x8 << 0)
+#define SOR_DP_TPG_PATTERN_CSTM (0x7 << 0)
+#define SOR_DP_TPG_PATTERN_PRBS7 (0x6 << 0)
+#define SOR_DP_TPG_PATTERN_SBLERRRATE (0x5 << 0)
+#define SOR_DP_TPG_PATTERN_D102 (0x4 << 0)
+#define SOR_DP_TPG_PATTERN_TRAIN3 (0x3 << 0)
+#define SOR_DP_TPG_PATTERN_TRAIN2 (0x2 << 0)
+#define SOR_DP_TPG_PATTERN_TRAIN1 (0x1 << 0)
+#define SOR_DP_TPG_PATTERN_NONE (0x0 << 0)
+
+#define SOR_DP_TPG_CONFIG 0x6e
+#define SOR_DP_LQ_CSTM_0 0x6f
+#define SOR_DP_LQ_CSTM_1 0x70
+#define SOR_DP_LQ_CSTM_2 0x71
+
+#endif
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index d36efc13b16f..d642d4a02134 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -74,7 +74,7 @@ static void set_scanout(struct drm_crtc *crtc, int n)
drm_flip_work_queue(&tilcdc_crtc->unref_work, tilcdc_crtc->scanout[n]);
drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
}
- tilcdc_crtc->scanout[n] = crtc->fb;
+ tilcdc_crtc->scanout[n] = crtc->primary->fb;
drm_framebuffer_reference(tilcdc_crtc->scanout[n]);
tilcdc_crtc->dirty &= ~stat[n];
pm_runtime_put_sync(dev->dev);
@@ -84,7 +84,7 @@ static void update_scanout(struct drm_crtc *crtc)
{
struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
struct drm_device *dev = crtc->dev;
- struct drm_framebuffer *fb = crtc->fb;
+ struct drm_framebuffer *fb = crtc->primary->fb;
struct drm_gem_cma_object *gem;
unsigned int depth, bpp;
@@ -159,7 +159,7 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
return -EBUSY;
}
- crtc->fb = fb;
+ crtc->primary->fb = fb;
tilcdc_crtc->event = event;
update_scanout(crtc);
@@ -339,7 +339,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
if (priv->rev == 2) {
unsigned int depth, bpp;
- drm_fb_get_bpp_depth(crtc->fb->pixel_format, &depth, &bpp);
+ drm_fb_get_bpp_depth(crtc->primary->fb->pixel_format, &depth, &bpp);
switch (bpp) {
case 16:
break;
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 214b7992a3aa..4ab9f7171c4f 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -412,7 +412,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
int ret;
spin_lock(&glob->lru_lock);
- ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
+ ret = __ttm_bo_reserve(bo, false, true, false, 0);
spin_lock(&bdev->fence_lock);
(void) ttm_bo_wait(bo, false, false, true);
@@ -443,7 +443,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
ttm_bo_add_to_lru(bo);
}
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
}
kref_get(&bo->list_kref);
@@ -494,7 +494,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
sync_obj = driver->sync_obj_ref(bo->sync_obj);
spin_unlock(&bdev->fence_lock);
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
spin_unlock(&glob->lru_lock);
ret = driver->sync_obj_wait(sync_obj, false, interruptible);
@@ -514,7 +514,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
return ret;
spin_lock(&glob->lru_lock);
- ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
+ ret = __ttm_bo_reserve(bo, false, true, false, 0);
/*
* We raced, and lost, someone else holds the reservation now,
@@ -532,7 +532,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
spin_unlock(&bdev->fence_lock);
if (ret || unlikely(list_empty(&bo->ddestroy))) {
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
spin_unlock(&glob->lru_lock);
return ret;
}
@@ -577,11 +577,11 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
kref_get(&nentry->list_kref);
}
- ret = ttm_bo_reserve_nolru(entry, false, true, false, 0);
+ ret = __ttm_bo_reserve(entry, false, true, false, 0);
if (remove_all && ret) {
spin_unlock(&glob->lru_lock);
- ret = ttm_bo_reserve_nolru(entry, false, false,
- false, 0);
+ ret = __ttm_bo_reserve(entry, false, false,
+ false, 0);
spin_lock(&glob->lru_lock);
}
@@ -726,7 +726,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &man->lru, lru) {
- ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
+ ret = __ttm_bo_reserve(bo, false, true, false, 0);
if (!ret)
break;
}
@@ -1451,6 +1451,7 @@ EXPORT_SYMBOL(ttm_bo_device_release);
int ttm_bo_device_init(struct ttm_bo_device *bdev,
struct ttm_bo_global *glob,
struct ttm_bo_driver *driver,
+ struct address_space *mapping,
uint64_t file_page_offset,
bool need_dma32)
{
@@ -1472,7 +1473,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
0x10000000);
INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
INIT_LIST_HEAD(&bdev->ddestroy);
- bdev->dev_mapping = NULL;
+ bdev->dev_mapping = mapping;
bdev->glob = glob;
bdev->need_dma32 = need_dma32;
bdev->val_seq = 0;
@@ -1629,7 +1630,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &glob->swap_lru, swap) {
- ret = ttm_bo_reserve_nolru(bo, false, true, false, 0);
+ ret = __ttm_bo_reserve(bo, false, true, false, 0);
if (!ret)
break;
}
@@ -1696,7 +1697,7 @@ out:
* already swapped buffer.
*/
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
kref_put(&bo->list_kref, ttm_bo_release_list);
return ret;
}
@@ -1730,10 +1731,10 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo)
return -ERESTARTSYS;
if (!ww_mutex_is_locked(&bo->resv->lock))
goto out_unlock;
- ret = ttm_bo_reserve_nolru(bo, true, false, false, NULL);
+ ret = __ttm_bo_reserve(bo, true, false, false, NULL);
if (unlikely(ret != 0))
goto out_unlock;
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
out_unlock:
mutex_unlock(&bo->wu_mutex);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c
index c58eba33bd5f..bd850c9f4bca 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_manager.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c
@@ -55,6 +55,7 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;
struct drm_mm *mm = &rman->mm;
struct drm_mm_node *node = NULL;
+ enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT;
unsigned long lpfn;
int ret;
@@ -66,11 +67,15 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
if (!node)
return -ENOMEM;
+ if (bo->mem.placement & TTM_PL_FLAG_TOPDOWN)
+ aflags = DRM_MM_CREATE_TOP;
+
spin_lock(&rman->lock);
- ret = drm_mm_insert_node_in_range(mm, node, mem->num_pages,
- mem->page_alignment,
+ ret = drm_mm_insert_node_in_range_generic(mm, node, mem->num_pages,
+ mem->page_alignment, 0,
placement->fpfn, lpfn,
- DRM_MM_SEARCH_BEST);
+ DRM_MM_SEARCH_BEST,
+ aflags);
spin_unlock(&rman->lock);
if (unlikely(ret)) {
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
index 479e9418e3d7..e8dac8758528 100644
--- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
@@ -46,7 +46,7 @@ static void ttm_eu_backoff_reservation_locked(struct list_head *list)
ttm_bo_add_to_lru(bo);
entry->removed = false;
}
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
}
}
@@ -140,8 +140,8 @@ retry:
if (entry->reserved)
continue;
- ret = ttm_bo_reserve_nolru(bo, true, (ticket == NULL), true,
- ticket);
+ ret = __ttm_bo_reserve(bo, true, (ticket == NULL), true,
+ ticket);
if (ret == -EDEADLK) {
/* uh oh, we lost out, drop every reservation and try
@@ -224,7 +224,7 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
entry->old_sync_obj = bo->sync_obj;
bo->sync_obj = driver->sync_obj_ref(sync_obj);
ttm_bo_add_to_lru(bo);
- ww_mutex_unlock(&bo->resv->lock);
+ __ttm_bo_unreserve(bo);
entry->reserved = false;
}
spin_unlock(&bdev->fence_lock);
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index 53b51c4e671a..d2a053352789 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -270,6 +270,52 @@ ttm_base_object_lookup_for_ref(struct ttm_object_device *tdev, uint32_t key)
}
EXPORT_SYMBOL(ttm_base_object_lookup_for_ref);
+/**
+ * ttm_ref_object_exists - Check whether a caller has a valid ref object
+ * (has opened) a base object.
+ *
+ * @tfile: Pointer to a struct ttm_object_file identifying the caller.
+ * @base: Pointer to a struct base object.
+ *
+ * Checks wether the caller identified by @tfile has put a valid USAGE
+ * reference object on the base object identified by @base.
+ */
+bool ttm_ref_object_exists(struct ttm_object_file *tfile,
+ struct ttm_base_object *base)
+{
+ struct drm_open_hash *ht = &tfile->ref_hash[TTM_REF_USAGE];
+ struct drm_hash_item *hash;
+ struct ttm_ref_object *ref;
+
+ rcu_read_lock();
+ if (unlikely(drm_ht_find_item_rcu(ht, base->hash.key, &hash) != 0))
+ goto out_false;
+
+ /*
+ * Verify that the ref object is really pointing to our base object.
+ * Our base object could actually be dead, and the ref object pointing
+ * to another base object with the same handle.
+ */
+ ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
+ if (unlikely(base != ref->obj))
+ goto out_false;
+
+ /*
+ * Verify that the ref->obj pointer was actually valid!
+ */
+ rmb();
+ if (unlikely(atomic_read(&ref->kref.refcount) == 0))
+ goto out_false;
+
+ rcu_read_unlock();
+ return true;
+
+ out_false:
+ rcu_read_unlock();
+ return false;
+}
+EXPORT_SYMBOL(ttm_ref_object_exists);
+
int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
enum ttm_ref_type ref_type, bool *existed)
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index dbadd49e4c4a..377176372da8 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -421,7 +421,7 @@ static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
clips[i].x2 - clips[i].x1,
clips[i].y2 - clips[i].y1);
if (ret)
- goto unlock;
+ break;
}
if (ufb->obj->base.import_attach) {
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index 0394811251bd..c041cd73f399 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -60,7 +60,7 @@ int udl_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- args->pitch = args->width * ((args->bpp + 1) / 8);
+ args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
args->size = args->pitch * args->height;
return udl_gem_create(file, dev,
args->size, &args->handle);
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index 2ae1eb7d1635..cddc4fcf35cf 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -310,7 +310,7 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
- struct udl_framebuffer *ufb = to_udl_fb(crtc->fb);
+ struct udl_framebuffer *ufb = to_udl_fb(crtc->primary->fb);
struct udl_device *udl = dev->dev_private;
char *buf;
char *wrptr;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
index 1e80152674b5..8bb26dcd9eae 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
@@ -117,10 +117,10 @@ static void vmw_hw_context_destroy(struct vmw_resource *res)
(void) vmw_context_binding_state_kill
(&container_of(res, struct vmw_user_context, res)->cbs);
(void) vmw_gb_context_destroy(res);
+ mutex_unlock(&dev_priv->binding_mutex);
if (dev_priv->pinned_bo != NULL &&
!dev_priv->query_cid_valid)
__vmw_execbuf_release_pinned_bo(dev_priv, NULL);
- mutex_unlock(&dev_priv->binding_mutex);
mutex_unlock(&dev_priv->cmdbuf_mutex);
return;
}
@@ -462,7 +462,6 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
struct vmw_resource *tmp;
struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
@@ -474,7 +473,7 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
if (unlikely(vmw_user_context_size == 0))
vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128;
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -521,7 +520,7 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
out_err:
vmw_resource_unreference(&res);
out_unlock:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
index a75840211b3c..70ddce8358b0 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
@@ -52,11 +52,10 @@ int vmw_dmabuf_to_placement(struct vmw_private *dev_priv,
struct ttm_placement *placement,
bool interruptible)
{
- struct vmw_master *vmaster = dev_priv->active_master;
struct ttm_buffer_object *bo = &buf->base;
int ret;
- ret = ttm_write_lock(&vmaster->lock, interruptible);
+ ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
return ret;
@@ -71,7 +70,7 @@ int vmw_dmabuf_to_placement(struct vmw_private *dev_priv,
ttm_bo_unreserve(bo);
err:
- ttm_write_unlock(&vmaster->lock);
+ ttm_write_unlock(&dev_priv->reservation_sem);
return ret;
}
@@ -95,12 +94,11 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf,
bool pin, bool interruptible)
{
- struct vmw_master *vmaster = dev_priv->active_master;
struct ttm_buffer_object *bo = &buf->base;
struct ttm_placement *placement;
int ret;
- ret = ttm_write_lock(&vmaster->lock, interruptible);
+ ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
return ret;
@@ -143,7 +141,7 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv,
err_unreserve:
ttm_bo_unreserve(bo);
err:
- ttm_write_unlock(&vmaster->lock);
+ ttm_write_unlock(&dev_priv->reservation_sem);
return ret;
}
@@ -198,7 +196,6 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv,
struct vmw_dma_buffer *buf,
bool pin, bool interruptible)
{
- struct vmw_master *vmaster = dev_priv->active_master;
struct ttm_buffer_object *bo = &buf->base;
struct ttm_placement placement;
int ret = 0;
@@ -209,7 +206,7 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv,
placement = vmw_vram_placement;
placement.lpfn = bo->num_pages;
- ret = ttm_write_lock(&vmaster->lock, interruptible);
+ ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
return ret;
@@ -232,7 +229,7 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv,
ttm_bo_unreserve(bo);
err_unlock:
- ttm_write_unlock(&vmaster->lock);
+ ttm_write_unlock(&dev_priv->reservation_sem);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 0083cbf99edf..4a223bbea3b3 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -142,11 +142,11 @@
static const struct drm_ioctl_desc vmw_ioctls[] = {
VMW_IOCTL_DEF(VMW_GET_PARAM, vmw_getparam_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_UNREF_DMABUF, vmw_dmabuf_unref_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_CURSOR_BYPASS,
vmw_kms_cursor_bypass_ioctl,
DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
@@ -159,29 +159,28 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
VMW_IOCTL_DEF(VMW_CREATE_CONTEXT, vmw_context_define_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_CREATE_SURFACE, vmw_surface_define_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_REF_SURFACE, vmw_surface_reference_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_EXECBUF, vmw_execbuf_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_FENCE_WAIT, vmw_fence_obj_wait_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_FENCE_SIGNALED,
vmw_fence_obj_signaled_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
- VMW_IOCTL_DEF(VMW_FENCE_EVENT,
- vmw_fence_event_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
+ VMW_IOCTL_DEF(VMW_FENCE_EVENT, vmw_fence_event_ioctl,
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
/* these allow direct access to the framebuffers mark as master only */
VMW_IOCTL_DEF(VMW_PRESENT, vmw_present_ioctl,
@@ -194,19 +193,19 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
DRM_MASTER | DRM_UNLOCKED),
VMW_IOCTL_DEF(VMW_CREATE_SHADER,
vmw_shader_define_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_UNREF_SHADER,
vmw_shader_destroy_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_GB_SURFACE_CREATE,
vmw_gb_surface_define_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_GB_SURFACE_REF,
vmw_gb_surface_reference_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_AUTH | DRM_UNLOCKED | DRM_RENDER_ALLOW),
VMW_IOCTL_DEF(VMW_SYNCCPU,
vmw_user_dmabuf_synccpu_ioctl,
- DRM_AUTH | DRM_UNLOCKED),
+ DRM_UNLOCKED | DRM_RENDER_ALLOW),
};
static struct pci_device_id vmw_pci_id_list[] = {
@@ -606,6 +605,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
mutex_init(&dev_priv->release_mutex);
mutex_init(&dev_priv->binding_mutex);
rwlock_init(&dev_priv->resource_lock);
+ ttm_lock_init(&dev_priv->reservation_sem);
for (i = vmw_res_context; i < vmw_res_max; ++i) {
idr_init(&dev_priv->res_idr[i]);
@@ -722,7 +722,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
ret = ttm_bo_device_init(&dev_priv->bdev,
dev_priv->bo_global_ref.ref.object,
- &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET,
+ &vmw_bo_driver,
+ dev->anon_inode->i_mapping,
+ VMWGFX_FILE_PAGE_OFFSET,
false);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed initializing TTM buffer object driver.\n");
@@ -969,7 +971,6 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
goto out_no_shman;
file_priv->driver_priv = vmw_fp;
- dev_priv->bdev.dev_mapping = dev->dev_mapping;
return 0;
@@ -980,12 +981,70 @@ out_no_tfile:
return ret;
}
-static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
+static struct vmw_master *vmw_master_check(struct drm_device *dev,
+ struct drm_file *file_priv,
+ unsigned int flags)
+{
+ int ret;
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ struct vmw_master *vmaster;
+
+ if (file_priv->minor->type != DRM_MINOR_LEGACY ||
+ !(flags & DRM_AUTH))
+ return NULL;
+
+ ret = mutex_lock_interruptible(&dev->master_mutex);
+ if (unlikely(ret != 0))
+ return ERR_PTR(-ERESTARTSYS);
+
+ if (file_priv->is_master) {
+ mutex_unlock(&dev->master_mutex);
+ return NULL;
+ }
+
+ /*
+ * Check if we were previously master, but now dropped.
+ */
+ if (vmw_fp->locked_master) {
+ mutex_unlock(&dev->master_mutex);
+ DRM_ERROR("Dropped master trying to access ioctl that "
+ "requires authentication.\n");
+ return ERR_PTR(-EACCES);
+ }
+ mutex_unlock(&dev->master_mutex);
+
+ /*
+ * Taking the drm_global_mutex after the TTM lock might deadlock
+ */
+ if (!(flags & DRM_UNLOCKED)) {
+ DRM_ERROR("Refusing locked ioctl access.\n");
+ return ERR_PTR(-EDEADLK);
+ }
+
+ /*
+ * Take the TTM lock. Possibly sleep waiting for the authenticating
+ * master to become master again, or for a SIGTERM if the
+ * authenticating master exits.
+ */
+ vmaster = vmw_master(file_priv->master);
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (unlikely(ret != 0))
+ vmaster = ERR_PTR(ret);
+
+ return vmaster;
+}
+
+static long vmw_generic_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg,
+ long (*ioctl_func)(struct file *, unsigned int,
+ unsigned long))
{
struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->minor->dev;
unsigned int nr = DRM_IOCTL_NR(cmd);
+ struct vmw_master *vmaster;
+ unsigned int flags;
+ long ret;
/*
* Do extra checking on driver private ioctls.
@@ -994,18 +1053,44 @@ static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END)
&& (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
const struct drm_ioctl_desc *ioctl =
- &vmw_ioctls[nr - DRM_COMMAND_BASE];
+ &vmw_ioctls[nr - DRM_COMMAND_BASE];
if (unlikely(ioctl->cmd_drv != cmd)) {
DRM_ERROR("Invalid command format, ioctl %d\n",
nr - DRM_COMMAND_BASE);
return -EINVAL;
}
+ flags = ioctl->flags;
+ } else if (!drm_ioctl_flags(nr, &flags))
+ return -EINVAL;
+
+ vmaster = vmw_master_check(dev, file_priv, flags);
+ if (unlikely(IS_ERR(vmaster))) {
+ DRM_INFO("IOCTL ERROR %d\n", nr);
+ return PTR_ERR(vmaster);
}
- return drm_ioctl(filp, cmd, arg);
+ ret = ioctl_func(filp, cmd, arg);
+ if (vmaster)
+ ttm_read_unlock(&vmaster->lock);
+
+ return ret;
+}
+
+static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ return vmw_generic_ioctl(filp, cmd, arg, &drm_ioctl);
}
+#ifdef CONFIG_COMPAT
+static long vmw_compat_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ return vmw_generic_ioctl(filp, cmd, arg, &drm_compat_ioctl);
+}
+#endif
+
static void vmw_lastclose(struct drm_device *dev)
{
struct drm_crtc *crtc;
@@ -1174,12 +1259,11 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
{
struct vmw_private *dev_priv =
container_of(nb, struct vmw_private, pm_nb);
- struct vmw_master *vmaster = dev_priv->active_master;
switch (val) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
- ttm_suspend_lock(&vmaster->lock);
+ ttm_suspend_lock(&dev_priv->reservation_sem);
/**
* This empties VRAM and unbinds all GMR bindings.
@@ -1193,7 +1277,7 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
case PM_POST_RESTORE:
- ttm_suspend_unlock(&vmaster->lock);
+ ttm_suspend_unlock(&dev_priv->reservation_sem);
break;
case PM_RESTORE_PREPARE:
@@ -1314,14 +1398,14 @@ static const struct file_operations vmwgfx_driver_fops = {
.poll = vmw_fops_poll,
.read = vmw_fops_read,
#if defined(CONFIG_COMPAT)
- .compat_ioctl = drm_compat_ioctl,
+ .compat_ioctl = vmw_compat_ioctl,
#endif
.llseek = noop_llseek,
};
static struct drm_driver driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
- DRIVER_MODESET | DRIVER_PRIME,
+ DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER,
.load = vmw_driver_load,
.unload = vmw_driver_unload,
.lastclose = vmw_lastclose,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 07831554dad7..6b252a887ae2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -40,9 +40,9 @@
#include <drm/ttm/ttm_module.h>
#include "vmwgfx_fence.h"
-#define VMWGFX_DRIVER_DATE "20140228"
+#define VMWGFX_DRIVER_DATE "20140325"
#define VMWGFX_DRIVER_MAJOR 2
-#define VMWGFX_DRIVER_MINOR 5
+#define VMWGFX_DRIVER_MINOR 6
#define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
@@ -487,6 +487,11 @@ struct vmw_private {
uint32_t num_3d_resources;
/*
+ * Replace this with an rwsem as soon as we have down_xx_interruptible()
+ */
+ struct ttm_lock reservation_sem;
+
+ /*
* Query processing. These members
* are protected by the cmdbuf mutex.
*/
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index efb575a7996c..931490b9cfed 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -2712,7 +2712,6 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
{
struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
/*
@@ -2729,7 +2728,7 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -2745,6 +2744,6 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
vmw_kms_cursor_post_execbuf(dev_priv);
out_unlock:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index ed5ce2a41bbf..a89ad938eacf 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -147,7 +147,7 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var,
}
if (!vmw_kms_validate_mode_vram(vmw_priv,
- info->fix.line_length,
+ var->xres * var->bits_per_pixel/8,
var->yoffset + var->yres)) {
DRM_ERROR("Requested geom can not fit in framebuffer\n");
return -EINVAL;
@@ -162,6 +162,8 @@ static int vmw_fb_set_par(struct fb_info *info)
struct vmw_private *vmw_priv = par->vmw_priv;
int ret;
+ info->fix.line_length = info->var.xres * info->var.bits_per_pixel/8;
+
ret = vmw_kms_write_svga(vmw_priv, info->var.xres, info->var.yres,
info->fix.line_length,
par->bpp, par->depth);
@@ -177,6 +179,7 @@ static int vmw_fb_set_par(struct fb_info *info)
vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, info->var.yoffset);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, info->var.xres);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, info->var.yres);
+ vmw_write(vmw_priv, SVGA_REG_BYTES_PER_LINE, info->fix.line_length);
vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
}
@@ -377,14 +380,13 @@ static int vmw_fb_create_bo(struct vmw_private *vmw_priv,
ne_placement.lpfn = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
- /* interuptable? */
- ret = ttm_write_lock(&vmw_priv->fbdev_master.lock, false);
- if (unlikely(ret != 0))
- return ret;
+ (void) ttm_write_lock(&vmw_priv->reservation_sem, false);
vmw_bo = kmalloc(sizeof(*vmw_bo), GFP_KERNEL);
- if (!vmw_bo)
+ if (!vmw_bo) {
+ ret = -ENOMEM;
goto err_unlock;
+ }
ret = vmw_dmabuf_init(vmw_priv, vmw_bo, size,
&ne_placement,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
index 47b70949bf3a..37881ecf5d7a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -226,7 +226,6 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
struct drm_vmw_present_arg *arg =
(struct drm_vmw_present_arg *)data;
struct vmw_surface *surface;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
struct drm_vmw_rect __user *clips_ptr;
struct drm_vmw_rect *clips = NULL;
struct drm_framebuffer *fb;
@@ -271,7 +270,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
}
vfb = vmw_framebuffer_to_vfb(fb);
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
goto out_no_ttm_lock;
@@ -291,7 +290,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data,
vmw_surface_unreference(&surface);
out_no_surface:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
out_no_ttm_lock:
drm_framebuffer_unreference(fb);
out_no_fb:
@@ -311,7 +310,6 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
struct drm_vmw_fence_rep __user *user_fence_rep =
(struct drm_vmw_fence_rep __user *)
(unsigned long)arg->fence_rep;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
struct drm_vmw_rect __user *clips_ptr;
struct drm_vmw_rect *clips = NULL;
struct drm_framebuffer *fb;
@@ -361,7 +359,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
goto out_no_ttm_lock;
}
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
goto out_no_ttm_lock;
@@ -369,7 +367,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
vfb, user_fence_rep,
clips, num_clips);
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
out_no_ttm_lock:
drm_framebuffer_unreference(fb);
out_no_fb:
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 8a650413dea5..a2dde5ad8138 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -468,7 +468,7 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
num_units = 0;
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list,
head) {
- if (crtc->fb != &framebuffer->base)
+ if (crtc->primary->fb != &framebuffer->base)
continue;
units[num_units++] = vmw_crtc_to_du(crtc);
}
@@ -596,7 +596,6 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
unsigned num_clips)
{
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
- struct vmw_master *vmaster = vmw_master(file_priv->master);
struct vmw_framebuffer_surface *vfbs =
vmw_framebuffer_to_vfbs(framebuffer);
struct drm_clip_rect norect;
@@ -611,7 +610,7 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
drm_modeset_lock_all(dev_priv->dev);
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0)) {
drm_modeset_unlock_all(dev_priv->dev);
return ret;
@@ -632,7 +631,7 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
flags, color,
clips, num_clips, inc, NULL);
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
drm_modeset_unlock_all(dev_priv->dev);
@@ -883,7 +882,7 @@ static int do_dmabuf_dirty_sou(struct drm_file *file_priv,
num_units = 0;
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
- if (crtc->fb != &framebuffer->base)
+ if (crtc->primary->fb != &framebuffer->base)
continue;
units[num_units++] = vmw_crtc_to_du(crtc);
}
@@ -954,7 +953,6 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
unsigned num_clips)
{
struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
- struct vmw_master *vmaster = vmw_master(file_priv->master);
struct vmw_framebuffer_dmabuf *vfbd =
vmw_framebuffer_to_vfbd(framebuffer);
struct drm_clip_rect norect;
@@ -962,7 +960,7 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
drm_modeset_lock_all(dev_priv->dev);
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0)) {
drm_modeset_unlock_all(dev_priv->dev);
return ret;
@@ -989,7 +987,7 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
clips, num_clips, increment, NULL);
}
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
drm_modeset_unlock_all(dev_priv->dev);
@@ -1245,7 +1243,7 @@ int vmw_kms_present(struct vmw_private *dev_priv,
num_units = 0;
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
- if (crtc->fb != &vfb->base)
+ if (crtc->primary->fb != &vfb->base)
continue;
units[num_units++] = vmw_crtc_to_du(crtc);
}
@@ -1382,7 +1380,7 @@ int vmw_kms_readback(struct vmw_private *dev_priv,
num_units = 0;
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
- if (crtc->fb != &vfb->base)
+ if (crtc->primary->fb != &vfb->base)
continue;
units[num_units++] = vmw_crtc_to_du(crtc);
}
@@ -1725,7 +1723,7 @@ int vmw_du_page_flip(struct drm_crtc *crtc,
uint32_t page_flip_flags)
{
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
- struct drm_framebuffer *old_fb = crtc->fb;
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb);
struct drm_file *file_priv ;
struct vmw_fence_obj *fence = NULL;
@@ -1743,7 +1741,7 @@ int vmw_du_page_flip(struct drm_crtc *crtc,
if (!vmw_kms_screen_object_flippable(dev_priv, crtc))
return -EINVAL;
- crtc->fb = fb;
+ crtc->primary->fb = fb;
/* do a full screen dirty update */
clips.x1 = clips.y1 = 0;
@@ -1783,7 +1781,7 @@ int vmw_du_page_flip(struct drm_crtc *crtc,
return ret;
out_no_fence:
- crtc->fb = old_fb;
+ crtc->primary->fb = old_fb;
return ret;
}
@@ -2022,7 +2020,6 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct vmw_private *dev_priv = vmw_priv(dev);
struct drm_vmw_update_layout_arg *arg =
(struct drm_vmw_update_layout_arg *)data;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
void __user *user_rects;
struct drm_vmw_rect *rects;
unsigned rects_size;
@@ -2030,7 +2027,7 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
int i;
struct drm_mode_config *mode_config = &dev->mode_config;
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -2072,6 +2069,6 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
out_free:
kfree(rects);
out_unlock:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index a055a26819c2..b2b9bd23aeee 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -93,7 +93,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
if (crtc == NULL)
return 0;
- fb = entry->base.crtc.fb;
+ fb = entry->base.crtc.primary->fb;
return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0],
fb->bits_per_pixel, fb->depth);
@@ -101,7 +101,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
if (!list_empty(&lds->active)) {
entry = list_entry(lds->active.next, typeof(*entry), active);
- fb = entry->base.crtc.fb;
+ fb = entry->base.crtc.primary->fb;
vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0],
fb->bits_per_pixel, fb->depth);
@@ -259,7 +259,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
connector->encoder = NULL;
encoder->crtc = NULL;
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
crtc->enabled = false;
vmw_ldu_del_active(dev_priv, ldu);
@@ -280,7 +280,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
vmw_fb_off(dev_priv);
- crtc->fb = fb;
+ crtc->primary->fb = fb;
encoder->crtc = crtc;
connector->encoder = encoder;
crtc->x = set->x;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 9757b57f8388..01d68f0a69dc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -538,8 +538,13 @@ int vmw_user_dmabuf_verify_access(struct ttm_buffer_object *bo,
return -EPERM;
vmw_user_bo = vmw_user_dma_buffer(bo);
- return (vmw_user_bo->prime.base.tfile == tfile ||
- vmw_user_bo->prime.base.shareable) ? 0 : -EPERM;
+
+ /* Check that the caller has opened the object. */
+ if (likely(ttm_ref_object_exists(tfile, &vmw_user_bo->prime.base)))
+ return 0;
+
+ DRM_ERROR("Could not grant buffer access.\n");
+ return -EPERM;
}
/**
@@ -676,10 +681,9 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
struct drm_vmw_dmabuf_rep *rep = &arg->rep;
struct vmw_dma_buffer *dma_buf;
uint32_t handle;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -696,7 +700,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
vmw_dmabuf_unreference(&dma_buf);
out_no_dmabuf:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
@@ -873,7 +877,6 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct vmw_resource *tmp;
struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret;
/*
@@ -884,7 +887,7 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
if (unlikely(vmw_user_stream_size == 0))
vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128;
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -932,7 +935,7 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
out_err:
vmw_resource_unreference(&res);
out_unlock:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
@@ -985,14 +988,13 @@ int vmw_dumb_create(struct drm_file *file_priv,
struct drm_mode_create_dumb *args)
{
struct vmw_private *dev_priv = vmw_priv(dev);
- struct vmw_master *vmaster = vmw_master(file_priv->master);
struct vmw_dma_buffer *dma_buf;
int ret;
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -1004,7 +1006,7 @@ int vmw_dumb_create(struct drm_file *file_priv,
vmw_dmabuf_unreference(&dma_buf);
out_no_dmabuf:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index 22406c8651ea..a95d3a0cabe4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -307,7 +307,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
connector->encoder = NULL;
encoder->crtc = NULL;
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
crtc->x = 0;
crtc->y = 0;
crtc->enabled = false;
@@ -368,7 +368,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
connector->encoder = NULL;
encoder->crtc = NULL;
- crtc->fb = NULL;
+ crtc->primary->fb = NULL;
crtc->x = 0;
crtc->y = 0;
crtc->enabled = false;
@@ -381,7 +381,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
connector->encoder = encoder;
encoder->crtc = crtc;
crtc->mode = *mode;
- crtc->fb = fb;
+ crtc->primary->fb = fb;
crtc->x = set->x;
crtc->y = set->y;
crtc->enabled = true;
@@ -572,5 +572,5 @@ void vmw_kms_screen_object_update_implicit_fb(struct vmw_private *dev_priv,
BUG_ON(!sou->base.is_implicit);
dev_priv->sou_priv->implicit_fb =
- vmw_framebuffer_to_vfb(sou->base.crtc.fb);
+ vmw_framebuffer_to_vfb(sou->base.crtc.primary->fb);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
index ee3856578a12..c1559eeaffe9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -449,7 +449,6 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data,
struct drm_vmw_shader_create_arg *arg =
(struct drm_vmw_shader_create_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
struct vmw_dma_buffer *buffer = NULL;
SVGA3dShaderType shader_type;
int ret;
@@ -487,14 +486,14 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data,
goto out_bad_arg;
}
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
goto out_bad_arg;
ret = vmw_shader_alloc(dev_priv, buffer, arg->size, arg->offset,
shader_type, tfile, &arg->shader_handle);
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
out_bad_arg:
vmw_dmabuf_unreference(&buffer);
return ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index e7af580ab977..4ecdbf3e59da 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -36,11 +36,13 @@
* @base: The TTM base object handling user-space visibility.
* @srf: The surface metadata.
* @size: TTM accounting size for the surface.
+ * @master: master of the creating client. Used for security check.
*/
struct vmw_user_surface {
struct ttm_prime_object prime;
struct vmw_surface srf;
uint32_t size;
+ struct drm_master *master;
};
/**
@@ -624,6 +626,8 @@ static void vmw_user_surface_free(struct vmw_resource *res)
struct vmw_private *dev_priv = srf->res.dev_priv;
uint32_t size = user_srf->size;
+ if (user_srf->master)
+ drm_master_put(&user_srf->master);
kfree(srf->offsets);
kfree(srf->sizes);
kfree(srf->snooper.image);
@@ -697,7 +701,6 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
struct vmw_surface_offset *cur_offset;
uint32_t num_sizes;
uint32_t size;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
const struct svga3d_surface_desc *desc;
if (unlikely(vmw_user_surface_size == 0))
@@ -723,7 +726,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -820,6 +823,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
user_srf->prime.base.shareable = false;
user_srf->prime.base.tfile = NULL;
+ if (drm_is_primary_client(file_priv))
+ user_srf->master = drm_master_get(file_priv->master);
/**
* From this point, the generic resource management functions
@@ -862,7 +867,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
rep->sid = user_srf->prime.base.hash.key;
vmw_resource_unreference(&res);
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return 0;
out_no_copy:
kfree(srf->offsets);
@@ -873,7 +878,81 @@ out_no_sizes:
out_no_user_srf:
ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
out_unlock:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
+ return ret;
+}
+
+
+static int
+vmw_surface_handle_reference(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ uint32_t u_handle,
+ enum drm_vmw_handle_type handle_type,
+ struct ttm_base_object **base_p)
+{
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_user_surface *user_srf;
+ uint32_t handle;
+ struct ttm_base_object *base;
+ int ret;
+
+ if (handle_type == DRM_VMW_HANDLE_PRIME) {
+ ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle);
+ if (unlikely(ret != 0))
+ return ret;
+ } else {
+ if (unlikely(drm_is_render_client(file_priv))) {
+ DRM_ERROR("Render client refused legacy "
+ "surface reference.\n");
+ return -EACCES;
+ }
+ handle = u_handle;
+ }
+
+ ret = -EINVAL;
+ base = ttm_base_object_lookup_for_ref(dev_priv->tdev, handle);
+ if (unlikely(base == NULL)) {
+ DRM_ERROR("Could not find surface to reference.\n");
+ goto out_no_lookup;
+ }
+
+ if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE)) {
+ DRM_ERROR("Referenced object is not a surface.\n");
+ goto out_bad_resource;
+ }
+
+ if (handle_type != DRM_VMW_HANDLE_PRIME) {
+ user_srf = container_of(base, struct vmw_user_surface,
+ prime.base);
+
+ /*
+ * Make sure the surface creator has the same
+ * authenticating master.
+ */
+ if (drm_is_primary_client(file_priv) &&
+ user_srf->master != file_priv->master) {
+ DRM_ERROR("Trying to reference surface outside of"
+ " master domain.\n");
+ ret = -EACCES;
+ goto out_bad_resource;
+ }
+
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Could not add a reference to a surface.\n");
+ goto out_bad_resource;
+ }
+ }
+
+ *base_p = base;
+ return 0;
+
+out_bad_resource:
+ ttm_base_object_unref(&base);
+out_no_lookup:
+ if (handle_type == DRM_VMW_HANDLE_PRIME)
+ (void) ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
+
return ret;
}
@@ -898,27 +977,16 @@ int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
struct vmw_user_surface *user_srf;
struct drm_vmw_size __user *user_sizes;
struct ttm_base_object *base;
- int ret = -EINVAL;
-
- base = ttm_base_object_lookup_for_ref(dev_priv->tdev, req->sid);
- if (unlikely(base == NULL)) {
- DRM_ERROR("Could not find surface to reference.\n");
- return -EINVAL;
- }
+ int ret;
- if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE))
- goto out_bad_resource;
+ ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid,
+ req->handle_type, &base);
+ if (unlikely(ret != 0))
+ return ret;
user_srf = container_of(base, struct vmw_user_surface, prime.base);
srf = &user_srf->srf;
- ret = ttm_ref_object_add(tfile, &user_srf->prime.base,
- TTM_REF_USAGE, NULL);
- if (unlikely(ret != 0)) {
- DRM_ERROR("Could not add a reference to a surface.\n");
- goto out_no_reference;
- }
-
rep->flags = srf->flags;
rep->format = srf->format;
memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels));
@@ -931,10 +999,10 @@ int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
if (unlikely(ret != 0)) {
DRM_ERROR("copy_to_user failed %p %u\n",
user_sizes, srf->num_sizes);
+ ttm_ref_object_base_unref(tfile, base->hash.key, TTM_REF_USAGE);
ret = -EFAULT;
}
-out_bad_resource:
-out_no_reference:
+
ttm_base_object_unref(&base);
return ret;
@@ -1173,7 +1241,6 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
int ret;
uint32_t size;
- struct vmw_master *vmaster = vmw_master(file_priv->master);
const struct svga3d_surface_desc *desc;
uint32_t backup_handle;
@@ -1189,7 +1256,7 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- ret = ttm_read_lock(&vmaster->lock, true);
+ ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
return ret;
@@ -1228,6 +1295,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
user_srf->prime.base.shareable = false;
user_srf->prime.base.tfile = NULL;
+ if (drm_is_primary_client(file_priv))
+ user_srf->master = drm_master_get(file_priv->master);
/**
* From this point, the generic resource management functions
@@ -1283,12 +1352,12 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
vmw_resource_unreference(&res);
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return 0;
out_no_user_srf:
ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
out_unlock:
- ttm_read_unlock(&vmaster->lock);
+ ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
@@ -1315,14 +1384,10 @@ int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data,
uint32_t backup_handle;
int ret = -EINVAL;
- base = ttm_base_object_lookup_for_ref(dev_priv->tdev, req->sid);
- if (unlikely(base == NULL)) {
- DRM_ERROR("Could not find surface to reference.\n");
- return -EINVAL;
- }
-
- if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE))
- goto out_bad_resource;
+ ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid,
+ req->handle_type, &base);
+ if (unlikely(ret != 0))
+ return ret;
user_srf = container_of(base, struct vmw_user_surface, prime.base);
srf = &user_srf->srf;
@@ -1331,13 +1396,6 @@ int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data,
goto out_bad_resource;
}
- ret = ttm_ref_object_add(tfile, &user_srf->prime.base,
- TTM_REF_USAGE, NULL);
- if (unlikely(ret != 0)) {
- DRM_ERROR("Could not add a reference to a GB surface.\n");
- goto out_bad_resource;
- }
-
mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */
ret = vmw_user_dmabuf_reference(tfile, srf->res.backup,
&backup_handle);
@@ -1346,8 +1404,7 @@ int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data,
if (unlikely(ret != 0)) {
DRM_ERROR("Could not add a reference to a GB surface "
"backup buffer.\n");
- (void) ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
- req->sid,
+ (void) ttm_ref_object_base_unref(tfile, base->hash.key,
TTM_REF_USAGE);
goto out_bad_resource;
}
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index bfb09d802abd..b10550ee1d89 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -102,6 +102,7 @@ u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs)
{
return (u32)atomic_add_return(incrs, &sp->max_val);
}
+EXPORT_SYMBOL(host1x_syncpt_incr_max);
/*
* Write cached syncpoint and waitbase values to hardware.
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 29dd9f746dfa..3eb4281689b5 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -79,9 +79,11 @@ enum chips {
/* Each client has this additional data */
struct adm1021_data {
- struct device *hwmon_dev;
+ struct i2c_client *client;
enum chips type;
+ const struct attribute_group *groups[3];
+
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
char low_power; /* !=0 if device in low power mode */
@@ -101,7 +103,6 @@ static int adm1021_probe(struct i2c_client *client,
static int adm1021_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm1021_init_client(struct i2c_client *client);
-static int adm1021_remove(struct i2c_client *client);
static struct adm1021_data *adm1021_update_device(struct device *dev);
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
@@ -128,7 +129,6 @@ static struct i2c_driver adm1021_driver = {
.name = "adm1021",
},
.probe = adm1021_probe,
- .remove = adm1021_remove,
.id_table = adm1021_id,
.detect = adm1021_detect,
.address_list = normal_i2c,
@@ -182,8 +182,8 @@ static ssize_t set_temp_max(struct device *dev,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long temp;
int err;
@@ -207,8 +207,8 @@ static ssize_t set_temp_min(struct device *dev,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long temp;
int err;
@@ -238,8 +238,8 @@ static ssize_t set_low_power(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
char low_power;
unsigned long val;
int err;
@@ -412,15 +412,15 @@ static int adm1021_detect(struct i2c_client *client,
static int adm1021_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device *dev = &client->dev;
struct adm1021_data *data;
- int err;
+ struct device *hwmon_dev;
- data = devm_kzalloc(&client->dev, sizeof(struct adm1021_data),
- GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct adm1021_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- i2c_set_clientdata(client, data);
+ data->client = client;
data->type = id->driver_data;
mutex_init(&data->update_lock);
@@ -428,29 +428,14 @@ static int adm1021_probe(struct i2c_client *client,
if (data->type != lm84 && !read_only)
adm1021_init_client(client);
- /* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &adm1021_group);
- if (err)
- return err;
-
- if (data->type != lm84) {
- err = sysfs_create_group(&client->dev.kobj, &adm1021_min_group);
- if (err)
- goto error;
- }
+ data->groups[0] = &adm1021_group;
+ if (data->type != lm84)
+ data->groups[1] = &adm1021_min_group;
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto error;
- }
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
- return 0;
-
-error:
- sysfs_remove_group(&client->dev.kobj, &adm1021_min_group);
- sysfs_remove_group(&client->dev.kobj, &adm1021_group);
- return err;
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
static void adm1021_init_client(struct i2c_client *client)
@@ -462,21 +447,10 @@ static void adm1021_init_client(struct i2c_client *client)
i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04);
}
-static int adm1021_remove(struct i2c_client *client)
-{
- struct adm1021_data *data = i2c_get_clientdata(client);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &adm1021_min_group);
- sysfs_remove_group(&client->dev.kobj, &adm1021_group);
-
- return 0;
-}
-
static struct adm1021_data *adm1021_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1021_data *data = i2c_get_clientdata(client);
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
mutex_lock(&data->update_lock);
@@ -484,7 +458,7 @@ static struct adm1021_data *adm1021_update_device(struct device *dev)
|| !data->valid) {
int i;
- dev_dbg(&client->dev, "Starting adm1021 update\n");
+ dev_dbg(dev, "Starting adm1021 update\n");
for (i = 0; i < 2; i++) {
data->temp[i] = 1000 *
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
index 8d9f2a0e8efe..71463689d163 100644
--- a/drivers/hwmon/asc7621.c
+++ b/drivers/hwmon/asc7621.c
@@ -1115,7 +1115,6 @@ asc7621_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the asc7621 chip */
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index ddff02e3e66f..6edce42c61d5 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -353,8 +353,6 @@ static int atxp1_probe(struct i2c_client *new_client,
data->vrm = vid_which_vrm();
i2c_set_clientdata(new_client, data);
- data->valid = 0;
-
mutex_init(&data->update_lock);
/* Register sysfs hooks */
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index f31bc4c48644..6d02e3b06375 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -810,20 +810,20 @@ static int __init coretemp_init(void)
if (err)
goto exit;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(i)
get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
- put_online_cpus();
+ cpu_notifier_register_done();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
- register_hotcpu_notifier(&coretemp_cpu_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&coretemp_cpu_notifier);
+ cpu_notifier_register_done();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -838,8 +838,8 @@ static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
- get_online_cpus();
- unregister_hotcpu_notifier(&coretemp_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&coretemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
@@ -847,7 +847,7 @@ static void __exit coretemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
- put_online_cpus();
+ cpu_notifier_register_done();
platform_driver_unregister(&coretemp_driver);
}
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index 1a8aa1265262..32f5132c4652 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -1648,7 +1648,7 @@ static void __exit f71805f_exit(void)
platform_driver_unregister(&f71805f_driver);
}
-MODULE_AUTHOR("Jean Delvare <khali@linux-fr>");
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver");
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 70749fc15a4f..a327fd3402a7 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -11,6 +11,7 @@
* similar parts. The other devices are supported by different drivers.
*
* Supports: IT8603E Super I/O chip w/LPC interface
+ * IT8623E Super I/O chip w/LPC interface
* IT8705F Super I/O chip w/LPC interface
* IT8712F Super I/O chip w/LPC interface
* IT8716F Super I/O chip w/LPC interface
@@ -147,7 +148,8 @@ static inline void superio_exit(void)
#define IT8772E_DEVID 0x8772
#define IT8782F_DEVID 0x8782
#define IT8783E_DEVID 0x8783
-#define IT8306E_DEVID 0x8603
+#define IT8603E_DEVID 0x8603
+#define IT8623E_DEVID 0x8623
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
@@ -1431,7 +1433,7 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr,
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
-/* special AVCC3 IT8306E in9 */
+/* special AVCC3 IT8603E in9 */
static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0);
static ssize_t show_name(struct device *dev, struct device_attribute
@@ -1766,7 +1768,8 @@ static int __init it87_find(unsigned short *address,
case IT8783E_DEVID:
sio_data->type = it8783;
break;
- case IT8306E_DEVID:
+ case IT8603E_DEVID:
+ case IT8623E_DEVID:
sio_data->type = it8603;
break;
case 0xffff: /* No device at all */
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index b4ad598feb6c..848b9611151f 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -155,8 +155,9 @@ enum chips { lm63, lm64, lm96163 };
*/
struct lm63_data {
- struct device *hwmon_dev;
+ struct i2c_client *client;
struct mutex update_lock;
+ const struct attribute_group *groups[5];
char valid; /* zero until following fields are valid */
char lut_valid; /* zero until lut fields are valid */
unsigned long last_updated; /* in jiffies */
@@ -218,9 +219,9 @@ static inline int lut_temp_to_reg(struct lm63_data *data, long val)
* Update the lookup table register cache.
* client->update_lock must be held when calling this function.
*/
-static void lm63_update_lut(struct i2c_client *client)
+static void lm63_update_lut(struct lm63_data *data)
{
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct i2c_client *client = data->client;
int i;
if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
@@ -241,8 +242,8 @@ static void lm63_update_lut(struct i2c_client *client)
static struct lm63_data *lm63_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long next_update;
mutex_lock(&data->update_lock);
@@ -310,7 +311,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
data->valid = 1;
}
- lm63_update_lut(client);
+ lm63_update_lut(data);
mutex_unlock(&data->update_lock);
@@ -321,18 +322,17 @@ static struct lm63_data *lm63_update_device(struct device *dev)
* Trip points in the lookup table should be in ascending order for both
* temperatures and PWM output values.
*/
-static int lm63_lut_looks_bad(struct i2c_client *client)
+static int lm63_lut_looks_bad(struct device *dev, struct lm63_data *data)
{
- struct lm63_data *data = i2c_get_clientdata(client);
int i;
mutex_lock(&data->update_lock);
- lm63_update_lut(client);
+ lm63_update_lut(data);
for (i = 1; i < data->lut_size; i++) {
if (data->pwm1[1 + i - 1] > data->pwm1[1 + i]
|| data->temp8[3 + i - 1] > data->temp8[3 + i]) {
- dev_warn(&client->dev,
+ dev_warn(dev,
"Lookup table doesn't look sane (check entries %d and %d)\n",
i, i + 1);
break;
@@ -358,8 +358,8 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err;
@@ -399,8 +399,8 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->index;
unsigned long val;
int err;
@@ -435,8 +435,8 @@ static ssize_t set_pwm1_enable(struct device *dev,
struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err;
@@ -450,7 +450,7 @@ static ssize_t set_pwm1_enable(struct device *dev,
* Only let the user switch to automatic mode if the lookup table
* looks sane.
*/
- if (val == 2 && lm63_lut_looks_bad(client))
+ if (val == 2 && lm63_lut_looks_bad(dev, data))
return -EPERM;
mutex_lock(&data->update_lock);
@@ -461,7 +461,7 @@ static ssize_t set_pwm1_enable(struct device *dev,
else
data->config_fan &= ~0x20;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
- data->config_fan);
+ data->config_fan);
mutex_unlock(&data->update_lock);
return count;
}
@@ -505,8 +505,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->index;
long val;
int err;
@@ -579,8 +579,8 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long val;
int err;
int nr = attr->index;
@@ -635,8 +635,8 @@ static ssize_t set_temp2_crit_hyst(struct device *dev,
struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long val;
int err;
long hyst;
@@ -657,11 +657,11 @@ static ssize_t set_temp2_crit_hyst(struct device *dev,
* Set conversion rate.
* client->update_lock must be held when calling this function.
*/
-static void lm63_set_convrate(struct i2c_client *client, struct lm63_data *data,
- unsigned int interval)
+static void lm63_set_convrate(struct lm63_data *data, unsigned int interval)
{
- int i;
+ struct i2c_client *client = data->client;
unsigned int update_interval;
+ int i;
/* Shift calculations to avoid rounding errors */
interval <<= 6;
@@ -689,8 +689,7 @@ static ssize_t set_update_interval(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
unsigned long val;
int err;
@@ -699,7 +698,7 @@ static ssize_t set_update_interval(struct device *dev,
return err;
mutex_lock(&data->update_lock);
- lm63_set_convrate(client, data, clamp_val(val, 0, 100000));
+ lm63_set_convrate(data, clamp_val(val, 0, 100000));
mutex_unlock(&data->update_lock);
return count;
@@ -708,8 +707,7 @@ static ssize_t set_update_interval(struct device *dev,
static ssize_t show_type(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
return sprintf(buf, data->trutherm ? "1\n" : "2\n");
}
@@ -717,8 +715,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr,
static ssize_t set_type(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int ret;
u8 reg;
@@ -915,6 +913,15 @@ static struct attribute *lm63_attributes[] = {
NULL
};
+static struct attribute *lm63_attributes_temp2_type[] = {
+ &dev_attr_temp2_type.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_temp2_type = {
+ .attrs = lm63_attributes_temp2_type,
+};
+
static struct attribute *lm63_attributes_extra_lut[] = {
&sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
@@ -946,8 +953,7 @@ static umode_t lm63_attribute_mode(struct kobject *kobj,
struct attribute *attr, int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data = dev_get_drvdata(dev);
if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
&& (data->kind == lm64 ||
@@ -1026,9 +1032,10 @@ static int lm63_detect(struct i2c_client *client,
* Ideally we shouldn't have to initialize anything, since the BIOS
* should have taken care of everything
*/
-static void lm63_init_client(struct i2c_client *client)
+static void lm63_init_client(struct lm63_data *data)
{
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct i2c_client *client = data->client;
+ struct device *dev = &client->dev;
u8 convrate;
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
@@ -1037,7 +1044,7 @@ static void lm63_init_client(struct i2c_client *client)
/* Start converting if needed */
if (data->config & 0x40) { /* standby */
- dev_dbg(&client->dev, "Switching to operational mode\n");
+ dev_dbg(dev, "Switching to operational mode\n");
data->config &= 0xA7;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
data->config);
@@ -1090,13 +1097,13 @@ static void lm63_init_client(struct i2c_client *client)
/* Show some debug info about the LM63 configuration */
if (data->kind == lm63)
- dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
+ dev_dbg(dev, "Alert/tach pin configured for %s\n",
(data->config & 0x04) ? "tachometer input" :
"alert output");
- dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
+ dev_dbg(dev, "PWM clock %s kHz, output frequency %u Hz\n",
(data->config_fan & 0x08) ? "1.4" : "360",
((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
- dev_dbg(&client->dev, "PWM output active %s, %s mode\n",
+ dev_dbg(dev, "PWM output active %s, %s mode\n",
(data->config_fan & 0x10) ? "low" : "high",
(data->config_fan & 0x20) ? "manual" : "auto");
}
@@ -1104,15 +1111,16 @@ static void lm63_init_client(struct i2c_client *client)
static int lm63_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
struct lm63_data *data;
- int err;
+ int groups = 0;
- data = devm_kzalloc(&client->dev, sizeof(struct lm63_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct lm63_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- i2c_set_clientdata(client, data);
- data->valid = 0;
+ data->client = client;
mutex_init(&data->update_lock);
/* Set the device type */
@@ -1121,59 +1129,21 @@ static int lm63_probe(struct i2c_client *client,
data->temp2_offset = 16000;
/* Initialize chip */
- lm63_init_client(client);
+ lm63_init_client(data);
/* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &lm63_group);
- if (err)
- return err;
- if (data->config & 0x04) { /* tachometer enabled */
- err = sysfs_create_group(&client->dev.kobj, &lm63_group_fan1);
- if (err)
- goto exit_remove_files;
- }
- if (data->kind == lm96163) {
- err = device_create_file(&client->dev, &dev_attr_temp2_type);
- if (err)
- goto exit_remove_files;
-
- err = sysfs_create_group(&client->dev.kobj,
- &lm63_group_extra_lut);
- if (err)
- goto exit_remove_files;
- }
-
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
- }
+ data->groups[groups++] = &lm63_group;
+ if (data->config & 0x04) /* tachometer enabled */
+ data->groups[groups++] = &lm63_group_fan1;
- return 0;
-
-exit_remove_files:
- sysfs_remove_group(&client->dev.kobj, &lm63_group);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
if (data->kind == lm96163) {
- device_remove_file(&client->dev, &dev_attr_temp2_type);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
+ data->groups[groups++] = &lm63_group_temp2_type;
+ data->groups[groups++] = &lm63_group_extra_lut;
}
- return err;
-}
-
-static int lm63_remove(struct i2c_client *client)
-{
- struct lm63_data *data = i2c_get_clientdata(client);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &lm63_group);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
- if (data->kind == lm96163) {
- device_remove_file(&client->dev, &dev_attr_temp2_type);
- sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
- }
-
- return 0;
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
/*
@@ -1194,7 +1164,6 @@ static struct i2c_driver lm63_driver = {
.name = "lm63",
},
.probe = lm63_probe,
- .remove = lm63_remove,
.id_table = lm63_id,
.detect = lm63_detect,
.address_list = normal_i2c,
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index f17beb5e6dd6..502771c06fd9 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -348,7 +348,6 @@ static int lm77_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the LM77 chip */
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index eba89aac3ece..bd0a1ebbf867 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -112,7 +112,7 @@ static inline long TEMP_FROM_REG(u16 temp)
*/
struct lm80_data {
- struct device *hwmon_dev;
+ struct i2c_client *client;
struct mutex update_lock;
char error; /* !=0 if error occurred during last update */
char valid; /* !=0 if following fields are valid */
@@ -140,7 +140,6 @@ static int lm80_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info);
static void lm80_init_client(struct i2c_client *client);
-static int lm80_remove(struct i2c_client *client);
static struct lm80_data *lm80_update_device(struct device *dev);
static int lm80_read_value(struct i2c_client *client, u8 reg);
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
@@ -162,7 +161,6 @@ static struct i2c_driver lm80_driver = {
.name = "lm80",
},
.probe = lm80_probe,
- .remove = lm80_remove,
.id_table = lm80_id,
.detect = lm80_detect,
.address_list = normal_i2c,
@@ -191,8 +189,8 @@ static ssize_t set_in_##suffix(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
int nr = to_sensor_dev_attr(attr)->index; \
- struct i2c_client *client = to_i2c_client(dev); \
- struct lm80_data *data = i2c_get_clientdata(client); \
+ struct lm80_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
long val; \
int err = kstrtol(buf, 10, &val); \
if (err < 0) \
@@ -235,8 +233,8 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct lm80_data *data = i2c_get_clientdata(client);
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -259,8 +257,8 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = to_i2c_client(dev);
- struct lm80_data *data = i2c_get_clientdata(client);
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long min, val;
u8 reg;
int err = kstrtoul(buf, 10, &val);
@@ -286,7 +284,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
data->fan_div[nr] = 3;
break;
default:
- dev_err(&client->dev,
+ dev_err(dev,
"fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
val);
mutex_unlock(&data->update_lock);
@@ -332,8 +330,8 @@ show_temp(os_hyst, temp_os_hyst);
static ssize_t set_temp_##suffix(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct lm80_data *data = i2c_get_clientdata(client); \
+ struct lm80_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
long val; \
int err = kstrtol(buf, 10, &val); \
if (err < 0) \
@@ -440,7 +438,7 @@ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 13);
* Real code
*/
-static struct attribute *lm80_attributes[] = {
+static struct attribute *lm80_attrs[] = {
&sensor_dev_attr_in0_min.dev_attr.attr,
&sensor_dev_attr_in1_min.dev_attr.attr,
&sensor_dev_attr_in2_min.dev_attr.attr,
@@ -487,10 +485,7 @@ static struct attribute *lm80_attributes[] = {
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
NULL
};
-
-static const struct attribute_group lm80_group = {
- .attrs = lm80_attributes,
-};
+ATTRIBUTE_GROUPS(lm80);
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
@@ -541,14 +536,15 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
static int lm80_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
struct lm80_data *data;
- int err;
- data = devm_kzalloc(&client->dev, sizeof(struct lm80_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- i2c_set_clientdata(client, data);
+ data->client = client;
mutex_init(&data->update_lock);
/* Initialize the LM80 chip */
@@ -558,32 +554,10 @@ static int lm80_probe(struct i2c_client *client,
data->fan_min[0] = lm80_read_value(client, LM80_REG_FAN_MIN(1));
data->fan_min[1] = lm80_read_value(client, LM80_REG_FAN_MIN(2));
- /* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &lm80_group);
- if (err)
- return err;
-
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto error_remove;
- }
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, lm80_groups);
- return 0;
-
-error_remove:
- sysfs_remove_group(&client->dev.kobj, &lm80_group);
- return err;
-}
-
-static int lm80_remove(struct i2c_client *client)
-{
- struct lm80_data *data = i2c_get_clientdata(client);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &lm80_group);
-
- return 0;
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
static int lm80_read_value(struct i2c_client *client, u8 reg)
@@ -614,8 +588,8 @@ static void lm80_init_client(struct i2c_client *client)
static struct lm80_data *lm80_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm80_data *data = i2c_get_clientdata(client);
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int i;
int rv;
int prev_rv;
@@ -627,7 +601,7 @@ static struct lm80_data *lm80_update_device(struct device *dev)
lm80_init_client(client);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
- dev_dbg(&client->dev, "Starting lm80 update\n");
+ dev_dbg(dev, "Starting lm80 update\n");
for (i = 0; i <= 6; i++) {
rv = lm80_read_value(client, LM80_REG_IN(i));
if (rv < 0)
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index abd270243ba7..be02155788c3 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -349,7 +349,6 @@ static int lm83_probe(struct i2c_client *new_client,
return -ENOMEM;
i2c_set_clientdata(new_client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/*
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 4c5f20231c1a..ba1d83d48056 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -903,7 +903,6 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the LM87 chip */
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 701e952ae523..c9ff08dbe10c 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -365,7 +365,9 @@ enum lm90_temp11_reg_index {
*/
struct lm90_data {
+ struct i2c_client *client;
struct device *hwmon_dev;
+ const struct attribute_group *groups[6];
struct mutex update_lock;
struct regulator *regulator;
char valid; /* zero until following fields are valid */
@@ -513,8 +515,8 @@ static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
static struct lm90_data *lm90_update_device(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long next_update;
mutex_lock(&data->update_lock);
@@ -793,8 +795,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->index;
long val;
int err;
@@ -860,8 +862,8 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
};
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
int nr = attr->nr;
int index = attr->index;
long val;
@@ -922,8 +924,8 @@ static ssize_t show_temphyst(struct device *dev,
static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
long val;
int err;
int temp;
@@ -976,8 +978,8 @@ static ssize_t set_update_interval(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct lm90_data *data = i2c_get_clientdata(client);
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
unsigned long val;
int err;
@@ -1057,6 +1059,15 @@ static const struct attribute_group lm90_group = {
.attrs = lm90_attributes,
};
+static struct attribute *lm90_temp2_offset_attributes[] = {
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm90_temp2_offset_group = {
+ .attrs = lm90_temp2_offset_attributes,
+};
+
/*
* Additional attributes for devices with emergency sensors
*/
@@ -1393,22 +1404,6 @@ static int lm90_detect(struct i2c_client *client,
return 0;
}
-static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data)
-{
- struct device *dev = &client->dev;
-
- if (data->flags & LM90_HAVE_TEMP3)
- sysfs_remove_group(&dev->kobj, &lm90_temp3_group);
- if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
- sysfs_remove_group(&dev->kobj, &lm90_emergency_alarm_group);
- if (data->flags & LM90_HAVE_EMERGENCY)
- sysfs_remove_group(&dev->kobj, &lm90_emergency_group);
- if (data->flags & LM90_HAVE_OFFSET)
- device_remove_file(dev, &sensor_dev_attr_temp2_offset.dev_attr);
- device_remove_file(dev, &dev_attr_pec);
- sysfs_remove_group(&dev->kobj, &lm90_group);
-}
-
static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
{
/* Restore initial configuration */
@@ -1418,10 +1413,9 @@ static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
data->config_orig);
}
-static void lm90_init_client(struct i2c_client *client)
+static void lm90_init_client(struct i2c_client *client, struct lm90_data *data)
{
u8 config, convrate;
- struct lm90_data *data = i2c_get_clientdata(client);
if (lm90_read_reg(client, LM90_REG_R_CONVRATE, &convrate) < 0) {
dev_warn(&client->dev, "Failed to read convrate register!\n");
@@ -1519,6 +1513,7 @@ static int lm90_probe(struct i2c_client *client,
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
struct lm90_data *data;
struct regulator *regulator;
+ int groups = 0;
int err;
regulator = devm_regulator_get(dev, "vcc");
@@ -1527,15 +1522,15 @@ static int lm90_probe(struct i2c_client *client,
err = regulator_enable(regulator);
if (err < 0) {
- dev_err(&client->dev,
- "Failed to enable regulator: %d\n", err);
+ dev_err(dev, "Failed to enable regulator: %d\n", err);
return err;
}
- data = devm_kzalloc(&client->dev, sizeof(struct lm90_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
+ data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -1562,44 +1557,34 @@ static int lm90_probe(struct i2c_client *client,
data->max_convrate = lm90_params[data->kind].max_convrate;
/* Initialize the LM90 chip */
- lm90_init_client(client);
+ lm90_init_client(client, data);
/* Register sysfs hooks */
- err = sysfs_create_group(&dev->kobj, &lm90_group);
- if (err)
- goto exit_restore;
+ data->groups[groups++] = &lm90_group;
+
+ if (data->flags & LM90_HAVE_OFFSET)
+ data->groups[groups++] = &lm90_temp2_offset_group;
+
+ if (data->flags & LM90_HAVE_EMERGENCY)
+ data->groups[groups++] = &lm90_emergency_group;
+
+ if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
+ data->groups[groups++] = &lm90_emergency_alarm_group;
+
+ if (data->flags & LM90_HAVE_TEMP3)
+ data->groups[groups++] = &lm90_temp3_group;
+
if (client->flags & I2C_CLIENT_PEC) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_OFFSET) {
- err = device_create_file(dev,
- &sensor_dev_attr_temp2_offset.dev_attr);
- if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_EMERGENCY) {
- err = sysfs_create_group(&dev->kobj, &lm90_emergency_group);
- if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
- err = sysfs_create_group(&dev->kobj,
- &lm90_emergency_alarm_group);
- if (err)
- goto exit_remove_files;
- }
- if (data->flags & LM90_HAVE_TEMP3) {
- err = sysfs_create_group(&dev->kobj, &lm90_temp3_group);
- if (err)
- goto exit_remove_files;
+ goto exit_restore;
}
- data->hwmon_dev = hwmon_device_register(dev);
+ data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
+ goto exit_remove_pec;
}
if (client->irq) {
@@ -1618,8 +1603,8 @@ static int lm90_probe(struct i2c_client *client,
exit_unregister:
hwmon_device_unregister(data->hwmon_dev);
-exit_remove_files:
- lm90_remove_files(client, data);
+exit_remove_pec:
+ device_remove_file(dev, &dev_attr_pec);
exit_restore:
lm90_restore_conf(client, data);
regulator_disable(data->regulator);
@@ -1632,7 +1617,7 @@ static int lm90_remove(struct i2c_client *client)
struct lm90_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
- lm90_remove_files(client, data);
+ device_remove_file(&client->dev, &dev_attr_pec);
lm90_restore_conf(client, data);
regulator_disable(data->regulator);
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 9d0e87a4f0cb..b9022db6511a 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -380,7 +380,6 @@ static int lm92_probe(struct i2c_client *new_client,
return -ENOMEM;
i2c_set_clientdata(new_client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the chipset */
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index 6f1c6c0dbaf5..adf23165a6a7 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -2754,7 +2754,6 @@ static int lm93_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
/* housekeeping */
- data->valid = 0;
data->update = update;
mutex_init(&data->update_lock);
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index 6638e997f83f..4c23afe113e2 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -273,7 +273,6 @@ static int max1619_probe(struct i2c_client *new_client,
return -ENOMEM;
i2c_set_clientdata(new_client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
/* Initialize the MAX1619 chip */
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index 330fe117e219..988181e4cfcd 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -1225,7 +1225,7 @@ static int pc87360_probe(struct platform_device *pdev)
int i;
struct pc87360_data *data;
int err = 0;
- const char *name = "pc87360";
+ const char *name;
int use_thermistors = 0;
struct device *dev = &pdev->dev;
@@ -1233,13 +1233,14 @@ static int pc87360_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
- data->fannr = 2;
- data->innr = 0;
- data->tempnr = 0;
-
switch (devid) {
+ default:
+ name = "pc87360";
+ data->fannr = 2;
+ break;
case 0xe8:
name = "pc87363";
+ data->fannr = 2;
break;
case 0xe4:
name = "pc87364";
@@ -1260,7 +1261,6 @@ static int pc87360_probe(struct platform_device *pdev)
}
data->name = name;
- data->valid = 0;
mutex_init(&data->lock);
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index 38944e94f65f..8df43c51de2c 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -319,7 +319,7 @@ static int __init via_cputemp_init(void)
if (err)
goto exit;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(i) {
struct cpuinfo_x86 *c = &cpu_data(i);
@@ -339,14 +339,14 @@ static int __init via_cputemp_init(void)
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
- put_online_cpus();
+ cpu_notifier_register_done();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
- register_hotcpu_notifier(&via_cputemp_cpu_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ cpu_notifier_register_done();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -361,8 +361,8 @@ static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
- get_online_cpus();
- unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
@@ -370,7 +370,7 @@ static void __exit via_cputemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
- put_online_cpus();
+ cpu_notifier_register_done();
platform_driver_unregister(&via_cputemp_driver);
}
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index df585808adb6..4068db4d9580 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -1376,7 +1376,6 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
err = w83792d_detect_subclients(client);
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index 6384b268f590..ac3043122011 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -188,12 +188,8 @@ static int w83l785ts_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, data);
- data->valid = 0;
mutex_init(&data->update_lock);
- /* Default values in case the first read fails (unlikely). */
- data->temp[1] = data->temp[0] = 0;
-
/*
* Initialize the W83L785TS chip
* Nothing yet, assume it is already started.
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index de17c5593d97..014afab1d551 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -936,7 +936,7 @@ config I2C_ACORN
config I2C_ELEKTOR
tristate "Elektor ISA card"
- depends on ISA && HAS_IOPORT && BROKEN_ON_SMP
+ depends on ISA && HAS_IOPORT_MAP && BROKEN_ON_SMP
select I2C_ALGOPCF
help
This supports the PCF8584 ISA bus I2C adapter. Say Y if you own
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 8e1939f564f4..51493ed4643b 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -681,14 +681,19 @@ static int __init intel_idle_init(void)
if (intel_idle_cpuidle_devices == NULL)
return -ENOMEM;
+ cpu_notifier_register_begin();
+
for_each_online_cpu(i) {
retval = intel_idle_cpu_init(i);
if (retval) {
+ cpu_notifier_register_done();
cpuidle_unregister_driver(&intel_idle_driver);
return retval;
}
}
- register_cpu_notifier(&cpu_hotplug_notifier);
+ __register_cpu_notifier(&cpu_hotplug_notifier);
+
+ cpu_notifier_register_done();
return 0;
}
@@ -698,10 +703,13 @@ static void __exit intel_idle_exit(void)
intel_idle_cpuidle_devices_uninit();
cpuidle_unregister_driver(&intel_idle_driver);
+ cpu_notifier_register_begin();
if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE)
on_each_cpu(__setup_broadcast_timer, (void *)false, 1);
- unregister_cpu_notifier(&cpu_hotplug_notifier);
+ __unregister_cpu_notifier(&cpu_hotplug_notifier);
+
+ cpu_notifier_register_done();
return;
}
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4bf4c16de976..d86196cfe4b4 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -193,6 +193,16 @@ config TI_AM335X_ADC
Say yes here to build support for Texas Instruments ADC
driver which is also a MFD client.
+config TWL4030_MADC
+ tristate "TWL4030 MADC (Monitoring A/D Converter)"
+ depends on TWL4030_CORE
+ help
+ This driver provides support for Triton TWL4030-MADC. The
+ driver supports both RT and SW conversion methods.
+
+ This driver can also be built as a module. If so, the module will be
+ called twl4030-madc.
+
config TWL6030_GPADC
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
depends on TWL4030_CORE
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index bb252540664a..ab346d88c688 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 4c583e471339..7de1c4c87942 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -29,7 +29,6 @@
*
*/
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -47,20 +46,84 @@
#include <linux/gfp.h>
#include <linux/err.h>
-/*
+#include <linux/iio/iio.h>
+
+/**
* struct twl4030_madc_data - a container for madc info
- * @dev - pointer to device structure for madc
- * @lock - mutex protecting this data structure
- * @requests - Array of request struct corresponding to SW1, SW2 and RT
- * @imr - Interrupt mask register of MADC
- * @isr - Interrupt status register of MADC
+ * @dev: Pointer to device structure for madc
+ * @lock: Mutex protecting this data structure
+ * @requests: Array of request struct corresponding to SW1, SW2 and RT
+ * @use_second_irq: IRQ selection (main or co-processor)
+ * @imr: Interrupt mask register of MADC
+ * @isr: Interrupt status register of MADC
*/
struct twl4030_madc_data {
struct device *dev;
struct mutex lock; /* mutex protecting this data structure */
struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
- int imr;
- int isr;
+ bool use_second_irq;
+ u8 imr;
+ u8 isr;
+};
+
+static int twl4030_madc_read(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct twl4030_madc_data *madc = iio_priv(iio_dev);
+ struct twl4030_madc_request req;
+ int ret;
+
+ req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
+
+ req.channels = BIT(chan->channel);
+ req.active = false;
+ req.func_cb = NULL;
+ req.type = TWL4030_MADC_WAIT;
+ req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
+ req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
+
+ ret = twl4030_madc_conversion(&req);
+ if (ret < 0)
+ return ret;
+
+ *val = req.rbuf[chan->channel];
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info twl4030_madc_iio_info = {
+ .read_raw = &twl4030_madc_read,
+ .driver_module = THIS_MODULE,
+};
+
+#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
+ .type = _type, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ .datasheet_name = _name, \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
+ TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
+ TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
+ TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
+ TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
+ TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
+ TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
+ TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
+ TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
+ TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
+ TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
+ TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
+ TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
+ TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
+ TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
+ TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
+ TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
};
static struct twl4030_madc_data *twl4030_madc;
@@ -91,17 +154,16 @@ twl4030_divider_ratios[16] = {
};
-/*
- * Conversion table from -3 to 55 degree Celcius
- */
-static int therm_tbl[] = {
-30800, 29500, 28300, 27100,
-26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900,
-17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100,
-11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310,
-8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830,
-5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170,
-4040, 3910, 3790, 3670, 3550
+/* Conversion table from -3 to 55 degrees Celcius */
+static int twl4030_therm_tbl[] = {
+ 30800, 29500, 28300, 27100,
+ 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
+ 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
+ 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
+ 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
+ 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
+ 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
+ 3550
};
/*
@@ -133,37 +195,32 @@ const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
},
};
-/*
- * Function to read a particular channel value.
- * @madc - pointer to struct twl4030_madc_data
- * @reg - lsb of ADC Channel
- * If the i2c read fails it returns an error else returns 0.
+/**
+ * twl4030_madc_channel_raw_read() - Function to read a particular channel value
+ * @madc: pointer to struct twl4030_madc_data
+ * @reg: lsb of ADC Channel
+ *
+ * Return: 0 on success, an error code otherwise.
*/
static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
{
- u8 msb, lsb;
+ u16 val;
int ret;
/*
* For each ADC channel, we have MSB and LSB register pair. MSB address
* is always LSB address+1. reg parameter is the address of LSB register
*/
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
+ ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
if (ret) {
- dev_err(madc->dev, "unable to read MSB register 0x%X\n",
- reg + 1);
- return ret;
- }
- ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
- if (ret) {
- dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
+ dev_err(madc->dev, "unable to read register 0x%X\n", reg);
return ret;
}
- return (int)(((msb << 8) | lsb) >> 6);
+ return (int)(val >> 6);
}
/*
- * Return battery temperature
+ * Return battery temperature in degrees Celsius
* Or < 0 on failure.
*/
static int twl4030battery_temperature(int raw_volt)
@@ -172,18 +229,18 @@ static int twl4030battery_temperature(int raw_volt)
int temp, curr, volt, res, ret;
volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
- /* Getting and calculating the supply current in micro ampers */
+ /* Getting and calculating the supply current in micro amperes */
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
REG_BCICTL2);
if (ret < 0)
return ret;
+
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
/* Getting and calculating the thermistor resistance in ohms */
res = volt * 1000 / curr;
/* calculating temperature */
for (temp = 58; temp >= 0; temp--) {
- int actual = therm_tbl[temp];
-
+ int actual = twl4030_therm_tbl[temp];
if ((actual - res) >= 0)
break;
}
@@ -205,11 +262,12 @@ static int twl4030battery_current(int raw_volt)
else /* slope of 0.88 mV/mA */
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
}
+
/*
* Function to read channel values
* @madc - pointer to twl4030_madc_data struct
* @reg_base - Base address of the first channel
- * @Channels - 16 bit bitmap. If the bit is set, channel value is read
+ * @Channels - 16 bit bitmap. If the bit is set, channel's value is read
* @buf - The channel values are stored here. if read fails error
* @raw - Return raw values without conversion
* value is stored
@@ -220,17 +278,17 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
long channels, int *buf,
bool raw)
{
- int count = 0, count_req = 0, i;
+ int count = 0;
+ int i;
u8 reg;
for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
- reg = reg_base + 2 * i;
+ reg = reg_base + (2 * i);
buf[i] = twl4030_madc_channel_raw_read(madc, reg);
if (buf[i] < 0) {
- dev_err(madc->dev,
- "Unable to read register 0x%X\n", reg);
- count_req++;
- continue;
+ dev_err(madc->dev, "Unable to read register 0x%X\n",
+ reg);
+ return buf[i];
}
if (raw) {
count++;
@@ -241,7 +299,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
buf[i] = twl4030battery_current(buf[i]);
if (buf[i] < 0) {
dev_err(madc->dev, "err reading current\n");
- count_req++;
+ return buf[i];
} else {
count++;
buf[i] = buf[i] - 750;
@@ -251,7 +309,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
buf[i] = twl4030battery_temperature(buf[i]);
if (buf[i] < 0) {
dev_err(madc->dev, "err reading temperature\n");
- count_req++;
+ return buf[i];
} else {
buf[i] -= 3;
count++;
@@ -272,8 +330,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
twl4030_divider_ratios[i].numerator);
}
}
- if (count_req)
- dev_err(madc->dev, "%d channel conversion failed\n", count_req);
return count;
}
@@ -297,13 +353,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
madc->imr);
return ret;
}
+
val &= ~(1 << id);
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
if (ret) {
dev_err(madc->dev,
"unable to write imr register 0x%X\n", madc->imr);
return ret;
-
}
return 0;
@@ -366,7 +422,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
continue;
ret = twl4030_madc_disable_irq(madc, i);
if (ret < 0)
- dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
+ dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
madc->requests[i].result_pending = 1;
}
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
@@ -448,21 +504,17 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
{
const struct twl4030_madc_conversion_method *method;
int ret = 0;
+
+ if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
+ return -ENOTSUPP;
+
method = &twl4030_conversion_methods[conv_method];
- switch (conv_method) {
- case TWL4030_MADC_SW1:
- case TWL4030_MADC_SW2:
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- TWL4030_MADC_SW_START, method->ctrl);
- if (ret) {
- dev_err(madc->dev,
- "unable to write ctrl register 0x%X\n",
- method->ctrl);
- return ret;
- }
- break;
- default:
- break;
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
+ method->ctrl);
+ if (ret) {
+ dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
+ method->ctrl);
+ return ret;
}
return 0;
@@ -513,7 +565,6 @@ static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
int twl4030_madc_conversion(struct twl4030_madc_request *req)
{
const struct twl4030_madc_conversion_method *method;
- u8 ch_msb, ch_lsb;
int ret;
if (!req || !twl4030_madc)
@@ -529,38 +580,22 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
ret = -EBUSY;
goto out;
}
- ch_msb = (req->channels >> 8) & 0xff;
- ch_lsb = req->channels & 0xff;
method = &twl4030_conversion_methods[req->method];
/* Select channels to be converted */
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel register 0x%X\n", method->sel + 1);
- goto out;
- }
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
+ ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
if (ret) {
dev_err(twl4030_madc->dev,
- "unable to write sel register 0x%X\n", method->sel + 1);
+ "unable to write sel register 0x%X\n", method->sel);
goto out;
}
/* Select averaging for all channels if do_avg is set */
if (req->do_avg) {
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- ch_msb, method->avg + 1);
+ ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
+ method->avg);
if (ret) {
dev_err(twl4030_madc->dev,
"unable to write avg register 0x%X\n",
- method->avg + 1);
- goto out;
- }
- ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
- ch_lsb, method->avg);
- if (ret) {
- dev_err(twl4030_madc->dev,
- "unable to write sel reg 0x%X\n",
- method->sel + 1);
+ method->avg);
goto out;
}
}
@@ -601,10 +636,6 @@ out:
}
EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
-/*
- * Return channel value
- * Or < 0 on failure.
- */
int twl4030_get_madc_conversion(int channel_no)
{
struct twl4030_madc_request req;
@@ -625,20 +656,25 @@ int twl4030_get_madc_conversion(int channel_no)
}
EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
-/*
+/**
+ * twl4030_madc_set_current_generator() - setup bias current
+ *
+ * @madc: pointer to twl4030_madc_data struct
+ * @chan: can be one of the two values:
+ * TWL4030_BCI_ITHEN
+ * Enables bias current for main battery type reading
+ * TWL4030_BCI_TYPEN
+ * Enables bias current for main battery temperature sensing
+ * @on: enable or disable chan.
+ *
* Function to enable or disable bias current for
* main battery type reading or temperature sensing
- * @madc - pointer to twl4030_madc_data struct
- * @chan - can be one of the two values
- * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
- * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
- * sensing
- * @on - enable or disable chan.
*/
static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
int chan, int on)
{
int ret;
+ int regmask;
u8 regval;
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
@@ -648,10 +684,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
TWL4030_BCI_BCICTL1);
return ret;
}
+
+ regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
if (on)
- regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
+ regval |= regmask;
else
- regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
+ regval &= ~regmask;
+
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
regval, TWL4030_BCI_BCICTL1);
if (ret) {
@@ -666,7 +705,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
/*
* Function that sets MADC software power on bit to enable MADC
* @madc - pointer to twl4030_madc_data struct
- * @on - Enable or disable MADC software powen on bit.
+ * @on - Enable or disable MADC software power on bit.
* returns error if i2c read/write fails else 0
*/
static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
@@ -702,31 +741,52 @@ static int twl4030_madc_probe(struct platform_device *pdev)
{
struct twl4030_madc_data *madc;
struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
- int ret;
+ struct device_node *np = pdev->dev.of_node;
+ int irq, ret;
u8 regval;
+ struct iio_dev *iio_dev = NULL;
- if (!pdata) {
- dev_err(&pdev->dev, "platform_data not available\n");
+ if (!pdata && !np) {
+ dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
return -EINVAL;
}
- madc = kzalloc(sizeof(*madc), GFP_KERNEL);
- if (!madc)
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
+ if (!iio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
return -ENOMEM;
+ }
+ madc = iio_priv(iio_dev);
madc->dev = &pdev->dev;
+ iio_dev->name = dev_name(&pdev->dev);
+ iio_dev->dev.parent = &pdev->dev;
+ iio_dev->dev.of_node = pdev->dev.of_node;
+ iio_dev->info = &twl4030_madc_iio_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = twl4030_madc_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
+
/*
* Phoenix provides 2 interrupt lines. The first one is connected to
* the OMAP. The other one can be connected to the other processor such
* as modem. Hence two separate ISR and IMR registers.
*/
- madc->imr = (pdata->irq_line == 1) ?
- TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
- madc->isr = (pdata->irq_line == 1) ?
- TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+ if (pdata)
+ madc->use_second_irq = (pdata->irq_line != 1);
+ else
+ madc->use_second_irq = of_property_read_bool(np,
+ "ti,system-uses-second-madc-irq");
+
+ madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
+ TWL4030_MADC_IMR1;
+ madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
+ TWL4030_MADC_ISR1;
+
ret = twl4030_madc_set_power(madc, 1);
if (ret < 0)
- goto err_power;
+ return ret;
ret = twl4030_madc_set_current_generator(madc, 0, 1);
if (ret < 0)
goto err_current_generator;
@@ -768,46 +828,63 @@ static int twl4030_madc_probe(struct platform_device *pdev)
}
}
- platform_set_drvdata(pdev, madc);
+ platform_set_drvdata(pdev, iio_dev);
mutex_init(&madc->lock);
- ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
twl4030_madc_threaded_irq_handler,
IRQF_TRIGGER_RISING, "twl4030_madc", madc);
if (ret) {
- dev_dbg(&pdev->dev, "could not request irq\n");
+ dev_err(&pdev->dev, "could not request irq\n");
goto err_i2c;
}
twl4030_madc = madc;
+
+ ret = iio_device_register(iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register iio device\n");
+ goto err_i2c;
+ }
+
return 0;
+
err_i2c:
twl4030_madc_set_current_generator(madc, 0, 0);
err_current_generator:
twl4030_madc_set_power(madc, 0);
-err_power:
- kfree(madc);
-
return ret;
}
static int twl4030_madc_remove(struct platform_device *pdev)
{
- struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
+ struct iio_dev *iio_dev = platform_get_drvdata(pdev);
+ struct twl4030_madc_data *madc = iio_priv(iio_dev);
+
+ iio_device_unregister(iio_dev);
- free_irq(platform_get_irq(pdev, 0), madc);
twl4030_madc_set_current_generator(madc, 0, 0);
twl4030_madc_set_power(madc, 0);
- kfree(madc);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id twl_madc_of_match[] = {
+ { .compatible = "ti,twl4030-madc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_madc_of_match);
+#endif
+
static struct platform_driver twl4030_madc_driver = {
.probe = twl4030_madc_probe,
.remove = twl4030_madc_remove,
.driver = {
.name = "twl4030_madc",
.owner = THIS_MODULE,
- },
+ .of_match_table = of_match_ptr(twl_madc_of_match),
+ },
};
module_platform_driver(twl4030_madc_driver);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index f772981bdcdb..5928ea71dd69 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY
config INPUT_MAX8997_HAPTIC
tristate "MAXIM MAX8997 haptic controller support"
- depends on PWM && HAVE_PWM && MFD_MAX8997
+ depends on PWM && MFD_MAX8997
select INPUT_FF_MEMLESS
help
This option enables device driver support for the haptic controller
@@ -470,7 +470,7 @@ config INPUT_PCF8574
config INPUT_PWM_BEEPER
tristate "PWM beeper support"
- depends on PWM && HAVE_PWM
+ depends on PWM
help
Say Y here to get support for PWM based beeper devices.
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
index 17ccba88d636..ed8e5e8449d3 100644
--- a/drivers/input/misc/ixp4xx-beeper.c
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -67,7 +67,7 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned
}
if (value > 20 && value < 32767)
- count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1;
+ count = (ixp4xx_timer_freq / (value * 4)) - 1;
ixp4xx_spkr_control(pin, count);
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 79bbc21c1d01..df56e4c74a7e 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -207,7 +207,7 @@ config SHMOBILE_IOMMU
bool "IOMMU for Renesas IPMMU/IPMMUI"
default n
depends on ARM
- depends on SH_MOBILE || COMPILE_TEST
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select IOMMU_API
select ARM_DMA_USE_IOMMU
select SHMOBILE_IPMMU
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index faf0da4bb3a2..c949520bd196 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -963,7 +963,7 @@ static void build_inv_iommu_pasid(struct iommu_cmd *cmd, u16 domid, int pasid,
address &= ~(0xfffULL);
- cmd->data[0] = pasid & PASID_MASK;
+ cmd->data[0] = pasid;
cmd->data[1] = domid;
cmd->data[2] = lower_32_bits(address);
cmd->data[3] = upper_32_bits(address);
@@ -982,10 +982,10 @@ static void build_inv_iotlb_pasid(struct iommu_cmd *cmd, u16 devid, int pasid,
address &= ~(0xfffULL);
cmd->data[0] = devid;
- cmd->data[0] |= (pasid & 0xff) << 16;
+ cmd->data[0] |= ((pasid >> 8) & 0xff) << 16;
cmd->data[0] |= (qdep & 0xff) << 24;
cmd->data[1] = devid;
- cmd->data[1] |= ((pasid >> 8) & 0xfff) << 16;
+ cmd->data[1] |= (pasid & 0xff) << 16;
cmd->data[2] = lower_32_bits(address);
cmd->data[2] |= CMD_INV_IOMMU_PAGES_GN_MASK;
cmd->data[3] = upper_32_bits(address);
@@ -1001,7 +1001,7 @@ static void build_complete_ppr(struct iommu_cmd *cmd, u16 devid, int pasid,
cmd->data[0] = devid;
if (gn) {
- cmd->data[1] = pasid & PASID_MASK;
+ cmd->data[1] = pasid;
cmd->data[2] = CMD_INV_IOMMU_PAGES_GN_MASK;
}
cmd->data[3] = tag & 0x1ff;
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 28b4bea7c109..b76c58dbe30c 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -150,7 +150,7 @@ int amd_iommus_present;
bool amd_iommu_np_cache __read_mostly;
bool amd_iommu_iotlb_sup __read_mostly = true;
-u32 amd_iommu_max_pasids __read_mostly = ~0;
+u32 amd_iommu_max_pasid __read_mostly = ~0;
bool amd_iommu_v2_present __read_mostly;
bool amd_iommu_pc_present __read_mostly;
@@ -1231,14 +1231,16 @@ static int iommu_init_pci(struct amd_iommu *iommu)
if (iommu_feature(iommu, FEATURE_GT)) {
int glxval;
- u32 pasids;
- u64 shift;
+ u32 max_pasid;
+ u64 pasmax;
- shift = iommu->features & FEATURE_PASID_MASK;
- shift >>= FEATURE_PASID_SHIFT;
- pasids = (1 << shift);
+ pasmax = iommu->features & FEATURE_PASID_MASK;
+ pasmax >>= FEATURE_PASID_SHIFT;
+ max_pasid = (1 << (pasmax + 1)) - 1;
- amd_iommu_max_pasids = min(amd_iommu_max_pasids, pasids);
+ amd_iommu_max_pasid = min(amd_iommu_max_pasid, max_pasid);
+
+ BUG_ON(amd_iommu_max_pasid & ~PASID_MASK);
glxval = iommu->features & FEATURE_GLXVAL_MASK;
glxval >>= FEATURE_GLXVAL_SHIFT;
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index cff039df056e..f1a5abf11acf 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -99,7 +99,12 @@
#define FEATURE_GLXVAL_SHIFT 14
#define FEATURE_GLXVAL_MASK (0x03ULL << FEATURE_GLXVAL_SHIFT)
-#define PASID_MASK 0x000fffff
+/* Note:
+ * The current driver only support 16-bit PASID.
+ * Currently, hardware only implement upto 16-bit PASID
+ * even though the spec says it could have upto 20 bits.
+ */
+#define PASID_MASK 0x0000ffff
/* MMIO status bits */
#define MMIO_STATUS_EVT_INT_MASK (1 << 1)
@@ -697,8 +702,8 @@ extern unsigned long *amd_iommu_pd_alloc_bitmap;
*/
extern u32 amd_iommu_unmap_flush;
-/* Smallest number of PASIDs supported by any IOMMU in the system */
-extern u32 amd_iommu_max_pasids;
+/* Smallest max PASID supported by any IOMMU in the system */
+extern u32 amd_iommu_max_pasid;
extern bool amd_iommu_v2_present;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 1d9ab39af29f..8b89e33a89fe 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -48,7 +48,7 @@
#include <asm/pgalloc.h>
/* Maximum number of stream IDs assigned to a single device */
-#define MAX_MASTER_STREAMIDS 8
+#define MAX_MASTER_STREAMIDS MAX_PHANDLE_ARGS
/* Maximum number of context banks per SMMU */
#define ARM_SMMU_MAX_CBS 128
@@ -60,6 +60,16 @@
#define ARM_SMMU_GR0(smmu) ((smmu)->base)
#define ARM_SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize)
+/*
+ * SMMU global address space with conditional offset to access secure
+ * aliases of non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448,
+ * nsGFSYNR0: 0x450)
+ */
+#define ARM_SMMU_GR0_NS(smmu) \
+ ((smmu)->base + \
+ ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \
+ ? 0x400 : 0))
+
/* Page table bits */
#define ARM_SMMU_PTE_XN (((pteval_t)3) << 53)
#define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52)
@@ -351,6 +361,9 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3)
#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4)
u32 features;
+
+#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
+ u32 options;
int version;
u32 num_context_banks;
@@ -401,6 +414,29 @@ struct arm_smmu_domain {
static DEFINE_SPINLOCK(arm_smmu_devices_lock);
static LIST_HEAD(arm_smmu_devices);
+struct arm_smmu_option_prop {
+ u32 opt;
+ const char *prop;
+};
+
+static struct arm_smmu_option_prop arm_smmu_options [] = {
+ { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
+ { 0, NULL},
+};
+
+static void parse_driver_options(struct arm_smmu_device *smmu)
+{
+ int i = 0;
+ do {
+ if (of_property_read_bool(smmu->dev->of_node,
+ arm_smmu_options[i].prop)) {
+ smmu->options |= arm_smmu_options[i].opt;
+ dev_notice(smmu->dev, "option %s\n",
+ arm_smmu_options[i].prop);
+ }
+ } while (arm_smmu_options[++i].opt);
+}
+
static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
struct device_node *dev_node)
{
@@ -614,16 +650,16 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
{
u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
struct arm_smmu_device *smmu = dev;
- void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+ void __iomem *gr0_base = ARM_SMMU_GR0_NS(smmu);
gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
- if (!gfsr)
- return IRQ_NONE;
-
gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
+ if (!gfsr)
+ return IRQ_NONE;
+
dev_err_ratelimited(smmu->dev,
"Unexpected global fault, this could be serious\n");
dev_err_ratelimited(smmu->dev,
@@ -642,7 +678,7 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
/* Ensure new page tables are visible to the hardware walker */
if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) {
- dsb();
+ dsb(ishst);
} else {
/*
* If the SMMU can't walk tables in the CPU caches, treat them
@@ -990,9 +1026,8 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
/*
* Recursively free the page tables for this domain. We don't
- * care about speculative TLB filling, because the TLB will be
- * nuked next time this context bank is re-allocated and no devices
- * currently map to these tables.
+ * care about speculative TLB filling because the tables should
+ * not be active in any context bank at this point (SCTLR.M is 0).
*/
pgd = pgd_base;
for (i = 0; i < PTRS_PER_PGD; ++i) {
@@ -1218,7 +1253,7 @@ static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
unsigned long addr, unsigned long end,
- unsigned long pfn, int flags, int stage)
+ unsigned long pfn, int prot, int stage)
{
pte_t *pte, *start;
pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN;
@@ -1240,28 +1275,28 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
if (stage == 1) {
pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG;
- if (!(flags & IOMMU_WRITE) && (flags & IOMMU_READ))
+ if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
pteval |= ARM_SMMU_PTE_AP_RDONLY;
- if (flags & IOMMU_CACHE)
+ if (prot & IOMMU_CACHE)
pteval |= (MAIR_ATTR_IDX_CACHE <<
ARM_SMMU_PTE_ATTRINDX_SHIFT);
} else {
pteval |= ARM_SMMU_PTE_HAP_FAULT;
- if (flags & IOMMU_READ)
+ if (prot & IOMMU_READ)
pteval |= ARM_SMMU_PTE_HAP_READ;
- if (flags & IOMMU_WRITE)
+ if (prot & IOMMU_WRITE)
pteval |= ARM_SMMU_PTE_HAP_WRITE;
- if (flags & IOMMU_CACHE)
+ if (prot & IOMMU_CACHE)
pteval |= ARM_SMMU_PTE_MEMATTR_OIWB;
else
pteval |= ARM_SMMU_PTE_MEMATTR_NC;
}
/* If no access, create a faulting entry to avoid TLB fills */
- if (flags & IOMMU_EXEC)
+ if (prot & IOMMU_EXEC)
pteval &= ~ARM_SMMU_PTE_XN;
- else if (!(flags & (IOMMU_READ | IOMMU_WRITE)))
+ else if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
pteval &= ~ARM_SMMU_PTE_PAGE;
pteval |= ARM_SMMU_PTE_SH_IS;
@@ -1323,7 +1358,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
unsigned long addr, unsigned long end,
- phys_addr_t phys, int flags, int stage)
+ phys_addr_t phys, int prot, int stage)
{
int ret;
pmd_t *pmd;
@@ -1347,7 +1382,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
do {
next = pmd_addr_end(addr, end);
ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn,
- flags, stage);
+ prot, stage);
phys += next - addr;
} while (pmd++, addr = next, addr < end);
@@ -1356,7 +1391,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
unsigned long addr, unsigned long end,
- phys_addr_t phys, int flags, int stage)
+ phys_addr_t phys, int prot, int stage)
{
int ret = 0;
pud_t *pud;
@@ -1380,7 +1415,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
do {
next = pud_addr_end(addr, end);
ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys,
- flags, stage);
+ prot, stage);
phys += next - addr;
} while (pud++, addr = next, addr < end);
@@ -1389,7 +1424,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
unsigned long iova, phys_addr_t paddr,
- size_t size, int flags)
+ size_t size, int prot)
{
int ret, stage;
unsigned long end;
@@ -1397,7 +1432,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
pgd_t *pgd = root_cfg->pgd;
struct arm_smmu_device *smmu = root_cfg->smmu;
- unsigned long irqflags;
+ unsigned long flags;
if (root_cfg->cbar == CBAR_TYPE_S2_TRANS) {
stage = 2;
@@ -1420,14 +1455,14 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
if (paddr & ~output_mask)
return -ERANGE;
- spin_lock_irqsave(&smmu_domain->lock, irqflags);
+ spin_lock_irqsave(&smmu_domain->lock, flags);
pgd += pgd_index(iova);
end = iova + size;
do {
unsigned long next = pgd_addr_end(iova, end);
ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr,
- flags, stage);
+ prot, stage);
if (ret)
goto out_unlock;
@@ -1436,13 +1471,13 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
} while (pgd++, iova != end);
out_unlock:
- spin_unlock_irqrestore(&smmu_domain->lock, irqflags);
+ spin_unlock_irqrestore(&smmu_domain->lock, flags);
return ret;
}
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
- phys_addr_t paddr, size_t size, int flags)
+ phys_addr_t paddr, size_t size, int prot)
{
struct arm_smmu_domain *smmu_domain = domain->priv;
@@ -1453,7 +1488,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
if ((phys_addr_t)iova & ~smmu_domain->output_mask)
return -ERANGE;
- return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, flags);
+ return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot);
}
static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
@@ -1597,9 +1632,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
int i = 0;
u32 reg;
- /* Clear Global FSR */
- reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
- writel(reg, gr0_base + ARM_SMMU_GR0_sGFSR);
+ /* clear global FSR */
+ reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
+ writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
/* Mark all SMRn as invalid and all S2CRn as bypass */
for (i = 0; i < smmu->num_mapping_groups; ++i) {
@@ -1619,7 +1654,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
- reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+ reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
/* Enable fault reporting */
reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
@@ -1638,7 +1673,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
/* Push the button */
arm_smmu_tlb_sync(smmu);
- writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sCR0);
+ writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
}
static int arm_smmu_id_size_to_bits(int size)
@@ -1885,6 +1920,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (err)
goto out_put_parent;
+ parse_driver_options(smmu);
+
if (smmu->version > 1 &&
smmu->num_context_banks != smmu->num_context_irqs) {
dev_err(dev,
@@ -1969,7 +2006,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
free_irq(smmu->irqs[i], smmu);
/* Turn the thing off */
- writel_relaxed(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
+ writel(sCR0_CLIENTPD,ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
return 0;
}
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 158156543410..f445c10df8df 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -43,14 +43,24 @@
#include "irq_remapping.h"
-/* No locks are needed as DMA remapping hardware unit
- * list is constructed at boot time and hotplug of
- * these units are not supported by the architecture.
+/*
+ * Assumptions:
+ * 1) The hotplug framework guarentees that DMAR unit will be hot-added
+ * before IO devices managed by that unit.
+ * 2) The hotplug framework guarantees that DMAR unit will be hot-removed
+ * after IO devices managed by that unit.
+ * 3) Hotplug events are rare.
+ *
+ * Locking rules for DMA and interrupt remapping related global data structures:
+ * 1) Use dmar_global_lock in process context
+ * 2) Use RCU in interrupt context
*/
+DECLARE_RWSEM(dmar_global_lock);
LIST_HEAD(dmar_drhd_units);
struct acpi_table_header * __initdata dmar_tbl;
static acpi_size dmar_tbl_size;
+static int dmar_dev_scope_status = 1;
static int alloc_iommu(struct dmar_drhd_unit *drhd);
static void free_iommu(struct intel_iommu *iommu);
@@ -62,73 +72,20 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
* the very end.
*/
if (drhd->include_all)
- list_add_tail(&drhd->list, &dmar_drhd_units);
+ list_add_tail_rcu(&drhd->list, &dmar_drhd_units);
else
- list_add(&drhd->list, &dmar_drhd_units);
+ list_add_rcu(&drhd->list, &dmar_drhd_units);
}
-static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
- struct pci_dev **dev, u16 segment)
-{
- struct pci_bus *bus;
- struct pci_dev *pdev = NULL;
- struct acpi_dmar_pci_path *path;
- int count;
-
- bus = pci_find_bus(segment, scope->bus);
- path = (struct acpi_dmar_pci_path *)(scope + 1);
- count = (scope->length - sizeof(struct acpi_dmar_device_scope))
- / sizeof(struct acpi_dmar_pci_path);
-
- while (count) {
- if (pdev)
- pci_dev_put(pdev);
- /*
- * Some BIOSes list non-exist devices in DMAR table, just
- * ignore it
- */
- if (!bus) {
- pr_warn("Device scope bus [%d] not found\n", scope->bus);
- break;
- }
- pdev = pci_get_slot(bus, PCI_DEVFN(path->device, path->function));
- if (!pdev) {
- /* warning will be printed below */
- break;
- }
- path ++;
- count --;
- bus = pdev->subordinate;
- }
- if (!pdev) {
- pr_warn("Device scope device [%04x:%02x:%02x.%02x] not found\n",
- segment, scope->bus, path->device, path->function);
- return 0;
- }
- if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && \
- pdev->subordinate) || (scope->entry_type == \
- ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) {
- pci_dev_put(pdev);
- pr_warn("Device scope type does not match for %s\n",
- pci_name(pdev));
- return -EINVAL;
- }
- *dev = pdev;
- return 0;
-}
-
-int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
- struct pci_dev ***devices, u16 segment)
+void *dmar_alloc_dev_scope(void *start, void *end, int *cnt)
{
struct acpi_dmar_device_scope *scope;
- void * tmp = start;
- int index;
- int ret;
*cnt = 0;
while (start < end) {
scope = start;
- if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
+ if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ACPI ||
+ scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE)
(*cnt)++;
else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC &&
@@ -138,43 +95,236 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt,
start += scope->length;
}
if (*cnt == 0)
- return 0;
+ return NULL;
- *devices = kcalloc(*cnt, sizeof(struct pci_dev *), GFP_KERNEL);
- if (!*devices)
- return -ENOMEM;
+ return kcalloc(*cnt, sizeof(struct dmar_dev_scope), GFP_KERNEL);
+}
- start = tmp;
- index = 0;
- while (start < end) {
+void dmar_free_dev_scope(struct dmar_dev_scope **devices, int *cnt)
+{
+ int i;
+ struct device *tmp_dev;
+
+ if (*devices && *cnt) {
+ for_each_active_dev_scope(*devices, *cnt, i, tmp_dev)
+ put_device(tmp_dev);
+ kfree(*devices);
+ }
+
+ *devices = NULL;
+ *cnt = 0;
+}
+
+/* Optimize out kzalloc()/kfree() for normal cases */
+static char dmar_pci_notify_info_buf[64];
+
+static struct dmar_pci_notify_info *
+dmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event)
+{
+ int level = 0;
+ size_t size;
+ struct pci_dev *tmp;
+ struct dmar_pci_notify_info *info;
+
+ BUG_ON(dev->is_virtfn);
+
+ /* Only generate path[] for device addition event */
+ if (event == BUS_NOTIFY_ADD_DEVICE)
+ for (tmp = dev; tmp; tmp = tmp->bus->self)
+ level++;
+
+ size = sizeof(*info) + level * sizeof(struct acpi_dmar_pci_path);
+ if (size <= sizeof(dmar_pci_notify_info_buf)) {
+ info = (struct dmar_pci_notify_info *)dmar_pci_notify_info_buf;
+ } else {
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info) {
+ pr_warn("Out of memory when allocating notify_info "
+ "for %s.\n", pci_name(dev));
+ if (dmar_dev_scope_status == 0)
+ dmar_dev_scope_status = -ENOMEM;
+ return NULL;
+ }
+ }
+
+ info->event = event;
+ info->dev = dev;
+ info->seg = pci_domain_nr(dev->bus);
+ info->level = level;
+ if (event == BUS_NOTIFY_ADD_DEVICE) {
+ for (tmp = dev, level--; tmp; tmp = tmp->bus->self) {
+ info->path[level].device = PCI_SLOT(tmp->devfn);
+ info->path[level].function = PCI_FUNC(tmp->devfn);
+ if (pci_is_root_bus(tmp->bus))
+ info->bus = tmp->bus->number;
+ }
+ }
+
+ return info;
+}
+
+static inline void dmar_free_pci_notify_info(struct dmar_pci_notify_info *info)
+{
+ if ((void *)info != dmar_pci_notify_info_buf)
+ kfree(info);
+}
+
+static bool dmar_match_pci_path(struct dmar_pci_notify_info *info, int bus,
+ struct acpi_dmar_pci_path *path, int count)
+{
+ int i;
+
+ if (info->bus != bus)
+ return false;
+ if (info->level != count)
+ return false;
+
+ for (i = 0; i < count; i++) {
+ if (path[i].device != info->path[i].device ||
+ path[i].function != info->path[i].function)
+ return false;
+ }
+
+ return true;
+}
+
+/* Return: > 0 if match found, 0 if no match found, < 0 if error happens */
+int dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
+ void *start, void*end, u16 segment,
+ struct dmar_dev_scope *devices,
+ int devices_cnt)
+{
+ int i, level;
+ struct device *tmp, *dev = &info->dev->dev;
+ struct acpi_dmar_device_scope *scope;
+ struct acpi_dmar_pci_path *path;
+
+ if (segment != info->seg)
+ return 0;
+
+ for (; start < end; start += scope->length) {
scope = start;
- if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
- scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) {
- ret = dmar_parse_one_dev_scope(scope,
- &(*devices)[index], segment);
- if (ret) {
- dmar_free_dev_scope(devices, cnt);
- return ret;
- }
- index ++;
+ if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
+ scope->entry_type != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
+ continue;
+
+ path = (struct acpi_dmar_pci_path *)(scope + 1);
+ level = (scope->length - sizeof(*scope)) / sizeof(*path);
+ if (!dmar_match_pci_path(info, scope->bus, path, level))
+ continue;
+
+ if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT) ^
+ (info->dev->hdr_type == PCI_HEADER_TYPE_NORMAL)) {
+ pr_warn("Device scope type does not match for %s\n",
+ pci_name(info->dev));
+ return -EINVAL;
}
- start += scope->length;
+
+ for_each_dev_scope(devices, devices_cnt, i, tmp)
+ if (tmp == NULL) {
+ devices[i].bus = info->dev->bus->number;
+ devices[i].devfn = info->dev->devfn;
+ rcu_assign_pointer(devices[i].dev,
+ get_device(dev));
+ return 1;
+ }
+ BUG_ON(i >= devices_cnt);
}
return 0;
}
-void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt)
+int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, u16 segment,
+ struct dmar_dev_scope *devices, int count)
{
- if (*devices && *cnt) {
- while (--*cnt >= 0)
- pci_dev_put((*devices)[*cnt]);
- kfree(*devices);
- *devices = NULL;
- *cnt = 0;
+ int index;
+ struct device *tmp;
+
+ if (info->seg != segment)
+ return 0;
+
+ for_each_active_dev_scope(devices, count, index, tmp)
+ if (tmp == &info->dev->dev) {
+ rcu_assign_pointer(devices[index].dev, NULL);
+ synchronize_rcu();
+ put_device(tmp);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dmar_pci_bus_add_dev(struct dmar_pci_notify_info *info)
+{
+ int ret = 0;
+ struct dmar_drhd_unit *dmaru;
+ struct acpi_dmar_hardware_unit *drhd;
+
+ for_each_drhd_unit(dmaru) {
+ if (dmaru->include_all)
+ continue;
+
+ drhd = container_of(dmaru->hdr,
+ struct acpi_dmar_hardware_unit, header);
+ ret = dmar_insert_dev_scope(info, (void *)(drhd + 1),
+ ((void *)drhd) + drhd->header.length,
+ dmaru->segment,
+ dmaru->devices, dmaru->devices_cnt);
+ if (ret != 0)
+ break;
}
+ if (ret >= 0)
+ ret = dmar_iommu_notify_scope_dev(info);
+ if (ret < 0 && dmar_dev_scope_status == 0)
+ dmar_dev_scope_status = ret;
+
+ return ret;
}
+static void dmar_pci_bus_del_dev(struct dmar_pci_notify_info *info)
+{
+ struct dmar_drhd_unit *dmaru;
+
+ for_each_drhd_unit(dmaru)
+ if (dmar_remove_dev_scope(info, dmaru->segment,
+ dmaru->devices, dmaru->devices_cnt))
+ break;
+ dmar_iommu_notify_scope_dev(info);
+}
+
+static int dmar_pci_bus_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pci_dev *pdev = to_pci_dev(data);
+ struct dmar_pci_notify_info *info;
+
+ /* Only care about add/remove events for physical functions */
+ if (pdev->is_virtfn)
+ return NOTIFY_DONE;
+ if (action != BUS_NOTIFY_ADD_DEVICE && action != BUS_NOTIFY_DEL_DEVICE)
+ return NOTIFY_DONE;
+
+ info = dmar_alloc_pci_notify_info(pdev, action);
+ if (!info)
+ return NOTIFY_DONE;
+
+ down_write(&dmar_global_lock);
+ if (action == BUS_NOTIFY_ADD_DEVICE)
+ dmar_pci_bus_add_dev(info);
+ else if (action == BUS_NOTIFY_DEL_DEVICE)
+ dmar_pci_bus_del_dev(info);
+ up_write(&dmar_global_lock);
+
+ dmar_free_pci_notify_info(info);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dmar_pci_bus_nb = {
+ .notifier_call = dmar_pci_bus_notifier,
+ .priority = INT_MIN,
+};
+
/**
* dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
* structure which uniquely represent one DMA remapping hardware unit
@@ -196,9 +346,18 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
dmaru->reg_base_addr = drhd->address;
dmaru->segment = drhd->segment;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
+ dmaru->devices = dmar_alloc_dev_scope((void *)(drhd + 1),
+ ((void *)drhd) + drhd->header.length,
+ &dmaru->devices_cnt);
+ if (dmaru->devices_cnt && dmaru->devices == NULL) {
+ kfree(dmaru);
+ return -ENOMEM;
+ }
ret = alloc_iommu(dmaru);
if (ret) {
+ dmar_free_dev_scope(&dmaru->devices,
+ &dmaru->devices_cnt);
kfree(dmaru);
return ret;
}
@@ -215,19 +374,24 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru)
kfree(dmaru);
}
-static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru)
+static int __init dmar_parse_one_andd(struct acpi_dmar_header *header)
{
- struct acpi_dmar_hardware_unit *drhd;
-
- drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;
-
- if (dmaru->include_all)
- return 0;
+ struct acpi_dmar_andd *andd = (void *)header;
+
+ /* Check for NUL termination within the designated length */
+ if (strnlen(andd->object_name, header->length - 8) == header->length - 8) {
+ WARN_TAINT(1, TAINT_FIRMWARE_WORKAROUND,
+ "Your BIOS is broken; ANDD object name is not NUL-terminated\n"
+ "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
+ dmi_get_system_info(DMI_BIOS_VENDOR),
+ dmi_get_system_info(DMI_BIOS_VERSION),
+ dmi_get_system_info(DMI_PRODUCT_VERSION));
+ return -EINVAL;
+ }
+ pr_info("ANDD device: %x name: %s\n", andd->device_number,
+ andd->object_name);
- return dmar_parse_dev_scope((void *)(drhd + 1),
- ((void *)drhd) + drhd->header.length,
- &dmaru->devices_cnt, &dmaru->devices,
- drhd->segment);
+ return 0;
}
#ifdef CONFIG_ACPI_NUMA
@@ -293,6 +457,10 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
(unsigned long long)rhsa->base_address,
rhsa->proximity_domain);
break;
+ case ACPI_DMAR_TYPE_ANDD:
+ /* We don't print this here because we need to sanity-check
+ it first. So print it in dmar_parse_one_andd() instead. */
+ break;
}
}
@@ -378,6 +546,9 @@ parse_dmar_table(void)
ret = dmar_parse_one_rhsa(entry_header);
#endif
break;
+ case ACPI_DMAR_TYPE_ANDD:
+ ret = dmar_parse_one_andd(entry_header);
+ break;
default:
pr_warn("Unknown DMAR structure type %d\n",
entry_header->type);
@@ -394,14 +565,15 @@ parse_dmar_table(void)
return ret;
}
-static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
- struct pci_dev *dev)
+static int dmar_pci_device_match(struct dmar_dev_scope devices[],
+ int cnt, struct pci_dev *dev)
{
int index;
+ struct device *tmp;
while (dev) {
- for (index = 0; index < cnt; index++)
- if (dev == devices[index])
+ for_each_active_dev_scope(devices, cnt, index, tmp)
+ if (dev_is_pci(tmp) && dev == to_pci_dev(tmp))
return 1;
/* Check our parent */
@@ -414,11 +586,12 @@ static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
struct dmar_drhd_unit *
dmar_find_matched_drhd_unit(struct pci_dev *dev)
{
- struct dmar_drhd_unit *dmaru = NULL;
+ struct dmar_drhd_unit *dmaru;
struct acpi_dmar_hardware_unit *drhd;
dev = pci_physfn(dev);
+ rcu_read_lock();
for_each_drhd_unit(dmaru) {
drhd = container_of(dmaru->hdr,
struct acpi_dmar_hardware_unit,
@@ -426,44 +599,128 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev)
if (dmaru->include_all &&
drhd->segment == pci_domain_nr(dev->bus))
- return dmaru;
+ goto out;
if (dmar_pci_device_match(dmaru->devices,
dmaru->devices_cnt, dev))
- return dmaru;
+ goto out;
}
+ dmaru = NULL;
+out:
+ rcu_read_unlock();
- return NULL;
+ return dmaru;
}
-int __init dmar_dev_scope_init(void)
+static void __init dmar_acpi_insert_dev_scope(u8 device_number,
+ struct acpi_device *adev)
{
- static int dmar_dev_scope_initialized;
- struct dmar_drhd_unit *drhd;
- int ret = -ENODEV;
-
- if (dmar_dev_scope_initialized)
- return dmar_dev_scope_initialized;
+ struct dmar_drhd_unit *dmaru;
+ struct acpi_dmar_hardware_unit *drhd;
+ struct acpi_dmar_device_scope *scope;
+ struct device *tmp;
+ int i;
+ struct acpi_dmar_pci_path *path;
- if (list_empty(&dmar_drhd_units))
- goto fail;
+ for_each_drhd_unit(dmaru) {
+ drhd = container_of(dmaru->hdr,
+ struct acpi_dmar_hardware_unit,
+ header);
- list_for_each_entry(drhd, &dmar_drhd_units, list) {
- ret = dmar_parse_dev(drhd);
- if (ret)
- goto fail;
+ for (scope = (void *)(drhd + 1);
+ (unsigned long)scope < ((unsigned long)drhd) + drhd->header.length;
+ scope = ((void *)scope) + scope->length) {
+ if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ACPI)
+ continue;
+ if (scope->enumeration_id != device_number)
+ continue;
+
+ path = (void *)(scope + 1);
+ pr_info("ACPI device \"%s\" under DMAR at %llx as %02x:%02x.%d\n",
+ dev_name(&adev->dev), dmaru->reg_base_addr,
+ scope->bus, path->device, path->function);
+ for_each_dev_scope(dmaru->devices, dmaru->devices_cnt, i, tmp)
+ if (tmp == NULL) {
+ dmaru->devices[i].bus = scope->bus;
+ dmaru->devices[i].devfn = PCI_DEVFN(path->device,
+ path->function);
+ rcu_assign_pointer(dmaru->devices[i].dev,
+ get_device(&adev->dev));
+ return;
+ }
+ BUG_ON(i >= dmaru->devices_cnt);
+ }
}
+ pr_warn("No IOMMU scope found for ANDD enumeration ID %d (%s)\n",
+ device_number, dev_name(&adev->dev));
+}
- ret = dmar_parse_rmrr_atsr_dev();
- if (ret)
- goto fail;
+static int __init dmar_acpi_dev_scope_init(void)
+{
+ struct acpi_dmar_andd *andd;
+
+ if (dmar_tbl == NULL)
+ return -ENODEV;
- dmar_dev_scope_initialized = 1;
+ for (andd = (void *)dmar_tbl + sizeof(struct acpi_table_dmar);
+ ((unsigned long)andd) < ((unsigned long)dmar_tbl) + dmar_tbl->length;
+ andd = ((void *)andd) + andd->header.length) {
+ if (andd->header.type == ACPI_DMAR_TYPE_ANDD) {
+ acpi_handle h;
+ struct acpi_device *adev;
+
+ if (!ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT,
+ andd->object_name,
+ &h))) {
+ pr_err("Failed to find handle for ACPI object %s\n",
+ andd->object_name);
+ continue;
+ }
+ acpi_bus_get_device(h, &adev);
+ if (!adev) {
+ pr_err("Failed to get device for ACPI object %s\n",
+ andd->object_name);
+ continue;
+ }
+ dmar_acpi_insert_dev_scope(andd->device_number, adev);
+ }
+ }
return 0;
+}
-fail:
- dmar_dev_scope_initialized = ret;
- return ret;
+int __init dmar_dev_scope_init(void)
+{
+ struct pci_dev *dev = NULL;
+ struct dmar_pci_notify_info *info;
+
+ if (dmar_dev_scope_status != 1)
+ return dmar_dev_scope_status;
+
+ if (list_empty(&dmar_drhd_units)) {
+ dmar_dev_scope_status = -ENODEV;
+ } else {
+ dmar_dev_scope_status = 0;
+
+ dmar_acpi_dev_scope_init();
+
+ for_each_pci_dev(dev) {
+ if (dev->is_virtfn)
+ continue;
+
+ info = dmar_alloc_pci_notify_info(dev,
+ BUS_NOTIFY_ADD_DEVICE);
+ if (!info) {
+ return dmar_dev_scope_status;
+ } else {
+ dmar_pci_bus_add_dev(info);
+ dmar_free_pci_notify_info(info);
+ }
+ }
+
+ bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
+ }
+
+ return dmar_dev_scope_status;
}
@@ -557,6 +814,7 @@ int __init detect_intel_iommu(void)
{
int ret;
+ down_write(&dmar_global_lock);
ret = dmar_table_detect();
if (ret)
ret = check_zero_address();
@@ -574,6 +832,7 @@ int __init detect_intel_iommu(void)
}
early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size);
dmar_tbl = NULL;
+ up_write(&dmar_global_lock);
return ret ? 1 : -ENODEV;
}
@@ -696,6 +955,7 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
}
iommu->agaw = agaw;
iommu->msagaw = msagaw;
+ iommu->segment = drhd->segment;
iommu->node = -1;
@@ -1386,10 +1646,15 @@ static int __init dmar_free_unused_resources(void)
if (irq_remapping_enabled || intel_iommu_enabled)
return 0;
+ if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units))
+ bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);
+
+ down_write(&dmar_global_lock);
list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) {
list_del(&dmaru->list);
dmar_free_drhd(dmaru);
}
+ up_write(&dmar_global_lock);
return 0;
}
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index a22c86c867fa..69fa7da5e48b 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, Intel Corporation.
+ * Copyright © 2006-2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -10,15 +10,11 @@
* 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.
- *
- * Copyright (C) 2006-2008 Intel Corporation
- * Author: Ashok Raj <ashok.raj@intel.com>
- * Author: Shaohua Li <shaohua.li@intel.com>
- * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
- * Author: Fenghua Yu <fenghua.yu@intel.com>
+ * Authors: David Woodhouse <dwmw2@infradead.org>,
+ * Ashok Raj <ashok.raj@intel.com>,
+ * Shaohua Li <shaohua.li@intel.com>,
+ * Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>,
+ * Fenghua Yu <fenghua.yu@intel.com>
*/
#include <linux/init.h>
@@ -33,6 +29,7 @@
#include <linux/dmar.h>
#include <linux/dma-mapping.h>
#include <linux/mempool.h>
+#include <linux/memory.h>
#include <linux/timer.h>
#include <linux/iova.h>
#include <linux/iommu.h>
@@ -372,14 +369,36 @@ struct dmar_domain {
struct device_domain_info {
struct list_head link; /* link to domain siblings */
struct list_head global; /* link to global list */
- int segment; /* PCI domain */
u8 bus; /* PCI bus number */
u8 devfn; /* PCI devfn number */
- struct pci_dev *dev; /* it's NULL for PCIe-to-PCI bridge */
+ struct device *dev; /* it's NULL for PCIe-to-PCI bridge */
struct intel_iommu *iommu; /* IOMMU used by this device */
struct dmar_domain *domain; /* pointer to domain */
};
+struct dmar_rmrr_unit {
+ struct list_head list; /* list of rmrr units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
+ u64 base_address; /* reserved base address*/
+ u64 end_address; /* reserved end address */
+ struct dmar_dev_scope *devices; /* target devices */
+ int devices_cnt; /* target device count */
+};
+
+struct dmar_atsr_unit {
+ struct list_head list; /* list of ATSR units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
+ struct dmar_dev_scope *devices; /* target devices */
+ int devices_cnt; /* target device count */
+ u8 include_all:1; /* include all ports */
+};
+
+static LIST_HEAD(dmar_atsr_units);
+static LIST_HEAD(dmar_rmrr_units);
+
+#define for_each_rmrr_units(rmrr) \
+ list_for_each_entry(rmrr, &dmar_rmrr_units, list)
+
static void flush_unmaps_timeout(unsigned long data);
static DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0);
@@ -389,6 +408,7 @@ struct deferred_flush_tables {
int next;
struct iova *iova[HIGH_WATER_MARK];
struct dmar_domain *domain[HIGH_WATER_MARK];
+ struct page *freelist[HIGH_WATER_MARK];
};
static struct deferred_flush_tables *deferred_flush;
@@ -402,7 +422,12 @@ static LIST_HEAD(unmaps_to_do);
static int timer_on;
static long list_size;
+static void domain_exit(struct dmar_domain *domain);
static void domain_remove_dev_info(struct dmar_domain *domain);
+static void domain_remove_one_dev_info(struct dmar_domain *domain,
+ struct device *dev);
+static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
+ struct device *dev);
#ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
int dmar_disabled = 0;
@@ -566,18 +591,31 @@ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain)
static void domain_update_iommu_coherency(struct dmar_domain *domain)
{
- int i;
-
- i = find_first_bit(domain->iommu_bmp, g_num_of_iommus);
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ int i, found = 0;
- domain->iommu_coherency = i < g_num_of_iommus ? 1 : 0;
+ domain->iommu_coherency = 1;
for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) {
+ found = 1;
if (!ecap_coherent(g_iommus[i]->ecap)) {
domain->iommu_coherency = 0;
break;
}
}
+ if (found)
+ return;
+
+ /* No hardware attached; use lowest common denominator */
+ rcu_read_lock();
+ for_each_active_iommu(iommu, drhd) {
+ if (!ecap_coherent(iommu->ecap)) {
+ domain->iommu_coherency = 0;
+ break;
+ }
+ }
+ rcu_read_unlock();
}
static void domain_update_iommu_snooping(struct dmar_domain *domain)
@@ -606,12 +644,15 @@ static void domain_update_iommu_superpage(struct dmar_domain *domain)
}
/* set iommu_superpage to the smallest common denominator */
+ rcu_read_lock();
for_each_active_iommu(iommu, drhd) {
mask &= cap_super_page_val(iommu->cap);
if (!mask) {
break;
}
}
+ rcu_read_unlock();
+
domain->iommu_superpage = fls(mask);
}
@@ -623,32 +664,56 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
domain_update_iommu_superpage(domain);
}
-static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn)
+static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
{
struct dmar_drhd_unit *drhd = NULL;
+ struct intel_iommu *iommu;
+ struct device *tmp;
+ struct pci_dev *ptmp, *pdev = NULL;
+ u16 segment;
int i;
- for_each_active_drhd_unit(drhd) {
- if (segment != drhd->segment)
+ if (dev_is_pci(dev)) {
+ pdev = to_pci_dev(dev);
+ segment = pci_domain_nr(pdev->bus);
+ } else if (ACPI_COMPANION(dev))
+ dev = &ACPI_COMPANION(dev)->dev;
+
+ rcu_read_lock();
+ for_each_active_iommu(iommu, drhd) {
+ if (pdev && segment != drhd->segment)
continue;
- for (i = 0; i < drhd->devices_cnt; i++) {
- if (drhd->devices[i] &&
- drhd->devices[i]->bus->number == bus &&
- drhd->devices[i]->devfn == devfn)
- return drhd->iommu;
- if (drhd->devices[i] &&
- drhd->devices[i]->subordinate &&
- drhd->devices[i]->subordinate->number <= bus &&
- drhd->devices[i]->subordinate->busn_res.end >= bus)
- return drhd->iommu;
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, tmp) {
+ if (tmp == dev) {
+ *bus = drhd->devices[i].bus;
+ *devfn = drhd->devices[i].devfn;
+ goto out;
+ }
+
+ if (!pdev || !dev_is_pci(tmp))
+ continue;
+
+ ptmp = to_pci_dev(tmp);
+ if (ptmp->subordinate &&
+ ptmp->subordinate->number <= pdev->bus->number &&
+ ptmp->subordinate->busn_res.end >= pdev->bus->number)
+ goto got_pdev;
}
- if (drhd->include_all)
- return drhd->iommu;
+ if (pdev && drhd->include_all) {
+ got_pdev:
+ *bus = pdev->bus->number;
+ *devfn = pdev->devfn;
+ goto out;
+ }
}
+ iommu = NULL;
+ out:
+ rcu_read_unlock();
- return NULL;
+ return iommu;
}
static void domain_flush_cache(struct dmar_domain *domain,
@@ -748,7 +813,7 @@ out:
}
static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
- unsigned long pfn, int target_level)
+ unsigned long pfn, int *target_level)
{
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
struct dma_pte *parent, *pte = NULL;
@@ -763,14 +828,14 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
parent = domain->pgd;
- while (level > 0) {
+ while (1) {
void *tmp_page;
offset = pfn_level_offset(pfn, level);
pte = &parent[offset];
- if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
+ if (!*target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
break;
- if (level == target_level)
+ if (level == *target_level)
break;
if (!dma_pte_present(pte)) {
@@ -791,10 +856,16 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
domain_flush_cache(domain, pte, sizeof(*pte));
}
}
+ if (level == 1)
+ break;
+
parent = phys_to_virt(dma_pte_addr(pte));
level--;
}
+ if (!*target_level)
+ *target_level = level;
+
return pte;
}
@@ -832,7 +903,7 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain,
}
/* clear last level pte, a tlb flush should be followed */
-static int dma_pte_clear_range(struct dmar_domain *domain,
+static void dma_pte_clear_range(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
@@ -862,8 +933,6 @@ static int dma_pte_clear_range(struct dmar_domain *domain,
(void *)pte - (void *)first_pte);
} while (start_pfn && start_pfn <= last_pfn);
-
- return min_t(int, (large_page - 1) * 9, MAX_AGAW_PFN_WIDTH);
}
static void dma_pte_free_level(struct dmar_domain *domain, int level,
@@ -921,6 +990,123 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
}
}
+/* When a page at a given level is being unlinked from its parent, we don't
+ need to *modify* it at all. All we need to do is make a list of all the
+ pages which can be freed just as soon as we've flushed the IOTLB and we
+ know the hardware page-walk will no longer touch them.
+ The 'pte' argument is the *parent* PTE, pointing to the page that is to
+ be freed. */
+static struct page *dma_pte_list_pagetables(struct dmar_domain *domain,
+ int level, struct dma_pte *pte,
+ struct page *freelist)
+{
+ struct page *pg;
+
+ pg = pfn_to_page(dma_pte_addr(pte) >> PAGE_SHIFT);
+ pg->freelist = freelist;
+ freelist = pg;
+
+ if (level == 1)
+ return freelist;
+
+ for (pte = page_address(pg); !first_pte_in_page(pte); pte++) {
+ if (dma_pte_present(pte) && !dma_pte_superpage(pte))
+ freelist = dma_pte_list_pagetables(domain, level - 1,
+ pte, freelist);
+ }
+
+ return freelist;
+}
+
+static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
+ struct dma_pte *pte, unsigned long pfn,
+ unsigned long start_pfn,
+ unsigned long last_pfn,
+ struct page *freelist)
+{
+ struct dma_pte *first_pte = NULL, *last_pte = NULL;
+
+ pfn = max(start_pfn, pfn);
+ pte = &pte[pfn_level_offset(pfn, level)];
+
+ do {
+ unsigned long level_pfn;
+
+ if (!dma_pte_present(pte))
+ goto next;
+
+ level_pfn = pfn & level_mask(level);
+
+ /* If range covers entire pagetable, free it */
+ if (start_pfn <= level_pfn &&
+ last_pfn >= level_pfn + level_size(level) - 1) {
+ /* These suborbinate page tables are going away entirely. Don't
+ bother to clear them; we're just going to *free* them. */
+ if (level > 1 && !dma_pte_superpage(pte))
+ freelist = dma_pte_list_pagetables(domain, level - 1, pte, freelist);
+
+ dma_clear_pte(pte);
+ if (!first_pte)
+ first_pte = pte;
+ last_pte = pte;
+ } else if (level > 1) {
+ /* Recurse down into a level that isn't *entirely* obsolete */
+ freelist = dma_pte_clear_level(domain, level - 1,
+ phys_to_virt(dma_pte_addr(pte)),
+ level_pfn, start_pfn, last_pfn,
+ freelist);
+ }
+next:
+ pfn += level_size(level);
+ } while (!first_pte_in_page(++pte) && pfn <= last_pfn);
+
+ if (first_pte)
+ domain_flush_cache(domain, first_pte,
+ (void *)++last_pte - (void *)first_pte);
+
+ return freelist;
+}
+
+/* We can't just free the pages because the IOMMU may still be walking
+ the page tables, and may have cached the intermediate levels. The
+ pages can only be freed after the IOTLB flush has been done. */
+struct page *domain_unmap(struct dmar_domain *domain,
+ unsigned long start_pfn,
+ unsigned long last_pfn)
+{
+ int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
+ struct page *freelist = NULL;
+
+ BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
+ BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
+ BUG_ON(start_pfn > last_pfn);
+
+ /* we don't need lock here; nobody else touches the iova range */
+ freelist = dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
+ domain->pgd, 0, start_pfn, last_pfn, NULL);
+
+ /* free pgd */
+ if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
+ struct page *pgd_page = virt_to_page(domain->pgd);
+ pgd_page->freelist = freelist;
+ freelist = pgd_page;
+
+ domain->pgd = NULL;
+ }
+
+ return freelist;
+}
+
+void dma_free_pagelist(struct page *freelist)
+{
+ struct page *pg;
+
+ while ((pg = freelist)) {
+ freelist = pg->freelist;
+ free_pgtable_page(page_address(pg));
+ }
+}
+
/* iommu handling */
static int iommu_alloc_root_entry(struct intel_iommu *iommu)
{
@@ -1030,7 +1216,7 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
break;
case DMA_TLB_PSI_FLUSH:
val = DMA_TLB_PSI_FLUSH|DMA_TLB_IVT|DMA_TLB_DID(did);
- /* Note: always flush non-leaf currently */
+ /* IH bit is passed in as part of address */
val_iva = size_order | addr;
break;
default:
@@ -1069,13 +1255,14 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
(unsigned long long)DMA_TLB_IAIG(val));
}
-static struct device_domain_info *iommu_support_dev_iotlb(
- struct dmar_domain *domain, int segment, u8 bus, u8 devfn)
+static struct device_domain_info *
+iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu,
+ u8 bus, u8 devfn)
{
int found = 0;
unsigned long flags;
struct device_domain_info *info;
- struct intel_iommu *iommu = device_to_iommu(segment, bus, devfn);
+ struct pci_dev *pdev;
if (!ecap_dev_iotlb_support(iommu->ecap))
return NULL;
@@ -1091,34 +1278,35 @@ static struct device_domain_info *iommu_support_dev_iotlb(
}
spin_unlock_irqrestore(&device_domain_lock, flags);
- if (!found || !info->dev)
+ if (!found || !info->dev || !dev_is_pci(info->dev))
return NULL;
- if (!pci_find_ext_capability(info->dev, PCI_EXT_CAP_ID_ATS))
- return NULL;
+ pdev = to_pci_dev(info->dev);
- if (!dmar_find_matched_atsr_unit(info->dev))
+ if (!pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS))
return NULL;
- info->iommu = iommu;
+ if (!dmar_find_matched_atsr_unit(pdev))
+ return NULL;
return info;
}
static void iommu_enable_dev_iotlb(struct device_domain_info *info)
{
- if (!info)
+ if (!info || !dev_is_pci(info->dev))
return;
- pci_enable_ats(info->dev, VTD_PAGE_SHIFT);
+ pci_enable_ats(to_pci_dev(info->dev), VTD_PAGE_SHIFT);
}
static void iommu_disable_dev_iotlb(struct device_domain_info *info)
{
- if (!info->dev || !pci_ats_enabled(info->dev))
+ if (!info->dev || !dev_is_pci(info->dev) ||
+ !pci_ats_enabled(to_pci_dev(info->dev)))
return;
- pci_disable_ats(info->dev);
+ pci_disable_ats(to_pci_dev(info->dev));
}
static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
@@ -1130,24 +1318,31 @@ static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry(info, &domain->devices, link) {
- if (!info->dev || !pci_ats_enabled(info->dev))
+ struct pci_dev *pdev;
+ if (!info->dev || !dev_is_pci(info->dev))
+ continue;
+
+ pdev = to_pci_dev(info->dev);
+ if (!pci_ats_enabled(pdev))
continue;
sid = info->bus << 8 | info->devfn;
- qdep = pci_ats_queue_depth(info->dev);
+ qdep = pci_ats_queue_depth(pdev);
qi_flush_dev_iotlb(info->iommu, sid, qdep, addr, mask);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
}
static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did,
- unsigned long pfn, unsigned int pages, int map)
+ unsigned long pfn, unsigned int pages, int ih, int map)
{
unsigned int mask = ilog2(__roundup_pow_of_two(pages));
uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT;
BUG_ON(pages == 0);
+ if (ih)
+ ih = 1 << 6;
/*
* Fallback to domain selective flush if no PSI support or the size is
* too big.
@@ -1158,7 +1353,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did,
iommu->flush.flush_iotlb(iommu, did, 0, 0,
DMA_TLB_DSI_FLUSH);
else
- iommu->flush.flush_iotlb(iommu, did, addr, mask,
+ iommu->flush.flush_iotlb(iommu, did, addr | ih, mask,
DMA_TLB_PSI_FLUSH);
/*
@@ -1261,10 +1456,6 @@ static int iommu_init_domains(struct intel_iommu *iommu)
return 0;
}
-
-static void domain_exit(struct dmar_domain *domain);
-static void vm_domain_exit(struct dmar_domain *domain);
-
static void free_dmar_iommu(struct intel_iommu *iommu)
{
struct dmar_domain *domain;
@@ -1273,18 +1464,21 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
if ((iommu->domains) && (iommu->domain_ids)) {
for_each_set_bit(i, iommu->domain_ids, cap_ndoms(iommu->cap)) {
+ /*
+ * Domain id 0 is reserved for invalid translation
+ * if hardware supports caching mode.
+ */
+ if (cap_caching_mode(iommu->cap) && i == 0)
+ continue;
+
domain = iommu->domains[i];
clear_bit(i, iommu->domain_ids);
spin_lock_irqsave(&domain->iommu_lock, flags);
count = --domain->iommu_count;
spin_unlock_irqrestore(&domain->iommu_lock, flags);
- if (count == 0) {
- if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE)
- vm_domain_exit(domain);
- else
- domain_exit(domain);
- }
+ if (count == 0)
+ domain_exit(domain);
}
}
@@ -1298,21 +1492,14 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
g_iommus[iommu->seq_id] = NULL;
- /* if all iommus are freed, free g_iommus */
- for (i = 0; i < g_num_of_iommus; i++) {
- if (g_iommus[i])
- break;
- }
-
- if (i == g_num_of_iommus)
- kfree(g_iommus);
-
/* free context mapping */
free_context_table(iommu);
}
-static struct dmar_domain *alloc_domain(void)
+static struct dmar_domain *alloc_domain(bool vm)
{
+ /* domain id for virtual machine, it won't be set in context */
+ static atomic_t vm_domid = ATOMIC_INIT(0);
struct dmar_domain *domain;
domain = alloc_domain_mem();
@@ -1320,8 +1507,15 @@ static struct dmar_domain *alloc_domain(void)
return NULL;
domain->nid = -1;
+ domain->iommu_count = 0;
memset(domain->iommu_bmp, 0, sizeof(domain->iommu_bmp));
domain->flags = 0;
+ spin_lock_init(&domain->iommu_lock);
+ INIT_LIST_HEAD(&domain->devices);
+ if (vm) {
+ domain->id = atomic_inc_return(&vm_domid);
+ domain->flags = DOMAIN_FLAG_VIRTUAL_MACHINE;
+ }
return domain;
}
@@ -1345,6 +1539,7 @@ static int iommu_attach_domain(struct dmar_domain *domain,
}
domain->id = num;
+ domain->iommu_count++;
set_bit(num, iommu->domain_ids);
set_bit(iommu->seq_id, domain->iommu_bmp);
iommu->domains[num] = domain;
@@ -1358,22 +1553,16 @@ static void iommu_detach_domain(struct dmar_domain *domain,
{
unsigned long flags;
int num, ndomains;
- int found = 0;
spin_lock_irqsave(&iommu->lock, flags);
ndomains = cap_ndoms(iommu->cap);
for_each_set_bit(num, iommu->domain_ids, ndomains) {
if (iommu->domains[num] == domain) {
- found = 1;
+ clear_bit(num, iommu->domain_ids);
+ iommu->domains[num] = NULL;
break;
}
}
-
- if (found) {
- clear_bit(num, iommu->domain_ids);
- clear_bit(iommu->seq_id, domain->iommu_bmp);
- iommu->domains[num] = NULL;
- }
spin_unlock_irqrestore(&iommu->lock, flags);
}
@@ -1445,8 +1634,6 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
unsigned long sagaw;
init_iova_domain(&domain->iovad, DMA_32BIT_PFN);
- spin_lock_init(&domain->iommu_lock);
-
domain_reserve_special_ranges(domain);
/* calculate AGAW */
@@ -1465,7 +1652,6 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
return -ENODEV;
}
domain->agaw = agaw;
- INIT_LIST_HEAD(&domain->devices);
if (ecap_coherent(iommu->ecap))
domain->iommu_coherency = 1;
@@ -1477,8 +1663,11 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
else
domain->iommu_snooping = 0;
- domain->iommu_superpage = fls(cap_super_page_val(iommu->cap));
- domain->iommu_count = 1;
+ if (intel_iommu_superpage)
+ domain->iommu_superpage = fls(cap_super_page_val(iommu->cap));
+ else
+ domain->iommu_superpage = 0;
+
domain->nid = iommu->node;
/* always allocate the top pgd */
@@ -1493,6 +1682,7 @@ static void domain_exit(struct dmar_domain *domain)
{
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
+ struct page *freelist = NULL;
/* Domain 0 is reserved, so dont process it */
if (!domain)
@@ -1502,29 +1692,33 @@ static void domain_exit(struct dmar_domain *domain)
if (!intel_iommu_strict)
flush_unmaps_timeout(0);
+ /* remove associated devices */
domain_remove_dev_info(domain);
+
/* destroy iovas */
put_iova_domain(&domain->iovad);
- /* clear ptes */
- dma_pte_clear_range(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
-
- /* free page tables */
- dma_pte_free_pagetable(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
+ freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
+ /* clear attached or cached domains */
+ rcu_read_lock();
for_each_active_iommu(iommu, drhd)
- if (test_bit(iommu->seq_id, domain->iommu_bmp))
+ if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
+ test_bit(iommu->seq_id, domain->iommu_bmp))
iommu_detach_domain(domain, iommu);
+ rcu_read_unlock();
+
+ dma_free_pagelist(freelist);
free_domain_mem(domain);
}
-static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
- u8 bus, u8 devfn, int translation)
+static int domain_context_mapping_one(struct dmar_domain *domain,
+ struct intel_iommu *iommu,
+ u8 bus, u8 devfn, int translation)
{
struct context_entry *context;
unsigned long flags;
- struct intel_iommu *iommu;
struct dma_pte *pgd;
unsigned long num;
unsigned long ndomains;
@@ -1539,10 +1733,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
BUG_ON(translation != CONTEXT_TT_PASS_THROUGH &&
translation != CONTEXT_TT_MULTI_LEVEL);
- iommu = device_to_iommu(segment, bus, devfn);
- if (!iommu)
- return -ENODEV;
-
context = device_to_context_entry(iommu, bus, devfn);
if (!context)
return -ENOMEM;
@@ -1600,7 +1790,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
context_set_domain_id(context, id);
if (translation != CONTEXT_TT_PASS_THROUGH) {
- info = iommu_support_dev_iotlb(domain, segment, bus, devfn);
+ info = iommu_support_dev_iotlb(domain, iommu, bus, devfn);
translation = info ? CONTEXT_TT_DEV_IOTLB :
CONTEXT_TT_MULTI_LEVEL;
}
@@ -1650,27 +1840,32 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
}
static int
-domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev,
- int translation)
+domain_context_mapping(struct dmar_domain *domain, struct device *dev,
+ int translation)
{
int ret;
- struct pci_dev *tmp, *parent;
+ struct pci_dev *pdev, *tmp, *parent;
+ struct intel_iommu *iommu;
+ u8 bus, devfn;
+
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
+ return -ENODEV;
- ret = domain_context_mapping_one(domain, pci_domain_nr(pdev->bus),
- pdev->bus->number, pdev->devfn,
+ ret = domain_context_mapping_one(domain, iommu, bus, devfn,
translation);
- if (ret)
+ if (ret || !dev_is_pci(dev))
return ret;
/* dependent device mapping */
+ pdev = to_pci_dev(dev);
tmp = pci_find_upstream_pcie_bridge(pdev);
if (!tmp)
return 0;
/* Secondary interface's bus number and devfn 0 */
parent = pdev->bus->self;
while (parent != tmp) {
- ret = domain_context_mapping_one(domain,
- pci_domain_nr(parent->bus),
+ ret = domain_context_mapping_one(domain, iommu,
parent->bus->number,
parent->devfn, translation);
if (ret)
@@ -1678,33 +1873,33 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev,
parent = parent->bus->self;
}
if (pci_is_pcie(tmp)) /* this is a PCIe-to-PCI bridge */
- return domain_context_mapping_one(domain,
- pci_domain_nr(tmp->subordinate),
+ return domain_context_mapping_one(domain, iommu,
tmp->subordinate->number, 0,
translation);
else /* this is a legacy PCI bridge */
- return domain_context_mapping_one(domain,
- pci_domain_nr(tmp->bus),
+ return domain_context_mapping_one(domain, iommu,
tmp->bus->number,
tmp->devfn,
translation);
}
-static int domain_context_mapped(struct pci_dev *pdev)
+static int domain_context_mapped(struct device *dev)
{
int ret;
- struct pci_dev *tmp, *parent;
+ struct pci_dev *pdev, *tmp, *parent;
struct intel_iommu *iommu;
+ u8 bus, devfn;
- iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
- pdev->devfn);
+ iommu = device_to_iommu(dev, &bus, &devfn);
if (!iommu)
return -ENODEV;
- ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn);
- if (!ret)
+ ret = device_context_mapped(iommu, bus, devfn);
+ if (!ret || !dev_is_pci(dev))
return ret;
+
/* dependent device mapping */
+ pdev = to_pci_dev(dev);
tmp = pci_find_upstream_pcie_bridge(pdev);
if (!tmp)
return ret;
@@ -1800,7 +1995,7 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
if (!pte) {
largepage_lvl = hardware_largepage_caps(domain, iov_pfn, phys_pfn, sg_res);
- first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, largepage_lvl);
+ first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, &largepage_lvl);
if (!pte)
return -ENOMEM;
/* It is large page*/
@@ -1899,14 +2094,13 @@ static inline void unlink_domain_info(struct device_domain_info *info)
list_del(&info->link);
list_del(&info->global);
if (info->dev)
- info->dev->dev.archdata.iommu = NULL;
+ info->dev->archdata.iommu = NULL;
}
static void domain_remove_dev_info(struct dmar_domain *domain)
{
struct device_domain_info *info;
- unsigned long flags;
- struct intel_iommu *iommu;
+ unsigned long flags, flags2;
spin_lock_irqsave(&device_domain_lock, flags);
while (!list_empty(&domain->devices)) {
@@ -1916,10 +2110,23 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
spin_unlock_irqrestore(&device_domain_lock, flags);
iommu_disable_dev_iotlb(info);
- iommu = device_to_iommu(info->segment, info->bus, info->devfn);
- iommu_detach_dev(iommu, info->bus, info->devfn);
- free_devinfo_mem(info);
+ iommu_detach_dev(info->iommu, info->bus, info->devfn);
+ if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) {
+ iommu_detach_dependent_devices(info->iommu, info->dev);
+ /* clear this iommu in iommu_bmp, update iommu count
+ * and capabilities
+ */
+ spin_lock_irqsave(&domain->iommu_lock, flags2);
+ if (test_and_clear_bit(info->iommu->seq_id,
+ domain->iommu_bmp)) {
+ domain->iommu_count--;
+ domain_update_iommu_cap(domain);
+ }
+ spin_unlock_irqrestore(&domain->iommu_lock, flags2);
+ }
+
+ free_devinfo_mem(info);
spin_lock_irqsave(&device_domain_lock, flags);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
@@ -1927,155 +2134,151 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
/*
* find_domain
- * Note: we use struct pci_dev->dev.archdata.iommu stores the info
+ * Note: we use struct device->archdata.iommu stores the info
*/
-static struct dmar_domain *
-find_domain(struct pci_dev *pdev)
+static struct dmar_domain *find_domain(struct device *dev)
{
struct device_domain_info *info;
/* No lock here, assumes no domain exit in normal case */
- info = pdev->dev.archdata.iommu;
+ info = dev->archdata.iommu;
if (info)
return info->domain;
return NULL;
}
+static inline struct device_domain_info *
+dmar_search_domain_by_dev_info(int segment, int bus, int devfn)
+{
+ struct device_domain_info *info;
+
+ list_for_each_entry(info, &device_domain_list, global)
+ if (info->iommu->segment == segment && info->bus == bus &&
+ info->devfn == devfn)
+ return info;
+
+ return NULL;
+}
+
+static struct dmar_domain *dmar_insert_dev_info(struct intel_iommu *iommu,
+ int bus, int devfn,
+ struct device *dev,
+ struct dmar_domain *domain)
+{
+ struct dmar_domain *found = NULL;
+ struct device_domain_info *info;
+ unsigned long flags;
+
+ info = alloc_devinfo_mem();
+ if (!info)
+ return NULL;
+
+ info->bus = bus;
+ info->devfn = devfn;
+ info->dev = dev;
+ info->domain = domain;
+ info->iommu = iommu;
+ if (!dev)
+ domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES;
+
+ spin_lock_irqsave(&device_domain_lock, flags);
+ if (dev)
+ found = find_domain(dev);
+ else {
+ struct device_domain_info *info2;
+ info2 = dmar_search_domain_by_dev_info(iommu->segment, bus, devfn);
+ if (info2)
+ found = info2->domain;
+ }
+ if (found) {
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+ free_devinfo_mem(info);
+ /* Caller must free the original domain */
+ return found;
+ }
+
+ list_add(&info->link, &domain->devices);
+ list_add(&info->global, &device_domain_list);
+ if (dev)
+ dev->archdata.iommu = info;
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+
+ return domain;
+}
+
/* domain is initialized */
-static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
+static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw)
{
- struct dmar_domain *domain, *found = NULL;
- struct intel_iommu *iommu;
- struct dmar_drhd_unit *drhd;
- struct device_domain_info *info, *tmp;
- struct pci_dev *dev_tmp;
+ struct dmar_domain *domain, *free = NULL;
+ struct intel_iommu *iommu = NULL;
+ struct device_domain_info *info;
+ struct pci_dev *dev_tmp = NULL;
unsigned long flags;
- int bus = 0, devfn = 0;
- int segment;
- int ret;
+ u8 bus, devfn, bridge_bus, bridge_devfn;
- domain = find_domain(pdev);
+ domain = find_domain(dev);
if (domain)
return domain;
- segment = pci_domain_nr(pdev->bus);
+ if (dev_is_pci(dev)) {
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 segment;
- dev_tmp = pci_find_upstream_pcie_bridge(pdev);
- if (dev_tmp) {
- if (pci_is_pcie(dev_tmp)) {
- bus = dev_tmp->subordinate->number;
- devfn = 0;
- } else {
- bus = dev_tmp->bus->number;
- devfn = dev_tmp->devfn;
- }
- spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(info, &device_domain_list, global) {
- if (info->segment == segment &&
- info->bus == bus && info->devfn == devfn) {
- found = info->domain;
- break;
+ segment = pci_domain_nr(pdev->bus);
+ dev_tmp = pci_find_upstream_pcie_bridge(pdev);
+ if (dev_tmp) {
+ if (pci_is_pcie(dev_tmp)) {
+ bridge_bus = dev_tmp->subordinate->number;
+ bridge_devfn = 0;
+ } else {
+ bridge_bus = dev_tmp->bus->number;
+ bridge_devfn = dev_tmp->devfn;
}
- }
- spin_unlock_irqrestore(&device_domain_lock, flags);
- /* pcie-pci bridge already has a domain, uses it */
- if (found) {
- domain = found;
- goto found_domain;
+ spin_lock_irqsave(&device_domain_lock, flags);
+ info = dmar_search_domain_by_dev_info(segment, bus, devfn);
+ if (info) {
+ iommu = info->iommu;
+ domain = info->domain;
+ }
+ spin_unlock_irqrestore(&device_domain_lock, flags);
+ /* pcie-pci bridge already has a domain, uses it */
+ if (info)
+ goto found_domain;
}
}
- domain = alloc_domain();
- if (!domain)
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
goto error;
- /* Allocate new domain for the device */
- drhd = dmar_find_matched_drhd_unit(pdev);
- if (!drhd) {
- printk(KERN_ERR "IOMMU: can't find DMAR for device %s\n",
- pci_name(pdev));
- free_domain_mem(domain);
- return NULL;
- }
- iommu = drhd->iommu;
-
- ret = iommu_attach_domain(domain, iommu);
- if (ret) {
+ /* Allocate and initialize new domain for the device */
+ domain = alloc_domain(false);
+ if (!domain)
+ goto error;
+ if (iommu_attach_domain(domain, iommu)) {
free_domain_mem(domain);
+ domain = NULL;
goto error;
}
-
- if (domain_init(domain, gaw)) {
- domain_exit(domain);
+ free = domain;
+ if (domain_init(domain, gaw))
goto error;
- }
/* register pcie-to-pci device */
if (dev_tmp) {
- info = alloc_devinfo_mem();
- if (!info) {
- domain_exit(domain);
+ domain = dmar_insert_dev_info(iommu, bridge_bus, bridge_devfn,
+ NULL, domain);
+ if (!domain)
goto error;
- }
- info->segment = segment;
- info->bus = bus;
- info->devfn = devfn;
- info->dev = NULL;
- info->domain = domain;
- /* This domain is shared by devices under p2p bridge */
- domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES;
-
- /* pcie-to-pci bridge already has a domain, uses it */
- found = NULL;
- spin_lock_irqsave(&device_domain_lock, flags);
- list_for_each_entry(tmp, &device_domain_list, global) {
- if (tmp->segment == segment &&
- tmp->bus == bus && tmp->devfn == devfn) {
- found = tmp->domain;
- break;
- }
- }
- if (found) {
- spin_unlock_irqrestore(&device_domain_lock, flags);
- free_devinfo_mem(info);
- domain_exit(domain);
- domain = found;
- } else {
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- spin_unlock_irqrestore(&device_domain_lock, flags);
- }
}
found_domain:
- info = alloc_devinfo_mem();
- if (!info)
- goto error;
- info->segment = segment;
- info->bus = pdev->bus->number;
- info->devfn = pdev->devfn;
- info->dev = pdev;
- info->domain = domain;
- spin_lock_irqsave(&device_domain_lock, flags);
- /* somebody is fast */
- found = find_domain(pdev);
- if (found != NULL) {
- spin_unlock_irqrestore(&device_domain_lock, flags);
- if (found != domain) {
- domain_exit(domain);
- domain = found;
- }
- free_devinfo_mem(info);
- return domain;
- }
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- pdev->dev.archdata.iommu = info;
- spin_unlock_irqrestore(&device_domain_lock, flags);
- return domain;
+ domain = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
error:
- /* recheck it here, maybe others set it */
- return find_domain(pdev);
+ if (free != domain)
+ domain_exit(free);
+
+ return domain;
}
static int iommu_identity_mapping;
@@ -2109,14 +2312,14 @@ static int iommu_domain_identity_map(struct dmar_domain *domain,
DMA_PTE_READ|DMA_PTE_WRITE);
}
-static int iommu_prepare_identity_map(struct pci_dev *pdev,
+static int iommu_prepare_identity_map(struct device *dev,
unsigned long long start,
unsigned long long end)
{
struct dmar_domain *domain;
int ret;
- domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
+ domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
if (!domain)
return -ENOMEM;
@@ -2126,13 +2329,13 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
up to start with in si_domain */
if (domain == si_domain && hw_pass_through) {
printk("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
- pci_name(pdev), start, end);
+ dev_name(dev), start, end);
return 0;
}
printk(KERN_INFO
"IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
- pci_name(pdev), start, end);
+ dev_name(dev), start, end);
if (end < start) {
WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n"
@@ -2160,7 +2363,7 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
goto error;
/* context entry init */
- ret = domain_context_mapping(domain, pdev, CONTEXT_TT_MULTI_LEVEL);
+ ret = domain_context_mapping(domain, dev, CONTEXT_TT_MULTI_LEVEL);
if (ret)
goto error;
@@ -2172,12 +2375,12 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev,
}
static inline int iommu_prepare_rmrr_dev(struct dmar_rmrr_unit *rmrr,
- struct pci_dev *pdev)
+ struct device *dev)
{
- if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
+ if (dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO)
return 0;
- return iommu_prepare_identity_map(pdev, rmrr->base_address,
- rmrr->end_address);
+ return iommu_prepare_identity_map(dev, rmrr->base_address,
+ rmrr->end_address);
}
#ifdef CONFIG_INTEL_IOMMU_FLOPPY_WA
@@ -2191,7 +2394,7 @@ static inline void iommu_prepare_isa(void)
return;
printk(KERN_INFO "IOMMU: Prepare 0-16MiB unity mapping for LPC\n");
- ret = iommu_prepare_identity_map(pdev, 0, 16*1024*1024 - 1);
+ ret = iommu_prepare_identity_map(&pdev->dev, 0, 16*1024*1024 - 1);
if (ret)
printk(KERN_ERR "IOMMU: Failed to create 0-16MiB identity map; "
@@ -2213,10 +2416,12 @@ static int __init si_domain_init(int hw)
struct intel_iommu *iommu;
int nid, ret = 0;
- si_domain = alloc_domain();
+ si_domain = alloc_domain(false);
if (!si_domain)
return -EFAULT;
+ si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY;
+
for_each_active_iommu(iommu, drhd) {
ret = iommu_attach_domain(si_domain, iommu);
if (ret) {
@@ -2230,7 +2435,6 @@ static int __init si_domain_init(int hw)
return -EFAULT;
}
- si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY;
pr_debug("IOMMU: identity mapping domain is domain %d\n",
si_domain->id);
@@ -2252,16 +2456,14 @@ static int __init si_domain_init(int hw)
return 0;
}
-static void domain_remove_one_dev_info(struct dmar_domain *domain,
- struct pci_dev *pdev);
-static int identity_mapping(struct pci_dev *pdev)
+static int identity_mapping(struct device *dev)
{
struct device_domain_info *info;
if (likely(!iommu_identity_mapping))
return 0;
- info = pdev->dev.archdata.iommu;
+ info = dev->archdata.iommu;
if (info && info != DUMMY_DEVICE_DOMAIN_INFO)
return (info->domain == si_domain);
@@ -2269,111 +2471,112 @@ static int identity_mapping(struct pci_dev *pdev)
}
static int domain_add_dev_info(struct dmar_domain *domain,
- struct pci_dev *pdev,
- int translation)
+ struct device *dev, int translation)
{
- struct device_domain_info *info;
- unsigned long flags;
+ struct dmar_domain *ndomain;
+ struct intel_iommu *iommu;
+ u8 bus, devfn;
int ret;
- info = alloc_devinfo_mem();
- if (!info)
- return -ENOMEM;
-
- info->segment = pci_domain_nr(pdev->bus);
- info->bus = pdev->bus->number;
- info->devfn = pdev->devfn;
- info->dev = pdev;
- info->domain = domain;
+ iommu = device_to_iommu(dev, &bus, &devfn);
+ if (!iommu)
+ return -ENODEV;
- spin_lock_irqsave(&device_domain_lock, flags);
- list_add(&info->link, &domain->devices);
- list_add(&info->global, &device_domain_list);
- pdev->dev.archdata.iommu = info;
- spin_unlock_irqrestore(&device_domain_lock, flags);
+ ndomain = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
+ if (ndomain != domain)
+ return -EBUSY;
- ret = domain_context_mapping(domain, pdev, translation);
+ ret = domain_context_mapping(domain, dev, translation);
if (ret) {
- spin_lock_irqsave(&device_domain_lock, flags);
- unlink_domain_info(info);
- spin_unlock_irqrestore(&device_domain_lock, flags);
- free_devinfo_mem(info);
+ domain_remove_one_dev_info(domain, dev);
return ret;
}
return 0;
}
-static bool device_has_rmrr(struct pci_dev *dev)
+static bool device_has_rmrr(struct device *dev)
{
struct dmar_rmrr_unit *rmrr;
+ struct device *tmp;
int i;
+ rcu_read_lock();
for_each_rmrr_units(rmrr) {
- for (i = 0; i < rmrr->devices_cnt; i++) {
- /*
- * Return TRUE if this RMRR contains the device that
- * is passed in.
- */
- if (rmrr->devices[i] == dev)
+ /*
+ * Return TRUE if this RMRR contains the device that
+ * is passed in.
+ */
+ for_each_active_dev_scope(rmrr->devices,
+ rmrr->devices_cnt, i, tmp)
+ if (tmp == dev) {
+ rcu_read_unlock();
return true;
- }
+ }
}
+ rcu_read_unlock();
return false;
}
-static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
+static int iommu_should_identity_map(struct device *dev, int startup)
{
- /*
- * We want to prevent any device associated with an RMRR from
- * getting placed into the SI Domain. This is done because
- * problems exist when devices are moved in and out of domains
- * and their respective RMRR info is lost. We exempt USB devices
- * from this process due to their usage of RMRRs that are known
- * to not be needed after BIOS hand-off to OS.
- */
- if (device_has_rmrr(pdev) &&
- (pdev->class >> 8) != PCI_CLASS_SERIAL_USB)
- return 0;
+ if (dev_is_pci(dev)) {
+ struct pci_dev *pdev = to_pci_dev(dev);
- if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
- return 1;
+ /*
+ * We want to prevent any device associated with an RMRR from
+ * getting placed into the SI Domain. This is done because
+ * problems exist when devices are moved in and out of domains
+ * and their respective RMRR info is lost. We exempt USB devices
+ * from this process due to their usage of RMRRs that are known
+ * to not be needed after BIOS hand-off to OS.
+ */
+ if (device_has_rmrr(dev) &&
+ (pdev->class >> 8) != PCI_CLASS_SERIAL_USB)
+ return 0;
- if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
- return 1;
+ if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
+ return 1;
- if (!(iommu_identity_mapping & IDENTMAP_ALL))
- return 0;
+ if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
+ return 1;
- /*
- * We want to start off with all devices in the 1:1 domain, and
- * take them out later if we find they can't access all of memory.
- *
- * However, we can't do this for PCI devices behind bridges,
- * because all PCI devices behind the same bridge will end up
- * with the same source-id on their transactions.
- *
- * Practically speaking, we can't change things around for these
- * devices at run-time, because we can't be sure there'll be no
- * DMA transactions in flight for any of their siblings.
- *
- * So PCI devices (unless they're on the root bus) as well as
- * their parent PCI-PCI or PCIe-PCI bridges must be left _out_ of
- * the 1:1 domain, just in _case_ one of their siblings turns out
- * not to be able to map all of memory.
- */
- if (!pci_is_pcie(pdev)) {
- if (!pci_is_root_bus(pdev->bus))
+ if (!(iommu_identity_mapping & IDENTMAP_ALL))
return 0;
- if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
+
+ /*
+ * We want to start off with all devices in the 1:1 domain, and
+ * take them out later if we find they can't access all of memory.
+ *
+ * However, we can't do this for PCI devices behind bridges,
+ * because all PCI devices behind the same bridge will end up
+ * with the same source-id on their transactions.
+ *
+ * Practically speaking, we can't change things around for these
+ * devices at run-time, because we can't be sure there'll be no
+ * DMA transactions in flight for any of their siblings.
+ *
+ * So PCI devices (unless they're on the root bus) as well as
+ * their parent PCI-PCI or PCIe-PCI bridges must be left _out_ of
+ * the 1:1 domain, just in _case_ one of their siblings turns out
+ * not to be able to map all of memory.
+ */
+ if (!pci_is_pcie(pdev)) {
+ if (!pci_is_root_bus(pdev->bus))
+ return 0;
+ if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
+ return 0;
+ } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
- } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_PCI_BRIDGE)
- return 0;
+ } else {
+ if (device_has_rmrr(dev))
+ return 0;
+ }
- /*
+ /*
* At boot time, we don't yet know if devices will be 64-bit capable.
- * Assume that they will -- if they turn out not to be, then we can
+ * Assume that they will — if they turn out not to be, then we can
* take them out of the 1:1 domain later.
*/
if (!startup) {
@@ -2381,42 +2584,77 @@ static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
* If the device's dma_mask is less than the system's memory
* size then this is not a candidate for identity mapping.
*/
- u64 dma_mask = pdev->dma_mask;
+ u64 dma_mask = *dev->dma_mask;
- if (pdev->dev.coherent_dma_mask &&
- pdev->dev.coherent_dma_mask < dma_mask)
- dma_mask = pdev->dev.coherent_dma_mask;
+ if (dev->coherent_dma_mask &&
+ dev->coherent_dma_mask < dma_mask)
+ dma_mask = dev->coherent_dma_mask;
- return dma_mask >= dma_get_required_mask(&pdev->dev);
+ return dma_mask >= dma_get_required_mask(dev);
}
return 1;
}
+static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw)
+{
+ int ret;
+
+ if (!iommu_should_identity_map(dev, 1))
+ return 0;
+
+ ret = domain_add_dev_info(si_domain, dev,
+ hw ? CONTEXT_TT_PASS_THROUGH :
+ CONTEXT_TT_MULTI_LEVEL);
+ if (!ret)
+ pr_info("IOMMU: %s identity mapping for device %s\n",
+ hw ? "hardware" : "software", dev_name(dev));
+ else if (ret == -ENODEV)
+ /* device not associated with an iommu */
+ ret = 0;
+
+ return ret;
+}
+
+
static int __init iommu_prepare_static_identity_mapping(int hw)
{
struct pci_dev *pdev = NULL;
- int ret;
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ struct device *dev;
+ int i;
+ int ret = 0;
ret = si_domain_init(hw);
if (ret)
return -EFAULT;
for_each_pci_dev(pdev) {
- if (iommu_should_identity_map(pdev, 1)) {
- ret = domain_add_dev_info(si_domain, pdev,
- hw ? CONTEXT_TT_PASS_THROUGH :
- CONTEXT_TT_MULTI_LEVEL);
- if (ret) {
- /* device not associated with an iommu */
- if (ret == -ENODEV)
- continue;
- return ret;
+ ret = dev_prepare_static_identity_mapping(&pdev->dev, hw);
+ if (ret)
+ return ret;
+ }
+
+ for_each_active_iommu(iommu, drhd)
+ for_each_active_dev_scope(drhd->devices, drhd->devices_cnt, i, dev) {
+ struct acpi_device_physical_node *pn;
+ struct acpi_device *adev;
+
+ if (dev->bus != &acpi_bus_type)
+ continue;
+
+ adev= to_acpi_device(dev);
+ mutex_lock(&adev->physical_node_lock);
+ list_for_each_entry(pn, &adev->physical_node_list, node) {
+ ret = dev_prepare_static_identity_mapping(pn->dev, hw);
+ if (ret)
+ break;
}
- pr_info("IOMMU: %s identity mapping for device %s\n",
- hw ? "hardware" : "software", pci_name(pdev));
+ mutex_unlock(&adev->physical_node_lock);
+ if (ret)
+ return ret;
}
- }
return 0;
}
@@ -2425,7 +2663,7 @@ static int __init init_dmars(void)
{
struct dmar_drhd_unit *drhd;
struct dmar_rmrr_unit *rmrr;
- struct pci_dev *pdev;
+ struct device *dev;
struct intel_iommu *iommu;
int i, ret;
@@ -2461,7 +2699,7 @@ static int __init init_dmars(void)
sizeof(struct deferred_flush_tables), GFP_KERNEL);
if (!deferred_flush) {
ret = -ENOMEM;
- goto error;
+ goto free_g_iommus;
}
for_each_active_iommu(iommu, drhd) {
@@ -2469,7 +2707,7 @@ static int __init init_dmars(void)
ret = iommu_init_domains(iommu);
if (ret)
- goto error;
+ goto free_iommu;
/*
* TBD:
@@ -2479,7 +2717,7 @@ static int __init init_dmars(void)
ret = iommu_alloc_root_entry(iommu);
if (ret) {
printk(KERN_ERR "IOMMU: allocate root entry failed\n");
- goto error;
+ goto free_iommu;
}
if (!ecap_pass_through(iommu->ecap))
hw_pass_through = 0;
@@ -2548,7 +2786,7 @@ static int __init init_dmars(void)
ret = iommu_prepare_static_identity_mapping(hw_pass_through);
if (ret) {
printk(KERN_CRIT "Failed to setup IOMMU pass-through\n");
- goto error;
+ goto free_iommu;
}
}
/*
@@ -2567,15 +2805,10 @@ static int __init init_dmars(void)
*/
printk(KERN_INFO "IOMMU: Setting RMRR:\n");
for_each_rmrr_units(rmrr) {
- for (i = 0; i < rmrr->devices_cnt; i++) {
- pdev = rmrr->devices[i];
- /*
- * some BIOS lists non-exist devices in DMAR
- * table.
- */
- if (!pdev)
- continue;
- ret = iommu_prepare_rmrr_dev(rmrr, pdev);
+ /* some BIOS lists non-exist devices in DMAR table. */
+ for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
+ i, dev) {
+ ret = iommu_prepare_rmrr_dev(rmrr, dev);
if (ret)
printk(KERN_ERR
"IOMMU: mapping reserved region failed\n");
@@ -2606,7 +2839,7 @@ static int __init init_dmars(void)
ret = dmar_set_interrupt(iommu);
if (ret)
- goto error;
+ goto free_iommu;
iommu_set_root_entry(iommu);
@@ -2615,17 +2848,20 @@ static int __init init_dmars(void)
ret = iommu_enable_translation(iommu);
if (ret)
- goto error;
+ goto free_iommu;
iommu_disable_protect_mem_regions(iommu);
}
return 0;
-error:
+
+free_iommu:
for_each_active_iommu(iommu, drhd)
free_dmar_iommu(iommu);
kfree(deferred_flush);
+free_g_iommus:
kfree(g_iommus);
+error:
return ret;
}
@@ -2634,7 +2870,6 @@ static struct iova *intel_alloc_iova(struct device *dev,
struct dmar_domain *domain,
unsigned long nrpages, uint64_t dma_mask)
{
- struct pci_dev *pdev = to_pci_dev(dev);
struct iova *iova = NULL;
/* Restrict dma_mask to the width that the iommu can handle */
@@ -2654,34 +2889,31 @@ static struct iova *intel_alloc_iova(struct device *dev,
iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1);
if (unlikely(!iova)) {
printk(KERN_ERR "Allocating %ld-page iova for %s failed",
- nrpages, pci_name(pdev));
+ nrpages, dev_name(dev));
return NULL;
}
return iova;
}
-static struct dmar_domain *__get_valid_domain_for_dev(struct pci_dev *pdev)
+static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
{
struct dmar_domain *domain;
int ret;
- domain = get_domain_for_dev(pdev,
- DEFAULT_DOMAIN_ADDRESS_WIDTH);
+ domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
if (!domain) {
- printk(KERN_ERR
- "Allocating domain for %s failed", pci_name(pdev));
+ printk(KERN_ERR "Allocating domain for %s failed",
+ dev_name(dev));
return NULL;
}
/* make sure context mapping is ok */
- if (unlikely(!domain_context_mapped(pdev))) {
- ret = domain_context_mapping(domain, pdev,
- CONTEXT_TT_MULTI_LEVEL);
+ if (unlikely(!domain_context_mapped(dev))) {
+ ret = domain_context_mapping(domain, dev, CONTEXT_TT_MULTI_LEVEL);
if (ret) {
- printk(KERN_ERR
- "Domain context map for %s failed",
- pci_name(pdev));
+ printk(KERN_ERR "Domain context map for %s failed",
+ dev_name(dev));
return NULL;
}
}
@@ -2689,51 +2921,46 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct pci_dev *pdev)
return domain;
}
-static inline struct dmar_domain *get_valid_domain_for_dev(struct pci_dev *dev)
+static inline struct dmar_domain *get_valid_domain_for_dev(struct device *dev)
{
struct device_domain_info *info;
/* No lock here, assumes no domain exit in normal case */
- info = dev->dev.archdata.iommu;
+ info = dev->archdata.iommu;
if (likely(info))
return info->domain;
return __get_valid_domain_for_dev(dev);
}
-static int iommu_dummy(struct pci_dev *pdev)
+static int iommu_dummy(struct device *dev)
{
- return pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
+ return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
}
-/* Check if the pdev needs to go through non-identity map and unmap process.*/
+/* Check if the dev needs to go through non-identity map and unmap process.*/
static int iommu_no_mapping(struct device *dev)
{
- struct pci_dev *pdev;
int found;
- if (unlikely(!dev_is_pci(dev)))
- return 1;
-
- pdev = to_pci_dev(dev);
- if (iommu_dummy(pdev))
+ if (iommu_dummy(dev))
return 1;
if (!iommu_identity_mapping)
return 0;
- found = identity_mapping(pdev);
+ found = identity_mapping(dev);
if (found) {
- if (iommu_should_identity_map(pdev, 0))
+ if (iommu_should_identity_map(dev, 0))
return 1;
else {
/*
* 32 bit DMA is removed from si_domain and fall back
* to non-identity mapping.
*/
- domain_remove_one_dev_info(si_domain, pdev);
+ domain_remove_one_dev_info(si_domain, dev);
printk(KERN_INFO "32bit %s uses non-identity mapping\n",
- pci_name(pdev));
+ dev_name(dev));
return 0;
}
} else {
@@ -2741,15 +2968,15 @@ static int iommu_no_mapping(struct device *dev)
* In case of a detached 64 bit DMA device from vm, the device
* is put into si_domain for identity mapping.
*/
- if (iommu_should_identity_map(pdev, 0)) {
+ if (iommu_should_identity_map(dev, 0)) {
int ret;
- ret = domain_add_dev_info(si_domain, pdev,
+ ret = domain_add_dev_info(si_domain, dev,
hw_pass_through ?
CONTEXT_TT_PASS_THROUGH :
CONTEXT_TT_MULTI_LEVEL);
if (!ret) {
printk(KERN_INFO "64bit %s uses identity mapping\n",
- pci_name(pdev));
+ dev_name(dev));
return 1;
}
}
@@ -2758,10 +2985,9 @@ static int iommu_no_mapping(struct device *dev)
return 0;
}
-static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
+static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
size_t size, int dir, u64 dma_mask)
{
- struct pci_dev *pdev = to_pci_dev(hwdev);
struct dmar_domain *domain;
phys_addr_t start_paddr;
struct iova *iova;
@@ -2772,17 +2998,17 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
BUG_ON(dir == DMA_NONE);
- if (iommu_no_mapping(hwdev))
+ if (iommu_no_mapping(dev))
return paddr;
- domain = get_valid_domain_for_dev(pdev);
+ domain = get_valid_domain_for_dev(dev);
if (!domain)
return 0;
iommu = domain_get_iommu(domain);
size = aligned_nrpages(paddr, size);
- iova = intel_alloc_iova(hwdev, domain, dma_to_mm_pfn(size), dma_mask);
+ iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), dma_mask);
if (!iova)
goto error;
@@ -2808,7 +3034,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
/* it's a non-present to present mapping. Only flush if caching mode */
if (cap_caching_mode(iommu->cap))
- iommu_flush_iotlb_psi(iommu, domain->id, mm_to_dma_pfn(iova->pfn_lo), size, 1);
+ iommu_flush_iotlb_psi(iommu, domain->id, mm_to_dma_pfn(iova->pfn_lo), size, 0, 1);
else
iommu_flush_write_buffer(iommu);
@@ -2820,7 +3046,7 @@ error:
if (iova)
__free_iova(&domain->iovad, iova);
printk(KERN_ERR"Device %s request: %zx@%llx dir %d --- failed\n",
- pci_name(pdev), size, (unsigned long long)paddr, dir);
+ dev_name(dev), size, (unsigned long long)paddr, dir);
return 0;
}
@@ -2830,7 +3056,7 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page,
struct dma_attrs *attrs)
{
return __intel_map_single(dev, page_to_phys(page) + offset, size,
- dir, to_pci_dev(dev)->dma_mask);
+ dir, *dev->dma_mask);
}
static void flush_unmaps(void)
@@ -2860,13 +3086,16 @@ static void flush_unmaps(void)
/* On real hardware multiple invalidations are expensive */
if (cap_caching_mode(iommu->cap))
iommu_flush_iotlb_psi(iommu, domain->id,
- iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1, 0);
+ iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1,
+ !deferred_flush[i].freelist[j], 0);
else {
mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1));
iommu_flush_dev_iotlb(deferred_flush[i].domain[j],
(uint64_t)iova->pfn_lo << PAGE_SHIFT, mask);
}
__free_iova(&deferred_flush[i].domain[j]->iovad, iova);
+ if (deferred_flush[i].freelist[j])
+ dma_free_pagelist(deferred_flush[i].freelist[j]);
}
deferred_flush[i].next = 0;
}
@@ -2883,7 +3112,7 @@ static void flush_unmaps_timeout(unsigned long data)
spin_unlock_irqrestore(&async_umap_flush_lock, flags);
}
-static void add_unmap(struct dmar_domain *dom, struct iova *iova)
+static void add_unmap(struct dmar_domain *dom, struct iova *iova, struct page *freelist)
{
unsigned long flags;
int next, iommu_id;
@@ -2899,6 +3128,7 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova)
next = deferred_flush[iommu_id].next;
deferred_flush[iommu_id].domain[next] = dom;
deferred_flush[iommu_id].iova[next] = iova;
+ deferred_flush[iommu_id].freelist[next] = freelist;
deferred_flush[iommu_id].next++;
if (!timer_on) {
@@ -2913,16 +3143,16 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- struct pci_dev *pdev = to_pci_dev(dev);
struct dmar_domain *domain;
unsigned long start_pfn, last_pfn;
struct iova *iova;
struct intel_iommu *iommu;
+ struct page *freelist;
if (iommu_no_mapping(dev))
return;
- domain = find_domain(pdev);
+ domain = find_domain(dev);
BUG_ON(!domain);
iommu = domain_get_iommu(domain);
@@ -2936,21 +3166,18 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
last_pfn = mm_to_dma_pfn(iova->pfn_hi + 1) - 1;
pr_debug("Device %s unmapping: pfn %lx-%lx\n",
- pci_name(pdev), start_pfn, last_pfn);
+ dev_name(dev), start_pfn, last_pfn);
- /* clear the whole page */
- dma_pte_clear_range(domain, start_pfn, last_pfn);
-
- /* free page tables */
- dma_pte_free_pagetable(domain, start_pfn, last_pfn);
+ freelist = domain_unmap(domain, start_pfn, last_pfn);
if (intel_iommu_strict) {
iommu_flush_iotlb_psi(iommu, domain->id, start_pfn,
- last_pfn - start_pfn + 1, 0);
+ last_pfn - start_pfn + 1, !freelist, 0);
/* free iova */
__free_iova(&domain->iovad, iova);
+ dma_free_pagelist(freelist);
} else {
- add_unmap(domain, iova);
+ add_unmap(domain, iova, freelist);
/*
* queue up the release of the unmap to save the 1/6th of the
* cpu used up by the iotlb flush operation...
@@ -2958,7 +3185,7 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
}
}
-static void *intel_alloc_coherent(struct device *hwdev, size_t size,
+static void *intel_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flags,
struct dma_attrs *attrs)
{
@@ -2968,10 +3195,10 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,
size = PAGE_ALIGN(size);
order = get_order(size);
- if (!iommu_no_mapping(hwdev))
+ if (!iommu_no_mapping(dev))
flags &= ~(GFP_DMA | GFP_DMA32);
- else if (hwdev->coherent_dma_mask < dma_get_required_mask(hwdev)) {
- if (hwdev->coherent_dma_mask < DMA_BIT_MASK(32))
+ else if (dev->coherent_dma_mask < dma_get_required_mask(dev)) {
+ if (dev->coherent_dma_mask < DMA_BIT_MASK(32))
flags |= GFP_DMA;
else
flags |= GFP_DMA32;
@@ -2982,16 +3209,16 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,
return NULL;
memset(vaddr, 0, size);
- *dma_handle = __intel_map_single(hwdev, virt_to_bus(vaddr), size,
+ *dma_handle = __intel_map_single(dev, virt_to_bus(vaddr), size,
DMA_BIDIRECTIONAL,
- hwdev->coherent_dma_mask);
+ dev->coherent_dma_mask);
if (*dma_handle)
return vaddr;
free_pages((unsigned long)vaddr, order);
return NULL;
}
-static void intel_free_coherent(struct device *hwdev, size_t size, void *vaddr,
+static void intel_free_coherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, struct dma_attrs *attrs)
{
int order;
@@ -2999,24 +3226,24 @@ static void intel_free_coherent(struct device *hwdev, size_t size, void *vaddr,
size = PAGE_ALIGN(size);
order = get_order(size);
- intel_unmap_page(hwdev, dma_handle, size, DMA_BIDIRECTIONAL, NULL);
+ intel_unmap_page(dev, dma_handle, size, DMA_BIDIRECTIONAL, NULL);
free_pages((unsigned long)vaddr, order);
}
-static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
+static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist,
int nelems, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- struct pci_dev *pdev = to_pci_dev(hwdev);
struct dmar_domain *domain;
unsigned long start_pfn, last_pfn;
struct iova *iova;
struct intel_iommu *iommu;
+ struct page *freelist;
- if (iommu_no_mapping(hwdev))
+ if (iommu_no_mapping(dev))
return;
- domain = find_domain(pdev);
+ domain = find_domain(dev);
BUG_ON(!domain);
iommu = domain_get_iommu(domain);
@@ -3029,19 +3256,16 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
start_pfn = mm_to_dma_pfn(iova->pfn_lo);
last_pfn = mm_to_dma_pfn(iova->pfn_hi + 1) - 1;
- /* clear the whole page */
- dma_pte_clear_range(domain, start_pfn, last_pfn);
-
- /* free page tables */
- dma_pte_free_pagetable(domain, start_pfn, last_pfn);
+ freelist = domain_unmap(domain, start_pfn, last_pfn);
if (intel_iommu_strict) {
iommu_flush_iotlb_psi(iommu, domain->id, start_pfn,
- last_pfn - start_pfn + 1, 0);
+ last_pfn - start_pfn + 1, !freelist, 0);
/* free iova */
__free_iova(&domain->iovad, iova);
+ dma_free_pagelist(freelist);
} else {
- add_unmap(domain, iova);
+ add_unmap(domain, iova, freelist);
/*
* queue up the release of the unmap to save the 1/6th of the
* cpu used up by the iotlb flush operation...
@@ -3063,11 +3287,10 @@ static int intel_nontranslate_map_sg(struct device *hddev,
return nelems;
}
-static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems,
+static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nelems,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
int i;
- struct pci_dev *pdev = to_pci_dev(hwdev);
struct dmar_domain *domain;
size_t size = 0;
int prot = 0;
@@ -3078,10 +3301,10 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
struct intel_iommu *iommu;
BUG_ON(dir == DMA_NONE);
- if (iommu_no_mapping(hwdev))
- return intel_nontranslate_map_sg(hwdev, sglist, nelems, dir);
+ if (iommu_no_mapping(dev))
+ return intel_nontranslate_map_sg(dev, sglist, nelems, dir);
- domain = get_valid_domain_for_dev(pdev);
+ domain = get_valid_domain_for_dev(dev);
if (!domain)
return 0;
@@ -3090,8 +3313,8 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
for_each_sg(sglist, sg, nelems, i)
size += aligned_nrpages(sg->offset, sg->length);
- iova = intel_alloc_iova(hwdev, domain, dma_to_mm_pfn(size),
- pdev->dma_mask);
+ iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size),
+ *dev->dma_mask);
if (!iova) {
sglist->dma_length = 0;
return 0;
@@ -3124,7 +3347,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
/* it's a non-present to present mapping. Only flush if caching mode */
if (cap_caching_mode(iommu->cap))
- iommu_flush_iotlb_psi(iommu, domain->id, start_vpfn, size, 1);
+ iommu_flush_iotlb_psi(iommu, domain->id, start_vpfn, size, 0, 1);
else
iommu_flush_write_buffer(iommu);
@@ -3259,29 +3482,28 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB, quir
static void __init init_no_remapping_devices(void)
{
struct dmar_drhd_unit *drhd;
+ struct device *dev;
+ int i;
for_each_drhd_unit(drhd) {
if (!drhd->include_all) {
- int i;
- for (i = 0; i < drhd->devices_cnt; i++)
- if (drhd->devices[i] != NULL)
- break;
- /* ignore DMAR unit if no pci devices exist */
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, dev)
+ break;
+ /* ignore DMAR unit if no devices exist */
if (i == drhd->devices_cnt)
drhd->ignored = 1;
}
}
for_each_active_drhd_unit(drhd) {
- int i;
if (drhd->include_all)
continue;
- for (i = 0; i < drhd->devices_cnt; i++)
- if (drhd->devices[i] &&
- !IS_GFX_DEVICE(drhd->devices[i]))
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, dev)
+ if (!dev_is_pci(dev) || !IS_GFX_DEVICE(to_pci_dev(dev)))
break;
-
if (i < drhd->devices_cnt)
continue;
@@ -3291,11 +3513,9 @@ static void __init init_no_remapping_devices(void)
intel_iommu_gfx_mapped = 1;
} else {
drhd->ignored = 1;
- for (i = 0; i < drhd->devices_cnt; i++) {
- if (!drhd->devices[i])
- continue;
- drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
- }
+ for_each_active_dev_scope(drhd->devices,
+ drhd->devices_cnt, i, dev)
+ dev->archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO;
}
}
}
@@ -3438,13 +3658,6 @@ static void __init init_iommu_pm_ops(void)
static inline void init_iommu_pm_ops(void) {}
#endif /* CONFIG_PM */
-LIST_HEAD(dmar_rmrr_units);
-
-static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
-{
- list_add(&rmrr->list, &dmar_rmrr_units);
-}
-
int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
{
@@ -3459,25 +3672,19 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header)
rmrr = (struct acpi_dmar_reserved_memory *)header;
rmrru->base_address = rmrr->base_address;
rmrru->end_address = rmrr->end_address;
+ rmrru->devices = dmar_alloc_dev_scope((void *)(rmrr + 1),
+ ((void *)rmrr) + rmrr->header.length,
+ &rmrru->devices_cnt);
+ if (rmrru->devices_cnt && rmrru->devices == NULL) {
+ kfree(rmrru);
+ return -ENOMEM;
+ }
- dmar_register_rmrr_unit(rmrru);
- return 0;
-}
-
-static int __init
-rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
-{
- struct acpi_dmar_reserved_memory *rmrr;
+ list_add(&rmrru->list, &dmar_rmrr_units);
- rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
- return dmar_parse_dev_scope((void *)(rmrr + 1),
- ((void *)rmrr) + rmrr->header.length,
- &rmrru->devices_cnt, &rmrru->devices,
- rmrr->segment);
+ return 0;
}
-static LIST_HEAD(dmar_atsr_units);
-
int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
{
struct acpi_dmar_atsr *atsr;
@@ -3490,26 +3697,21 @@ int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr)
atsru->hdr = hdr;
atsru->include_all = atsr->flags & 0x1;
+ if (!atsru->include_all) {
+ atsru->devices = dmar_alloc_dev_scope((void *)(atsr + 1),
+ (void *)atsr + atsr->header.length,
+ &atsru->devices_cnt);
+ if (atsru->devices_cnt && atsru->devices == NULL) {
+ kfree(atsru);
+ return -ENOMEM;
+ }
+ }
- list_add(&atsru->list, &dmar_atsr_units);
+ list_add_rcu(&atsru->list, &dmar_atsr_units);
return 0;
}
-static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru)
-{
- struct acpi_dmar_atsr *atsr;
-
- if (atsru->include_all)
- return 0;
-
- atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
- return dmar_parse_dev_scope((void *)(atsr + 1),
- (void *)atsr + atsr->header.length,
- &atsru->devices_cnt, &atsru->devices,
- atsr->segment);
-}
-
static void intel_iommu_free_atsr(struct dmar_atsr_unit *atsru)
{
dmar_free_dev_scope(&atsru->devices, &atsru->devices_cnt);
@@ -3535,62 +3737,97 @@ static void intel_iommu_free_dmars(void)
int dmar_find_matched_atsr_unit(struct pci_dev *dev)
{
- int i;
+ int i, ret = 1;
struct pci_bus *bus;
+ struct pci_dev *bridge = NULL;
+ struct device *tmp;
struct acpi_dmar_atsr *atsr;
struct dmar_atsr_unit *atsru;
dev = pci_physfn(dev);
-
- list_for_each_entry(atsru, &dmar_atsr_units, list) {
- atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
- if (atsr->segment == pci_domain_nr(dev->bus))
- goto found;
- }
-
- return 0;
-
-found:
for (bus = dev->bus; bus; bus = bus->parent) {
- struct pci_dev *bridge = bus->self;
-
+ bridge = bus->self;
if (!bridge || !pci_is_pcie(bridge) ||
pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
-
- if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
- for (i = 0; i < atsru->devices_cnt; i++)
- if (atsru->devices[i] == bridge)
- return 1;
+ if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT)
break;
- }
}
+ if (!bridge)
+ return 0;
- if (atsru->include_all)
- return 1;
+ rcu_read_lock();
+ list_for_each_entry_rcu(atsru, &dmar_atsr_units, list) {
+ atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
+ if (atsr->segment != pci_domain_nr(dev->bus))
+ continue;
- return 0;
+ for_each_dev_scope(atsru->devices, atsru->devices_cnt, i, tmp)
+ if (tmp == &bridge->dev)
+ goto out;
+
+ if (atsru->include_all)
+ goto out;
+ }
+ ret = 0;
+out:
+ rcu_read_unlock();
+
+ return ret;
}
-int __init dmar_parse_rmrr_atsr_dev(void)
+int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
{
- struct dmar_rmrr_unit *rmrr;
- struct dmar_atsr_unit *atsr;
int ret = 0;
+ struct dmar_rmrr_unit *rmrru;
+ struct dmar_atsr_unit *atsru;
+ struct acpi_dmar_atsr *atsr;
+ struct acpi_dmar_reserved_memory *rmrr;
- list_for_each_entry(rmrr, &dmar_rmrr_units, list) {
- ret = rmrr_parse_dev(rmrr);
- if (ret)
- return ret;
+ if (!intel_iommu_enabled && system_state != SYSTEM_BOOTING)
+ return 0;
+
+ list_for_each_entry(rmrru, &dmar_rmrr_units, list) {
+ rmrr = container_of(rmrru->hdr,
+ struct acpi_dmar_reserved_memory, header);
+ if (info->event == BUS_NOTIFY_ADD_DEVICE) {
+ ret = dmar_insert_dev_scope(info, (void *)(rmrr + 1),
+ ((void *)rmrr) + rmrr->header.length,
+ rmrr->segment, rmrru->devices,
+ rmrru->devices_cnt);
+ if (ret > 0)
+ break;
+ else if(ret < 0)
+ return ret;
+ } else if (info->event == BUS_NOTIFY_DEL_DEVICE) {
+ if (dmar_remove_dev_scope(info, rmrr->segment,
+ rmrru->devices, rmrru->devices_cnt))
+ break;
+ }
}
- list_for_each_entry(atsr, &dmar_atsr_units, list) {
- ret = atsr_parse_dev(atsr);
- if (ret)
- return ret;
+ list_for_each_entry(atsru, &dmar_atsr_units, list) {
+ if (atsru->include_all)
+ continue;
+
+ atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
+ if (info->event == BUS_NOTIFY_ADD_DEVICE) {
+ ret = dmar_insert_dev_scope(info, (void *)(atsr + 1),
+ (void *)atsr + atsr->header.length,
+ atsr->segment, atsru->devices,
+ atsru->devices_cnt);
+ if (ret > 0)
+ break;
+ else if(ret < 0)
+ return ret;
+ } else if (info->event == BUS_NOTIFY_DEL_DEVICE) {
+ if (dmar_remove_dev_scope(info, atsr->segment,
+ atsru->devices, atsru->devices_cnt))
+ break;
+ }
}
- return ret;
+ return 0;
}
/*
@@ -3603,24 +3840,26 @@ static int device_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
- struct pci_dev *pdev = to_pci_dev(dev);
struct dmar_domain *domain;
- if (iommu_no_mapping(dev))
+ if (iommu_dummy(dev))
return 0;
- domain = find_domain(pdev);
- if (!domain)
+ if (action != BUS_NOTIFY_UNBOUND_DRIVER &&
+ action != BUS_NOTIFY_DEL_DEVICE)
return 0;
- if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) {
- domain_remove_one_dev_info(domain, pdev);
+ domain = find_domain(dev);
+ if (!domain)
+ return 0;
- if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
- !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
- list_empty(&domain->devices))
- domain_exit(domain);
- }
+ down_read(&dmar_global_lock);
+ domain_remove_one_dev_info(domain, dev);
+ if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
+ !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
+ list_empty(&domain->devices))
+ domain_exit(domain);
+ up_read(&dmar_global_lock);
return 0;
}
@@ -3629,6 +3868,75 @@ static struct notifier_block device_nb = {
.notifier_call = device_notifier,
};
+static int intel_iommu_memory_notifier(struct notifier_block *nb,
+ unsigned long val, void *v)
+{
+ struct memory_notify *mhp = v;
+ unsigned long long start, end;
+ unsigned long start_vpfn, last_vpfn;
+
+ switch (val) {
+ case MEM_GOING_ONLINE:
+ start = mhp->start_pfn << PAGE_SHIFT;
+ end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
+ if (iommu_domain_identity_map(si_domain, start, end)) {
+ pr_warn("dmar: failed to build identity map for [%llx-%llx]\n",
+ start, end);
+ return NOTIFY_BAD;
+ }
+ break;
+
+ case MEM_OFFLINE:
+ case MEM_CANCEL_ONLINE:
+ start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
+ last_vpfn = mm_to_dma_pfn(mhp->start_pfn + mhp->nr_pages - 1);
+ while (start_vpfn <= last_vpfn) {
+ struct iova *iova;
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ struct page *freelist;
+
+ iova = find_iova(&si_domain->iovad, start_vpfn);
+ if (iova == NULL) {
+ pr_debug("dmar: failed get IOVA for PFN %lx\n",
+ start_vpfn);
+ break;
+ }
+
+ iova = split_and_remove_iova(&si_domain->iovad, iova,
+ start_vpfn, last_vpfn);
+ if (iova == NULL) {
+ pr_warn("dmar: failed to split IOVA PFN [%lx-%lx]\n",
+ start_vpfn, last_vpfn);
+ return NOTIFY_BAD;
+ }
+
+ freelist = domain_unmap(si_domain, iova->pfn_lo,
+ iova->pfn_hi);
+
+ rcu_read_lock();
+ for_each_active_iommu(iommu, drhd)
+ iommu_flush_iotlb_psi(iommu, si_domain->id,
+ iova->pfn_lo,
+ iova->pfn_hi - iova->pfn_lo + 1,
+ !freelist, 0);
+ rcu_read_unlock();
+ dma_free_pagelist(freelist);
+
+ start_vpfn = iova->pfn_hi + 1;
+ free_iova_mem(iova);
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block intel_iommu_memory_nb = {
+ .notifier_call = intel_iommu_memory_notifier,
+ .priority = 0
+};
+
int __init intel_iommu_init(void)
{
int ret = -ENODEV;
@@ -3638,6 +3946,13 @@ int __init intel_iommu_init(void)
/* VT-d is required for a TXT/tboot launch, so enforce that */
force_on = tboot_force_iommu();
+ if (iommu_init_mempool()) {
+ if (force_on)
+ panic("tboot: Failed to initialize iommu memory\n");
+ return -ENOMEM;
+ }
+
+ down_write(&dmar_global_lock);
if (dmar_table_init()) {
if (force_on)
panic("tboot: Failed to initialize DMAR table\n");
@@ -3660,12 +3975,6 @@ int __init intel_iommu_init(void)
if (no_iommu || dmar_disabled)
goto out_free_dmar;
- if (iommu_init_mempool()) {
- if (force_on)
- panic("tboot: Failed to initialize iommu memory\n");
- goto out_free_dmar;
- }
-
if (list_empty(&dmar_rmrr_units))
printk(KERN_INFO "DMAR: No RMRR found\n");
@@ -3675,7 +3984,7 @@ int __init intel_iommu_init(void)
if (dmar_init_reserved_ranges()) {
if (force_on)
panic("tboot: Failed to reserve iommu ranges\n");
- goto out_free_mempool;
+ goto out_free_reserved_range;
}
init_no_remapping_devices();
@@ -3687,6 +3996,7 @@ int __init intel_iommu_init(void)
printk(KERN_ERR "IOMMU: dmar init failed\n");
goto out_free_reserved_range;
}
+ up_write(&dmar_global_lock);
printk(KERN_INFO
"PCI-DMA: Intel(R) Virtualization Technology for Directed I/O\n");
@@ -3699,8 +4009,9 @@ int __init intel_iommu_init(void)
init_iommu_pm_ops();
bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
-
bus_register_notifier(&pci_bus_type, &device_nb);
+ if (si_domain && !hw_pass_through)
+ register_memory_notifier(&intel_iommu_memory_nb);
intel_iommu_enabled = 1;
@@ -3708,21 +4019,23 @@ int __init intel_iommu_init(void)
out_free_reserved_range:
put_iova_domain(&reserved_iova_list);
-out_free_mempool:
- iommu_exit_mempool();
out_free_dmar:
intel_iommu_free_dmars();
+ up_write(&dmar_global_lock);
+ iommu_exit_mempool();
return ret;
}
static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
- struct pci_dev *pdev)
+ struct device *dev)
{
- struct pci_dev *tmp, *parent;
+ struct pci_dev *tmp, *parent, *pdev;
- if (!iommu || !pdev)
+ if (!iommu || !dev || !dev_is_pci(dev))
return;
+ pdev = to_pci_dev(dev);
+
/* dependent device detach */
tmp = pci_find_upstream_pcie_bridge(pdev);
/* Secondary interface's bus number and devfn 0 */
@@ -3743,29 +4056,28 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
}
static void domain_remove_one_dev_info(struct dmar_domain *domain,
- struct pci_dev *pdev)
+ struct device *dev)
{
struct device_domain_info *info, *tmp;
struct intel_iommu *iommu;
unsigned long flags;
int found = 0;
+ u8 bus, devfn;
- iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
- pdev->devfn);
+ iommu = device_to_iommu(dev, &bus, &devfn);
if (!iommu)
return;
spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry_safe(info, tmp, &domain->devices, link) {
- if (info->segment == pci_domain_nr(pdev->bus) &&
- info->bus == pdev->bus->number &&
- info->devfn == pdev->devfn) {
+ if (info->iommu == iommu && info->bus == bus &&
+ info->devfn == devfn) {
unlink_domain_info(info);
spin_unlock_irqrestore(&device_domain_lock, flags);
iommu_disable_dev_iotlb(info);
iommu_detach_dev(iommu, info->bus, info->devfn);
- iommu_detach_dependent_devices(iommu, pdev);
+ iommu_detach_dependent_devices(iommu, dev);
free_devinfo_mem(info);
spin_lock_irqsave(&device_domain_lock, flags);
@@ -3780,8 +4092,7 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
* owned by this domain, clear this iommu in iommu_bmp
* update iommu count and coherency
*/
- if (iommu == device_to_iommu(info->segment, info->bus,
- info->devfn))
+ if (info->iommu == iommu)
found = 1;
}
@@ -3805,67 +4116,11 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
}
}
-static void vm_domain_remove_all_dev_info(struct dmar_domain *domain)
-{
- struct device_domain_info *info;
- struct intel_iommu *iommu;
- unsigned long flags1, flags2;
-
- spin_lock_irqsave(&device_domain_lock, flags1);
- while (!list_empty(&domain->devices)) {
- info = list_entry(domain->devices.next,
- struct device_domain_info, link);
- unlink_domain_info(info);
- spin_unlock_irqrestore(&device_domain_lock, flags1);
-
- iommu_disable_dev_iotlb(info);
- iommu = device_to_iommu(info->segment, info->bus, info->devfn);
- iommu_detach_dev(iommu, info->bus, info->devfn);
- iommu_detach_dependent_devices(iommu, info->dev);
-
- /* clear this iommu in iommu_bmp, update iommu count
- * and capabilities
- */
- spin_lock_irqsave(&domain->iommu_lock, flags2);
- if (test_and_clear_bit(iommu->seq_id,
- domain->iommu_bmp)) {
- domain->iommu_count--;
- domain_update_iommu_cap(domain);
- }
- spin_unlock_irqrestore(&domain->iommu_lock, flags2);
-
- free_devinfo_mem(info);
- spin_lock_irqsave(&device_domain_lock, flags1);
- }
- spin_unlock_irqrestore(&device_domain_lock, flags1);
-}
-
-/* domain id for virtual machine, it won't be set in context */
-static atomic_t vm_domid = ATOMIC_INIT(0);
-
-static struct dmar_domain *iommu_alloc_vm_domain(void)
-{
- struct dmar_domain *domain;
-
- domain = alloc_domain_mem();
- if (!domain)
- return NULL;
-
- domain->id = atomic_inc_return(&vm_domid);
- domain->nid = -1;
- memset(domain->iommu_bmp, 0, sizeof(domain->iommu_bmp));
- domain->flags = DOMAIN_FLAG_VIRTUAL_MACHINE;
-
- return domain;
-}
-
static int md_domain_init(struct dmar_domain *domain, int guest_width)
{
int adjust_width;
init_iova_domain(&domain->iovad, DMA_32BIT_PFN);
- spin_lock_init(&domain->iommu_lock);
-
domain_reserve_special_ranges(domain);
/* calculate AGAW */
@@ -3873,9 +4128,6 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width)
adjust_width = guestwidth_to_adjustwidth(guest_width);
domain->agaw = width_to_agaw(adjust_width);
- INIT_LIST_HEAD(&domain->devices);
-
- domain->iommu_count = 0;
domain->iommu_coherency = 0;
domain->iommu_snooping = 0;
domain->iommu_superpage = 0;
@@ -3890,53 +4142,11 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width)
return 0;
}
-static void iommu_free_vm_domain(struct dmar_domain *domain)
-{
- unsigned long flags;
- struct dmar_drhd_unit *drhd;
- struct intel_iommu *iommu;
- unsigned long i;
- unsigned long ndomains;
-
- for_each_active_iommu(iommu, drhd) {
- ndomains = cap_ndoms(iommu->cap);
- for_each_set_bit(i, iommu->domain_ids, ndomains) {
- if (iommu->domains[i] == domain) {
- spin_lock_irqsave(&iommu->lock, flags);
- clear_bit(i, iommu->domain_ids);
- iommu->domains[i] = NULL;
- spin_unlock_irqrestore(&iommu->lock, flags);
- break;
- }
- }
- }
-}
-
-static void vm_domain_exit(struct dmar_domain *domain)
-{
- /* Domain 0 is reserved, so dont process it */
- if (!domain)
- return;
-
- vm_domain_remove_all_dev_info(domain);
- /* destroy iovas */
- put_iova_domain(&domain->iovad);
-
- /* clear ptes */
- dma_pte_clear_range(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
-
- /* free page tables */
- dma_pte_free_pagetable(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
-
- iommu_free_vm_domain(domain);
- free_domain_mem(domain);
-}
-
static int intel_iommu_domain_init(struct iommu_domain *domain)
{
struct dmar_domain *dmar_domain;
- dmar_domain = iommu_alloc_vm_domain();
+ dmar_domain = alloc_domain(true);
if (!dmar_domain) {
printk(KERN_ERR
"intel_iommu_domain_init: dmar_domain == NULL\n");
@@ -3945,7 +4155,7 @@ static int intel_iommu_domain_init(struct iommu_domain *domain)
if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
printk(KERN_ERR
"intel_iommu_domain_init() failed\n");
- vm_domain_exit(dmar_domain);
+ domain_exit(dmar_domain);
return -ENOMEM;
}
domain_update_iommu_cap(dmar_domain);
@@ -3963,33 +4173,32 @@ static void intel_iommu_domain_destroy(struct iommu_domain *domain)
struct dmar_domain *dmar_domain = domain->priv;
domain->priv = NULL;
- vm_domain_exit(dmar_domain);
+ domain_exit(dmar_domain);
}
static int intel_iommu_attach_device(struct iommu_domain *domain,
struct device *dev)
{
struct dmar_domain *dmar_domain = domain->priv;
- struct pci_dev *pdev = to_pci_dev(dev);
struct intel_iommu *iommu;
int addr_width;
+ u8 bus, devfn;
- /* normally pdev is not mapped */
- if (unlikely(domain_context_mapped(pdev))) {
+ /* normally dev is not mapped */
+ if (unlikely(domain_context_mapped(dev))) {
struct dmar_domain *old_domain;
- old_domain = find_domain(pdev);
+ old_domain = find_domain(dev);
if (old_domain) {
if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE ||
dmar_domain->flags & DOMAIN_FLAG_STATIC_IDENTITY)
- domain_remove_one_dev_info(old_domain, pdev);
+ domain_remove_one_dev_info(old_domain, dev);
else
domain_remove_dev_info(old_domain);
}
}
- iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number,
- pdev->devfn);
+ iommu = device_to_iommu(dev, &bus, &devfn);
if (!iommu)
return -ENODEV;
@@ -4021,16 +4230,15 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
dmar_domain->agaw--;
}
- return domain_add_dev_info(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL);
+ return domain_add_dev_info(dmar_domain, dev, CONTEXT_TT_MULTI_LEVEL);
}
static void intel_iommu_detach_device(struct iommu_domain *domain,
struct device *dev)
{
struct dmar_domain *dmar_domain = domain->priv;
- struct pci_dev *pdev = to_pci_dev(dev);
- domain_remove_one_dev_info(dmar_domain, pdev);
+ domain_remove_one_dev_info(dmar_domain, dev);
}
static int intel_iommu_map(struct iommu_domain *domain,
@@ -4072,18 +4280,51 @@ static int intel_iommu_map(struct iommu_domain *domain,
}
static size_t intel_iommu_unmap(struct iommu_domain *domain,
- unsigned long iova, size_t size)
+ unsigned long iova, size_t size)
{
struct dmar_domain *dmar_domain = domain->priv;
- int order;
+ struct page *freelist = NULL;
+ struct intel_iommu *iommu;
+ unsigned long start_pfn, last_pfn;
+ unsigned int npages;
+ int iommu_id, num, ndomains, level = 0;
+
+ /* Cope with horrid API which requires us to unmap more than the
+ size argument if it happens to be a large-page mapping. */
+ if (!pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level))
+ BUG();
+
+ if (size < VTD_PAGE_SIZE << level_to_offset_bits(level))
+ size = VTD_PAGE_SIZE << level_to_offset_bits(level);
+
+ start_pfn = iova >> VTD_PAGE_SHIFT;
+ last_pfn = (iova + size - 1) >> VTD_PAGE_SHIFT;
+
+ freelist = domain_unmap(dmar_domain, start_pfn, last_pfn);
+
+ npages = last_pfn - start_pfn + 1;
+
+ for_each_set_bit(iommu_id, dmar_domain->iommu_bmp, g_num_of_iommus) {
+ iommu = g_iommus[iommu_id];
+
+ /*
+ * find bit position of dmar_domain
+ */
+ ndomains = cap_ndoms(iommu->cap);
+ for_each_set_bit(num, iommu->domain_ids, ndomains) {
+ if (iommu->domains[num] == dmar_domain)
+ iommu_flush_iotlb_psi(iommu, num, start_pfn,
+ npages, !freelist, 0);
+ }
+
+ }
- order = dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT,
- (iova + size - 1) >> VTD_PAGE_SHIFT);
+ dma_free_pagelist(freelist);
if (dmar_domain->max_addr == iova + size)
dmar_domain->max_addr = iova;
- return PAGE_SIZE << order;
+ return size;
}
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
@@ -4091,9 +4332,10 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
{
struct dmar_domain *dmar_domain = domain->priv;
struct dma_pte *pte;
+ int level = 0;
u64 phys = 0;
- pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0);
+ pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level);
if (pte)
phys = dma_pte_addr(pte);
@@ -4121,9 +4363,9 @@ static int intel_iommu_add_device(struct device *dev)
struct pci_dev *bridge, *dma_pdev = NULL;
struct iommu_group *group;
int ret;
+ u8 bus, devfn;
- if (!device_to_iommu(pci_domain_nr(pdev->bus),
- pdev->bus->number, pdev->devfn))
+ if (!device_to_iommu(dev, &bus, &devfn))
return -ENODEV;
bridge = pci_find_upstream_pcie_bridge(pdev);
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index ef5f65dbafe9..9b174893f0f5 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -38,6 +38,17 @@ static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
static struct hpet_scope ir_hpet[MAX_HPET_TBS];
static int ir_ioapic_num, ir_hpet_num;
+/*
+ * Lock ordering:
+ * ->dmar_global_lock
+ * ->irq_2_ir_lock
+ * ->qi->q_lock
+ * ->iommu->register_lock
+ * Note:
+ * intel_irq_remap_ops.{supported,prepare,enable,disable,reenable} are called
+ * in single-threaded environment with interrupt disabled, so no need to tabke
+ * the dmar_global_lock.
+ */
static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
static int __init parse_ioapics_under_ir(void);
@@ -307,12 +318,14 @@ static int set_ioapic_sid(struct irte *irte, int apic)
if (!irte)
return -1;
+ down_read(&dmar_global_lock);
for (i = 0; i < MAX_IO_APICS; i++) {
if (ir_ioapic[i].id == apic) {
sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn;
break;
}
}
+ up_read(&dmar_global_lock);
if (sid == 0) {
pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic);
@@ -332,12 +345,14 @@ static int set_hpet_sid(struct irte *irte, u8 id)
if (!irte)
return -1;
+ down_read(&dmar_global_lock);
for (i = 0; i < MAX_HPET_TBS; i++) {
if (ir_hpet[i].id == id) {
sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
break;
}
}
+ up_read(&dmar_global_lock);
if (sid == 0) {
pr_warning("Failed to set source-id of HPET block (%d)\n", id);
@@ -794,10 +809,16 @@ static int __init parse_ioapics_under_ir(void)
static int __init ir_dev_scope_init(void)
{
+ int ret;
+
if (!irq_remapping_enabled)
return 0;
- return dmar_dev_scope_init();
+ down_write(&dmar_global_lock);
+ ret = dmar_dev_scope_init();
+ up_write(&dmar_global_lock);
+
+ return ret;
}
rootfs_initcall(ir_dev_scope_init);
@@ -878,23 +899,27 @@ static int intel_setup_ioapic_entry(int irq,
struct io_apic_irq_attr *attr)
{
int ioapic_id = mpc_ioapic_id(attr->ioapic);
- struct intel_iommu *iommu = map_ioapic_to_ir(ioapic_id);
+ struct intel_iommu *iommu;
struct IR_IO_APIC_route_entry *entry;
struct irte irte;
int index;
+ down_read(&dmar_global_lock);
+ iommu = map_ioapic_to_ir(ioapic_id);
if (!iommu) {
pr_warn("No mapping iommu for ioapic %d\n", ioapic_id);
- return -ENODEV;
- }
-
- entry = (struct IR_IO_APIC_route_entry *)route_entry;
-
- index = alloc_irte(iommu, irq, 1);
- if (index < 0) {
- pr_warn("Failed to allocate IRTE for ioapic %d\n", ioapic_id);
- return -ENOMEM;
+ index = -ENODEV;
+ } else {
+ index = alloc_irte(iommu, irq, 1);
+ if (index < 0) {
+ pr_warn("Failed to allocate IRTE for ioapic %d\n",
+ ioapic_id);
+ index = -ENOMEM;
+ }
}
+ up_read(&dmar_global_lock);
+ if (index < 0)
+ return index;
prepare_irte(&irte, vector, destination);
@@ -913,6 +938,7 @@ static int intel_setup_ioapic_entry(int irq,
irte.avail, irte.vector, irte.dest_id,
irte.sid, irte.sq, irte.svt);
+ entry = (struct IR_IO_APIC_route_entry *)route_entry;
memset(entry, 0, sizeof(*entry));
entry->index2 = (index >> 15) & 0x1;
@@ -1043,20 +1069,23 @@ static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
struct intel_iommu *iommu;
int index;
+ down_read(&dmar_global_lock);
iommu = map_dev_to_ir(dev);
if (!iommu) {
printk(KERN_ERR
"Unable to map PCI %s to iommu\n", pci_name(dev));
- return -ENOENT;
+ index = -ENOENT;
+ } else {
+ index = alloc_irte(iommu, irq, nvec);
+ if (index < 0) {
+ printk(KERN_ERR
+ "Unable to allocate %d IRTE for PCI %s\n",
+ nvec, pci_name(dev));
+ index = -ENOSPC;
+ }
}
+ up_read(&dmar_global_lock);
- index = alloc_irte(iommu, irq, nvec);
- if (index < 0) {
- printk(KERN_ERR
- "Unable to allocate %d IRTE for PCI %s\n", nvec,
- pci_name(dev));
- return -ENOSPC;
- }
return index;
}
@@ -1064,33 +1093,40 @@ static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
int index, int sub_handle)
{
struct intel_iommu *iommu;
+ int ret = -ENOENT;
+ down_read(&dmar_global_lock);
iommu = map_dev_to_ir(pdev);
- if (!iommu)
- return -ENOENT;
- /*
- * setup the mapping between the irq and the IRTE
- * base index, the sub_handle pointing to the
- * appropriate interrupt remap table entry.
- */
- set_irte_irq(irq, iommu, index, sub_handle);
+ if (iommu) {
+ /*
+ * setup the mapping between the irq and the IRTE
+ * base index, the sub_handle pointing to the
+ * appropriate interrupt remap table entry.
+ */
+ set_irte_irq(irq, iommu, index, sub_handle);
+ ret = 0;
+ }
+ up_read(&dmar_global_lock);
- return 0;
+ return ret;
}
static int intel_setup_hpet_msi(unsigned int irq, unsigned int id)
{
- struct intel_iommu *iommu = map_hpet_to_ir(id);
+ int ret = -1;
+ struct intel_iommu *iommu;
int index;
- if (!iommu)
- return -1;
-
- index = alloc_irte(iommu, irq, 1);
- if (index < 0)
- return -1;
+ down_read(&dmar_global_lock);
+ iommu = map_hpet_to_ir(id);
+ if (iommu) {
+ index = alloc_irte(iommu, irq, 1);
+ if (index >= 0)
+ ret = 0;
+ }
+ up_read(&dmar_global_lock);
- return 0;
+ return ret;
}
struct irq_remap_ops intel_irq_remap_ops = {
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 67da6cff74e8..f6b17e6af2fb 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -342,19 +342,30 @@ __is_range_overlap(struct rb_node *node,
return 0;
}
+static inline struct iova *
+alloc_and_init_iova(unsigned long pfn_lo, unsigned long pfn_hi)
+{
+ struct iova *iova;
+
+ iova = alloc_iova_mem();
+ if (iova) {
+ iova->pfn_lo = pfn_lo;
+ iova->pfn_hi = pfn_hi;
+ }
+
+ return iova;
+}
+
static struct iova *
__insert_new_range(struct iova_domain *iovad,
unsigned long pfn_lo, unsigned long pfn_hi)
{
struct iova *iova;
- iova = alloc_iova_mem();
- if (!iova)
- return iova;
+ iova = alloc_and_init_iova(pfn_lo, pfn_hi);
+ if (iova)
+ iova_insert_rbtree(&iovad->rbroot, iova);
- iova->pfn_hi = pfn_hi;
- iova->pfn_lo = pfn_lo;
- iova_insert_rbtree(&iovad->rbroot, iova);
return iova;
}
@@ -433,3 +444,44 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to)
}
spin_unlock_irqrestore(&from->iova_rbtree_lock, flags);
}
+
+struct iova *
+split_and_remove_iova(struct iova_domain *iovad, struct iova *iova,
+ unsigned long pfn_lo, unsigned long pfn_hi)
+{
+ unsigned long flags;
+ struct iova *prev = NULL, *next = NULL;
+
+ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
+ if (iova->pfn_lo < pfn_lo) {
+ prev = alloc_and_init_iova(iova->pfn_lo, pfn_lo - 1);
+ if (prev == NULL)
+ goto error;
+ }
+ if (iova->pfn_hi > pfn_hi) {
+ next = alloc_and_init_iova(pfn_hi + 1, iova->pfn_hi);
+ if (next == NULL)
+ goto error;
+ }
+
+ __cached_rbnode_delete_update(iovad, iova);
+ rb_erase(&iova->node, &iovad->rbroot);
+
+ if (prev) {
+ iova_insert_rbtree(&iovad->rbroot, prev);
+ iova->pfn_lo = pfn_lo;
+ }
+ if (next) {
+ iova_insert_rbtree(&iovad->rbroot, next);
+ iova->pfn_hi = pfn_hi;
+ }
+ spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+
+ return iova;
+
+error:
+ spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+ if (prev)
+ free_iova_mem(prev);
+ return NULL;
+}
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index bcd78a720630..7fcbfc498fa9 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -23,6 +23,9 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_iommu.h>
+#include <linux/of_irq.h>
#include <asm/cacheflush.h>
@@ -146,13 +149,10 @@ static int iommu_enable(struct omap_iommu *obj)
struct platform_device *pdev = to_platform_device(obj->dev);
struct iommu_platform_data *pdata = pdev->dev.platform_data;
- if (!pdata)
- return -EINVAL;
-
if (!arch_iommu)
return -ENODEV;
- if (pdata->deassert_reset) {
+ if (pdata && pdata->deassert_reset) {
err = pdata->deassert_reset(pdev, pdata->reset_name);
if (err) {
dev_err(obj->dev, "deassert_reset failed: %d\n", err);
@@ -172,14 +172,11 @@ static void iommu_disable(struct omap_iommu *obj)
struct platform_device *pdev = to_platform_device(obj->dev);
struct iommu_platform_data *pdata = pdev->dev.platform_data;
- if (!pdata)
- return;
-
arch_iommu->disable(obj);
pm_runtime_put_sync(obj->dev);
- if (pdata->assert_reset)
+ if (pdata && pdata->assert_reset)
pdata->assert_reset(pdev, pdata->reset_name);
}
@@ -523,7 +520,8 @@ static void flush_iopte_range(u32 *first, u32 *last)
static void iopte_free(u32 *iopte)
{
/* Note: freed iopte's must be clean ready for re-use */
- kmem_cache_free(iopte_cachep, iopte);
+ if (iopte)
+ kmem_cache_free(iopte_cachep, iopte);
}
static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
@@ -863,7 +861,7 @@ static int device_match_by_alias(struct device *dev, void *data)
**/
static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
{
- int err = -ENOMEM;
+ int err;
struct device *dev;
struct omap_iommu *obj;
@@ -871,7 +869,7 @@ static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
(void *)name,
device_match_by_alias);
if (!dev)
- return NULL;
+ return ERR_PTR(-ENODEV);
obj = to_iommu(dev);
@@ -890,8 +888,10 @@ static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
goto err_enable;
flush_iotlb_all(obj);
- if (!try_module_get(obj->owner))
+ if (!try_module_get(obj->owner)) {
+ err = -ENODEV;
goto err_module;
+ }
spin_unlock(&obj->iommu_lock);
@@ -940,17 +940,41 @@ static int omap_iommu_probe(struct platform_device *pdev)
struct omap_iommu *obj;
struct resource *res;
struct iommu_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *of = pdev->dev.of_node;
- obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
+ obj = devm_kzalloc(&pdev->dev, sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
if (!obj)
return -ENOMEM;
- obj->nr_tlb_entries = pdata->nr_tlb_entries;
- obj->name = pdata->name;
+ if (of) {
+ obj->name = dev_name(&pdev->dev);
+ obj->nr_tlb_entries = 32;
+ err = of_property_read_u32(of, "ti,#tlb-entries",
+ &obj->nr_tlb_entries);
+ if (err && err != -EINVAL)
+ return err;
+ if (obj->nr_tlb_entries != 32 && obj->nr_tlb_entries != 8)
+ return -EINVAL;
+ /*
+ * da_start and da_end are needed for omap-iovmm, so hardcode
+ * these values as used by OMAP3 ISP - the only user for
+ * omap-iovmm
+ */
+ obj->da_start = 0;
+ obj->da_end = 0xfffff000;
+ if (of_find_property(of, "ti,iommu-bus-err-back", NULL))
+ obj->has_bus_err_back = MMU_GP_REG_BUS_ERR_BACK_EN;
+ } else {
+ obj->nr_tlb_entries = pdata->nr_tlb_entries;
+ obj->name = pdata->name;
+ obj->da_start = pdata->da_start;
+ obj->da_end = pdata->da_end;
+ }
+ if (obj->da_end <= obj->da_start)
+ return -EINVAL;
+
obj->dev = &pdev->dev;
obj->ctx = (void *)obj + sizeof(*obj);
- obj->da_start = pdata->da_start;
- obj->da_end = pdata->da_end;
spin_lock_init(&obj->iommu_lock);
mutex_init(&obj->mmap_lock);
@@ -958,33 +982,18 @@ static int omap_iommu_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&obj->mmap);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto err_mem;
- }
-
- res = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!res) {
- err = -EIO;
- goto err_mem;
- }
-
- obj->regbase = ioremap(res->start, resource_size(res));
- if (!obj->regbase) {
- err = -ENOMEM;
- goto err_ioremap;
- }
+ obj->regbase = devm_ioremap_resource(obj->dev, res);
+ if (IS_ERR(obj->regbase))
+ return PTR_ERR(obj->regbase);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = -ENODEV;
- goto err_irq;
- }
- err = request_irq(irq, iommu_fault_handler, IRQF_SHARED,
- dev_name(&pdev->dev), obj);
+ if (irq < 0)
+ return -ENODEV;
+
+ err = devm_request_irq(obj->dev, irq, iommu_fault_handler, IRQF_SHARED,
+ dev_name(obj->dev), obj);
if (err < 0)
- goto err_irq;
+ return err;
platform_set_drvdata(pdev, obj);
pm_runtime_irq_safe(obj->dev);
@@ -992,42 +1001,34 @@ static int omap_iommu_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s registered\n", obj->name);
return 0;
-
-err_irq:
- iounmap(obj->regbase);
-err_ioremap:
- release_mem_region(res->start, resource_size(res));
-err_mem:
- kfree(obj);
- return err;
}
static int omap_iommu_remove(struct platform_device *pdev)
{
- int irq;
- struct resource *res;
struct omap_iommu *obj = platform_get_drvdata(pdev);
iopgtable_clear_entry_all(obj);
- irq = platform_get_irq(pdev, 0);
- free_irq(irq, obj);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- iounmap(obj->regbase);
-
pm_runtime_disable(obj->dev);
dev_info(&pdev->dev, "%s removed\n", obj->name);
- kfree(obj);
return 0;
}
+static struct of_device_id omap_iommu_of_match[] = {
+ { .compatible = "ti,omap2-iommu" },
+ { .compatible = "ti,omap4-iommu" },
+ { .compatible = "ti,dra7-iommu" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_iommu_of_match);
+
static struct platform_driver omap_iommu_driver = {
.probe = omap_iommu_probe,
.remove = omap_iommu_remove,
.driver = {
.name = "omap-iommu",
+ .of_match_table = of_match_ptr(omap_iommu_of_match),
},
};
@@ -1253,6 +1254,49 @@ static int omap_iommu_domain_has_cap(struct iommu_domain *domain,
return 0;
}
+static int omap_iommu_add_device(struct device *dev)
+{
+ struct omap_iommu_arch_data *arch_data;
+ struct device_node *np;
+
+ /*
+ * Allocate the archdata iommu structure for DT-based devices.
+ *
+ * TODO: Simplify this when removing non-DT support completely from the
+ * IOMMU users.
+ */
+ if (!dev->of_node)
+ return 0;
+
+ np = of_parse_phandle(dev->of_node, "iommus", 0);
+ if (!np)
+ return 0;
+
+ arch_data = kzalloc(sizeof(*arch_data), GFP_KERNEL);
+ if (!arch_data) {
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ arch_data->name = kstrdup(dev_name(dev), GFP_KERNEL);
+ dev->archdata.iommu = arch_data;
+
+ of_node_put(np);
+
+ return 0;
+}
+
+static void omap_iommu_remove_device(struct device *dev)
+{
+ struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
+
+ if (!dev->of_node || !arch_data)
+ return;
+
+ kfree(arch_data->name);
+ kfree(arch_data);
+}
+
static struct iommu_ops omap_iommu_ops = {
.domain_init = omap_iommu_domain_init,
.domain_destroy = omap_iommu_domain_destroy,
@@ -1262,6 +1306,8 @@ static struct iommu_ops omap_iommu_ops = {
.unmap = omap_iommu_unmap,
.iova_to_phys = omap_iommu_iova_to_phys,
.domain_has_cap = omap_iommu_domain_has_cap,
+ .add_device = omap_iommu_add_device,
+ .remove_device = omap_iommu_remove_device,
.pgsize_bitmap = OMAP_IOMMU_PGSIZES,
};
diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h
index 120084206602..ea920c3e94ff 100644
--- a/drivers/iommu/omap-iommu.h
+++ b/drivers/iommu/omap-iommu.h
@@ -52,6 +52,8 @@ struct omap_iommu {
void *ctx; /* iommu context: registres saved area */
u32 da_start;
u32 da_end;
+
+ int has_bus_err_back;
};
struct cr_regs {
@@ -130,6 +132,7 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
#define MMU_READ_CAM 0x68
#define MMU_READ_RAM 0x6c
#define MMU_EMU_FAULT_AD 0x70
+#define MMU_GP_REG 0x88
#define MMU_REG_SIZE 256
@@ -163,6 +166,8 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
#define MMU_RAM_MIXED_MASK (1 << MMU_RAM_MIXED_SHIFT)
#define MMU_RAM_MIXED MMU_RAM_MIXED_MASK
+#define MMU_GP_REG_BUS_ERR_BACK_EN 0x1
+
/*
* utilities for super page(16MB, 1MB, 64KB and 4KB)
*/
diff --git a/drivers/iommu/omap-iommu2.c b/drivers/iommu/omap-iommu2.c
index d745094a69dd..5e1ea3b0bf16 100644
--- a/drivers/iommu/omap-iommu2.c
+++ b/drivers/iommu/omap-iommu2.c
@@ -98,6 +98,9 @@ static int omap2_iommu_enable(struct omap_iommu *obj)
iommu_write_reg(obj, pa, MMU_TTB);
+ if (obj->has_bus_err_back)
+ iommu_write_reg(obj, MMU_GP_REG_BUS_ERR_BACK_EN, MMU_GP_REG);
+
__iommu_set_twl(obj, true);
return 0;
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 61ffdca96e25..d770f7406631 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -39,6 +39,14 @@ config IMGPDC_IRQ
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
+config CLPS711X_IRQCHIP
+ bool
+ depends on ARCH_CLPS711X
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+ select SPARSE_IRQ
+ default y
+
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
@@ -69,3 +77,11 @@ config VERSATILE_FPGA_IRQ_NR
config XTENSA_MX
bool
select IRQ_DOMAIN
+
+config IRQ_CROSSBAR
+ bool
+ help
+ Support for a CROSSBAR ip that preceeds the main interrupt controller.
+ The primary irqchip invokes the crossbar's callback which inturn allocates
+ a free irq and configures the IP. Thus the peripheral interrupts are
+ routed to one of the free irqchip interrupt lines.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 1c0c151d108c..f180f8d5fb7b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
+obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
@@ -27,3 +28,4 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
+obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 40e6440348ff..f8636a650cf6 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -17,7 +17,6 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <asm/mach/irq.h>
#include "irqchip.h"
@@ -81,7 +80,7 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
if (unlikely(!cascade_irq))
- do_bad_IRQ(irq, desc);
+ handle_bad_irq(irq, desc);
else
generic_handle_irq(cascade_irq);
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
new file mode 100644
index 000000000000..33340dc97d1d
--- /dev/null
+++ b/drivers/irqchip/irq-clps711x.c
@@ -0,0 +1,243 @@
+/*
+ * CLPS711X IRQ driver
+ *
+ * Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+#define CLPS711X_INTSR1 (0x0240)
+#define CLPS711X_INTMR1 (0x0280)
+#define CLPS711X_BLEOI (0x0600)
+#define CLPS711X_MCEOI (0x0640)
+#define CLPS711X_TEOI (0x0680)
+#define CLPS711X_TC1EOI (0x06c0)
+#define CLPS711X_TC2EOI (0x0700)
+#define CLPS711X_RTCEOI (0x0740)
+#define CLPS711X_UMSEOI (0x0780)
+#define CLPS711X_COEOI (0x07c0)
+#define CLPS711X_INTSR2 (0x1240)
+#define CLPS711X_INTMR2 (0x1280)
+#define CLPS711X_SRXEOF (0x1600)
+#define CLPS711X_KBDEOI (0x1700)
+#define CLPS711X_INTSR3 (0x2240)
+#define CLPS711X_INTMR3 (0x2280)
+
+static const struct {
+#define CLPS711X_FLAG_EN (1 << 0)
+#define CLPS711X_FLAG_FIQ (1 << 1)
+ unsigned int flags;
+ phys_addr_t eoi;
+} clps711x_irqs[] = {
+ [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
+ [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
+ [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
+ [5] = { CLPS711X_FLAG_EN, },
+ [6] = { CLPS711X_FLAG_EN, },
+ [7] = { CLPS711X_FLAG_EN, },
+ [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
+ [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
+ [10] = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
+ [11] = { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
+ [12] = { CLPS711X_FLAG_EN, },
+ [13] = { CLPS711X_FLAG_EN, },
+ [14] = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
+ [15] = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
+ [16] = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
+ [17] = { CLPS711X_FLAG_EN, },
+ [18] = { CLPS711X_FLAG_EN, },
+ [28] = { CLPS711X_FLAG_EN, },
+ [29] = { CLPS711X_FLAG_EN, },
+ [32] = { CLPS711X_FLAG_FIQ, },
+};
+
+static struct {
+ void __iomem *base;
+ void __iomem *intmr[3];
+ void __iomem *intsr[3];
+ struct irq_domain *domain;
+ struct irq_domain_ops ops;
+} *clps711x_intc;
+
+static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
+{
+ u32 irqnr, irqstat;
+
+ do {
+ irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
+ readw_relaxed(clps711x_intc->intsr[0]);
+ if (irqstat) {
+ irqnr = irq_find_mapping(clps711x_intc->domain,
+ fls(irqstat) - 1);
+ handle_IRQ(irqnr, regs);
+ }
+
+ irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
+ readw_relaxed(clps711x_intc->intsr[1]);
+ if (irqstat) {
+ irqnr = irq_find_mapping(clps711x_intc->domain,
+ fls(irqstat) - 1 + 16);
+ handle_IRQ(irqnr, regs);
+ }
+ } while (irqstat);
+}
+
+static void clps711x_intc_eoi(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi);
+}
+
+static void clps711x_intc_mask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
+ u32 tmp;
+
+ tmp = readl_relaxed(intmr);
+ tmp &= ~(1 << (hwirq % 16));
+ writel_relaxed(tmp, intmr);
+}
+
+static void clps711x_intc_unmask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
+ u32 tmp;
+
+ tmp = readl_relaxed(intmr);
+ tmp |= 1 << (hwirq % 16);
+ writel_relaxed(tmp, intmr);
+}
+
+static struct irq_chip clps711x_intc_chip = {
+ .name = "clps711x-intc",
+ .irq_eoi = clps711x_intc_eoi,
+ .irq_mask = clps711x_intc_mask,
+ .irq_unmask = clps711x_intc_unmask,
+};
+
+static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_flow_handler_t handler = handle_level_irq;
+ unsigned int flags = IRQF_VALID | IRQF_PROBE;
+
+ if (!clps711x_irqs[hw].flags)
+ return 0;
+
+ if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
+ handler = handle_bad_irq;
+ flags |= IRQF_NOAUTOEN;
+ } else if (clps711x_irqs[hw].eoi) {
+ handler = handle_fasteoi_irq;
+ }
+
+ /* Clear down pending interrupt */
+ if (clps711x_irqs[hw].eoi)
+ writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
+
+ irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
+ set_irq_flags(virq, flags);
+
+ return 0;
+}
+
+static int __init _clps711x_intc_init(struct device_node *np,
+ phys_addr_t base, resource_size_t size)
+{
+ int err;
+
+ clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
+ if (!clps711x_intc)
+ return -ENOMEM;
+
+ clps711x_intc->base = ioremap(base, size);
+ if (!clps711x_intc->base) {
+ err = -ENOMEM;
+ goto out_kfree;
+ }
+
+ clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1;
+ clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1;
+ clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2;
+ clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2;
+ clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3;
+ clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3;
+
+ /* Mask all interrupts */
+ writel_relaxed(0, clps711x_intc->intmr[0]);
+ writel_relaxed(0, clps711x_intc->intmr[1]);
+ writel_relaxed(0, clps711x_intc->intmr[2]);
+
+ err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id());
+ if (IS_ERR_VALUE(err))
+ goto out_iounmap;
+
+ clps711x_intc->ops.map = clps711x_intc_irq_map;
+ clps711x_intc->ops.xlate = irq_domain_xlate_onecell;
+ clps711x_intc->domain =
+ irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
+ 0, 0, &clps711x_intc->ops, NULL);
+ if (!clps711x_intc->domain) {
+ err = -ENOMEM;
+ goto out_irqfree;
+ }
+
+ irq_set_default_host(clps711x_intc->domain);
+ set_handle_irq(clps711x_irqh);
+
+#ifdef CONFIG_FIQ
+ init_FIQ(0);
+#endif
+
+ return 0;
+
+out_irqfree:
+ irq_free_descs(0, ARRAY_SIZE(clps711x_irqs));
+
+out_iounmap:
+ iounmap(clps711x_intc->base);
+
+out_kfree:
+ kfree(clps711x_intc);
+
+ return err;
+}
+
+void __init clps711x_intc_init(phys_addr_t base, resource_size_t size)
+{
+ BUG_ON(_clps711x_intc_init(NULL, base, size));
+}
+
+#ifdef CONFIG_IRQCHIP
+static int __init clps711x_intc_init_dt(struct device_node *np,
+ struct device_node *parent)
+{
+ struct resource res;
+ int err;
+
+ err = of_address_to_resource(np, 0, &res);
+ if (err)
+ return err;
+
+ return _clps711x_intc_init(np, res.start, resource_size(&res));
+}
+IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt);
+#endif
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
new file mode 100644
index 000000000000..fc817d28d1fe
--- /dev/null
+++ b/drivers/irqchip/irq-crossbar.c
@@ -0,0 +1,208 @@
+/*
+ * drivers/irqchip/irq-crossbar.c
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Sricharan R <r.sricharan@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/irqchip/arm-gic.h>
+
+#define IRQ_FREE -1
+#define GIC_IRQ_START 32
+
+/*
+ * @int_max: maximum number of supported interrupts
+ * @irq_map: array of interrupts to crossbar number mapping
+ * @crossbar_base: crossbar base address
+ * @register_offsets: offsets for each irq number
+ */
+struct crossbar_device {
+ uint int_max;
+ uint *irq_map;
+ void __iomem *crossbar_base;
+ int *register_offsets;
+ void (*write) (int, int);
+};
+
+static struct crossbar_device *cb;
+
+static inline void crossbar_writel(int irq_no, int cb_no)
+{
+ writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
+}
+
+static inline void crossbar_writew(int irq_no, int cb_no)
+{
+ writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
+}
+
+static inline void crossbar_writeb(int irq_no, int cb_no)
+{
+ writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
+}
+
+static inline int allocate_free_irq(int cb_no)
+{
+ int i;
+
+ for (i = 0; i < cb->int_max; i++) {
+ if (cb->irq_map[i] == IRQ_FREE) {
+ cb->irq_map[i] = cb_no;
+ return i;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
+ return 0;
+}
+
+static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
+
+ if (hw > GIC_IRQ_START)
+ cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
+}
+
+static int crossbar_domain_xlate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ unsigned long ret;
+
+ ret = allocate_free_irq(intspec[1]);
+
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ *out_hwirq = ret + GIC_IRQ_START;
+ return 0;
+}
+
+const struct irq_domain_ops routable_irq_domain_ops = {
+ .map = crossbar_domain_map,
+ .unmap = crossbar_domain_unmap,
+ .xlate = crossbar_domain_xlate
+};
+
+static int __init crossbar_of_init(struct device_node *node)
+{
+ int i, size, max, reserved = 0, entry;
+ const __be32 *irqsr;
+
+ cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL);
+
+ if (!cb)
+ return -ENOMEM;
+
+ cb->crossbar_base = of_iomap(node, 0);
+ if (!cb->crossbar_base)
+ goto err1;
+
+ of_property_read_u32(node, "ti,max-irqs", &max);
+ cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL);
+ if (!cb->irq_map)
+ goto err2;
+
+ cb->int_max = max;
+
+ for (i = 0; i < max; i++)
+ cb->irq_map[i] = IRQ_FREE;
+
+ /* Get and mark reserved irqs */
+ irqsr = of_get_property(node, "ti,irqs-reserved", &size);
+ if (irqsr) {
+ size /= sizeof(__be32);
+
+ for (i = 0; i < size; i++) {
+ of_property_read_u32_index(node,
+ "ti,irqs-reserved",
+ i, &entry);
+ if (entry > max) {
+ pr_err("Invalid reserved entry\n");
+ goto err3;
+ }
+ cb->irq_map[entry] = 0;
+ }
+ }
+
+ cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
+ if (!cb->register_offsets)
+ goto err3;
+
+ of_property_read_u32(node, "ti,reg-size", &size);
+
+ switch (size) {
+ case 1:
+ cb->write = crossbar_writeb;
+ break;
+ case 2:
+ cb->write = crossbar_writew;
+ break;
+ case 4:
+ cb->write = crossbar_writel;
+ break;
+ default:
+ pr_err("Invalid reg-size property\n");
+ goto err4;
+ break;
+ }
+
+ /*
+ * Register offsets are not linear because of the
+ * reserved irqs. so find and store the offsets once.
+ */
+ for (i = 0; i < max; i++) {
+ if (!cb->irq_map[i])
+ continue;
+
+ cb->register_offsets[i] = reserved;
+ reserved += size;
+ }
+
+ register_routable_domain_ops(&routable_irq_domain_ops);
+ return 0;
+
+err4:
+ kfree(cb->register_offsets);
+err3:
+ kfree(cb->irq_map);
+err2:
+ iounmap(cb->crossbar_base);
+err1:
+ kfree(cb);
+ return -ENOMEM;
+}
+
+static const struct of_device_id crossbar_match[] __initconst = {
+ { .compatible = "ti,irq-crossbar" },
+ {}
+};
+
+int __init irqcrossbar_init(void)
+{
+ struct device_node *np;
+ np = of_find_matching_node(NULL, crossbar_match);
+ if (!np)
+ return -ENODEV;
+
+ crossbar_of_init(np);
+ return 0;
+}
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 531769b2433a..4300b6606f5e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -661,9 +661,9 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
/*
* Ensure that stores to Normal memory are visible to the
- * other CPUs before issuing the IPI.
+ * other CPUs before they observe us issuing the IPI.
*/
- dsb();
+ dmb(ishst);
/* this always happens on GIC0 */
writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
@@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+
+ gic_routable_irq_domain_ops->map(d, irq, hw);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
+static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ gic_routable_irq_domain_ops->unmap(d, irq);
+}
+
static int gic_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
+ unsigned long ret = 0;
+
if (d->of_node != controller)
return -EINVAL;
if (intsize < 3)
@@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
*out_hwirq = intspec[1] + 16;
/* For SPIs, we need to add 16 more to get the GIC irq ID number */
- if (!intspec[0])
- *out_hwirq += 16;
+ if (!intspec[0]) {
+ ret = gic_routable_irq_domain_ops->xlate(d, controller,
+ intspec,
+ intsize,
+ out_hwirq,
+ out_type);
+
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ }
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
- return 0;
+
+ return ret;
}
#ifdef CONFIG_SMP
@@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = {
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
+ .unmap = gic_irq_domain_unmap,
.xlate = gic_irq_domain_xlate,
};
+/* Default functions for routable irq domain */
+static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ return 0;
+}
+
+static void gic_routable_irq_domain_unmap(struct irq_domain *d,
+ unsigned int irq)
+{
+}
+
+static int gic_routable_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ *out_hwirq += 16;
+ return 0;
+}
+
+const struct irq_domain_ops gic_default_routable_irq_domain_ops = {
+ .map = gic_routable_irq_domain_map,
+ .unmap = gic_routable_irq_domain_unmap,
+ .xlate = gic_routable_irq_domain_xlate,
+};
+
+const struct irq_domain_ops *gic_routable_irq_domain_ops =
+ &gic_default_routable_irq_domain_ops;
+
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node)
@@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
+ int nr_routable_irqs;
BUG_ON(gic_nr >= MAX_GIC_NR);
@@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic->gic_irqs = gic_irqs;
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
- irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
- if (IS_ERR_VALUE(irq_base)) {
- WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
- irq_start);
- irq_base = irq_start;
+
+ if (of_property_read_u32(node, "arm,routable-irqs",
+ &nr_routable_irqs)) {
+ irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
+ numa_node_id());
+ if (IS_ERR_VALUE(irq_base)) {
+ WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
+ irq_start);
+ irq_base = irq_start;
+ }
+
+ gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
+ hwirq_base, &gic_irq_domain_ops, gic);
+ } else {
+ gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
+ &gic_irq_domain_ops,
+ gic);
}
- gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
- hwirq_base, &gic_irq_domain_ops, gic);
+
if (WARN_ON(!gic->domain))
return;
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
index 3c8827fe83f3..1c3e2c9b46ba 100644
--- a/drivers/irqchip/irq-mmp.c
+++ b/drivers/irqchip/irq-mmp.c
@@ -22,7 +22,7 @@
#include <linux/of_irq.h>
#include <asm/exception.h>
-#include <asm/mach/irq.h>
+#include <asm/hardirq.h>
#include "irqchip.h"
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c
index 473f09a74d4d..37dab0b472cd 100644
--- a/drivers/irqchip/irq-vic.c
+++ b/drivers/irqchip/irq-vic.c
@@ -57,6 +57,7 @@
/**
* struct vic_device - VIC PM device
+ * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0.
* @irq: The IRQ number for the base of the VIC.
* @base: The register base for the VIC.
* @valid_sources: A bitmask of valid interrupts
@@ -224,6 +225,17 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
return handled;
}
+static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc)
+{
+ u32 stat, hwirq;
+ struct vic_device *vic = irq_desc_get_handler_data(desc);
+
+ while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
+ hwirq = ffs(stat) - 1;
+ generic_handle_irq(irq_find_mapping(vic->domain, hwirq));
+ }
+}
+
/*
* Keep iterating over all registered VIC's until there are no pending
* interrupts.
@@ -246,6 +258,7 @@ static struct irq_domain_ops vic_irqdomain_ops = {
/**
* vic_register() - Register a VIC.
* @base: The base address of the VIC.
+ * @parent_irq: The parent IRQ if cascaded, else 0.
* @irq: The base IRQ for the VIC.
* @valid_sources: bitmask of valid interrupts
* @resume_sources: bitmask of interrupts allowed for resume sources.
@@ -257,7 +270,8 @@ static struct irq_domain_ops vic_irqdomain_ops = {
*
* This also configures the IRQ domain for the VIC.
*/
-static void __init vic_register(void __iomem *base, unsigned int irq,
+static void __init vic_register(void __iomem *base, unsigned int parent_irq,
+ unsigned int irq,
u32 valid_sources, u32 resume_sources,
struct device_node *node)
{
@@ -273,15 +287,25 @@ static void __init vic_register(void __iomem *base, unsigned int irq,
v->base = base;
v->valid_sources = valid_sources;
v->resume_sources = resume_sources;
- v->irq = irq;
set_handle_irq(vic_handle_irq);
vic_id++;
+
+ if (parent_irq) {
+ irq_set_handler_data(parent_irq, v);
+ irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded);
+ }
+
v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
&vic_irqdomain_ops, v);
/* create an IRQ mapping for each valid IRQ */
for (i = 0; i < fls(valid_sources); i++)
if (valid_sources & (1 << i))
irq_create_mapping(v->domain, i);
+ /* If no base IRQ was passed, figure out our allocated base */
+ if (irq)
+ v->irq = irq;
+ else
+ v->irq = irq_find_mapping(v->domain, 0);
}
static void vic_ack_irq(struct irq_data *d)
@@ -409,10 +433,10 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
writel(32, base + VIC_PL190_DEF_VECT_ADDR);
}
- vic_register(base, irq_start, vic_sources, 0, node);
+ vic_register(base, 0, irq_start, vic_sources, 0, node);
}
-void __init __vic_init(void __iomem *base, int irq_start,
+void __init __vic_init(void __iomem *base, int parent_irq, int irq_start,
u32 vic_sources, u32 resume_sources,
struct device_node *node)
{
@@ -449,7 +473,7 @@ void __init __vic_init(void __iomem *base, int irq_start,
vic_init2(base);
- vic_register(base, irq_start, vic_sources, resume_sources, node);
+ vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node);
}
/**
@@ -462,8 +486,30 @@ void __init __vic_init(void __iomem *base, int irq_start,
void __init vic_init(void __iomem *base, unsigned int irq_start,
u32 vic_sources, u32 resume_sources)
{
- __vic_init(base, irq_start, vic_sources, resume_sources, NULL);
+ __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL);
+}
+
+/**
+ * vic_init_cascaded() - initialise a cascaded vectored interrupt controller
+ * @base: iomem base address
+ * @parent_irq: the parent IRQ we're cascaded off
+ * @irq_start: starting interrupt number, must be muliple of 32
+ * @vic_sources: bitmask of interrupt sources to allow
+ * @resume_sources: bitmask of interrupt sources to allow for resume
+ *
+ * This returns the base for the new interrupts or negative on error.
+ */
+int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq,
+ u32 vic_sources, u32 resume_sources)
+{
+ struct vic_device *v;
+
+ v = &vic_devices[vic_id];
+ __vic_init(base, parent_irq, 0, vic_sources, resume_sources, NULL);
+ /* Return out acquired base */
+ return v->irq;
}
+EXPORT_SYMBOL_GPL(vic_init_cascaded);
#ifdef CONFIG_OF
int __init vic_of_init(struct device_node *node, struct device_node *parent)
@@ -485,7 +531,7 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent)
/*
* Passing 0 as first IRQ makes the simple domain allocate descriptors
*/
- __vic_init(regs, 0, interrupt_mask, wakeup_mask, node);
+ __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);
return 0;
}
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 72156c123033..44c358ecf5a1 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -421,7 +421,7 @@ config LEDS_MC13783
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
depends on LEDS_CLASS
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
default y
help
This option enable support for the dual-GPIO LED found on the
@@ -431,7 +431,7 @@ config LEDS_NS2
config LEDS_NETXBIG
tristate "LED support for Big Network series LEDs"
depends on LEDS_CLASS
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
default y
help
This option enable support for LEDs found on the LaCie 2Big
diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c
index bfb39bb56ef1..e8b55c3a6170 100644
--- a/drivers/lguest/page_tables.c
+++ b/drivers/lguest/page_tables.c
@@ -887,7 +887,7 @@ void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable)
* _PAGE_ACCESSED then we can put a read-only PTE entry in immediately, and if
* they set _PAGE_DIRTY then we can put a writable PTE entry in immediately.
*/
-static void do_set_pte(struct lg_cpu *cpu, int idx,
+static void __guest_set_pte(struct lg_cpu *cpu, int idx,
unsigned long vaddr, pte_t gpte)
{
/* Look up the matching shadow page directory entry. */
@@ -960,13 +960,13 @@ void guest_set_pte(struct lg_cpu *cpu,
unsigned int i;
for (i = 0; i < ARRAY_SIZE(cpu->lg->pgdirs); i++)
if (cpu->lg->pgdirs[i].pgdir)
- do_set_pte(cpu, i, vaddr, gpte);
+ __guest_set_pte(cpu, i, vaddr, gpte);
} else {
/* Is this page table one we have a shadow for? */
int pgdir = find_pgdir(cpu->lg, gpgdir);
if (pgdir != ARRAY_SIZE(cpu->lg->pgdirs))
/* If so, do the update. */
- do_set_pte(cpu, pgdir, vaddr, gpte);
+ __guest_set_pte(cpu, pgdir, vaddr, gpte);
}
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 95ad936e6048..5bdedf6df153 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -285,6 +285,17 @@ config DM_CACHE_CLEANER
A simple cache policy that writes back all data to the
origin. Used when decommissioning a dm-cache.
+config DM_ERA
+ tristate "Era target (EXPERIMENTAL)"
+ depends on BLK_DEV_DM
+ default n
+ select DM_PERSISTENT_DATA
+ select DM_BIO_PRISON
+ ---help---
+ dm-era tracks which parts of a block device are written to
+ over time. Useful for maintaining cache coherency when using
+ vendor snapshots.
+
config DM_MIRROR
tristate "Mirror target"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index f26d83292579..a2da532b1c2b 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -14,6 +14,7 @@ dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o
dm-cache-mq-y += dm-cache-policy-mq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
+dm-era-y += dm-era-target.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o
@@ -53,6 +54,7 @@ obj-$(CONFIG_DM_VERITY) += dm-verity.o
obj-$(CONFIG_DM_CACHE) += dm-cache.o
obj-$(CONFIG_DM_CACHE_MQ) += dm-cache-mq.o
obj-$(CONFIG_DM_CACHE_CLEANER) += dm-cache-cleaner.o
+obj-$(CONFIG_DM_ERA) += dm-era.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
diff --git a/drivers/md/dm-cache-block-types.h b/drivers/md/dm-cache-block-types.h
index bed4ad4e1b7c..aac0e2df06be 100644
--- a/drivers/md/dm-cache-block-types.h
+++ b/drivers/md/dm-cache-block-types.h
@@ -19,7 +19,6 @@
typedef dm_block_t __bitwise__ dm_oblock_t;
typedef uint32_t __bitwise__ dm_cblock_t;
-typedef dm_block_t __bitwise__ dm_dblock_t;
static inline dm_oblock_t to_oblock(dm_block_t b)
{
@@ -41,14 +40,4 @@ static inline uint32_t from_cblock(dm_cblock_t b)
return (__force uint32_t) b;
}
-static inline dm_dblock_t to_dblock(dm_block_t b)
-{
- return (__force dm_dblock_t) b;
-}
-
-static inline dm_block_t from_dblock(dm_dblock_t b)
-{
- return (__force dm_block_t) b;
-}
-
#endif /* DM_CACHE_BLOCK_TYPES_H */
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 9ef0752e8a08..4ead4ba60656 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -109,7 +109,7 @@ struct dm_cache_metadata {
dm_block_t discard_root;
sector_t discard_block_size;
- dm_dblock_t discard_nr_blocks;
+ dm_oblock_t discard_nr_blocks;
sector_t data_block_size;
dm_cblock_t cache_blocks;
@@ -120,6 +120,12 @@ struct dm_cache_metadata {
unsigned policy_version[CACHE_POLICY_VERSION_SIZE];
size_t policy_hint_size;
struct dm_cache_statistics stats;
+
+ /*
+ * Reading the space map root can fail, so we read it into this
+ * buffer before the superblock is locked and updated.
+ */
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
};
/*-------------------------------------------------------------------
@@ -260,11 +266,31 @@ static void __setup_mapping_info(struct dm_cache_metadata *cmd)
}
}
+static int __save_sm_root(struct dm_cache_metadata *cmd)
+{
+ int r;
+ size_t metadata_len;
+
+ r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
+ if (r < 0)
+ return r;
+
+ return dm_sm_copy_root(cmd->metadata_sm, &cmd->metadata_space_map_root,
+ metadata_len);
+}
+
+static void __copy_sm_root(struct dm_cache_metadata *cmd,
+ struct cache_disk_superblock *disk_super)
+{
+ memcpy(&disk_super->metadata_space_map_root,
+ &cmd->metadata_space_map_root,
+ sizeof(cmd->metadata_space_map_root));
+}
+
static int __write_initial_superblock(struct dm_cache_metadata *cmd)
{
int r;
struct dm_block *sblock;
- size_t metadata_len;
struct cache_disk_superblock *disk_super;
sector_t bdev_size = i_size_read(cmd->bdev->bd_inode) >> SECTOR_SHIFT;
@@ -272,12 +298,16 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)
if (bdev_size > DM_CACHE_METADATA_MAX_SECTORS)
bdev_size = DM_CACHE_METADATA_MAX_SECTORS;
- r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
+ r = dm_tm_pre_commit(cmd->tm);
if (r < 0)
return r;
- r = dm_tm_pre_commit(cmd->tm);
- if (r < 0)
+ /*
+ * dm_sm_copy_root() can fail. So we need to do it before we start
+ * updating the superblock.
+ */
+ r = __save_sm_root(cmd);
+ if (r)
return r;
r = superblock_lock_zero(cmd, &sblock);
@@ -293,16 +323,13 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)
memset(disk_super->policy_version, 0, sizeof(disk_super->policy_version));
disk_super->policy_hint_size = 0;
- r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0)
- goto bad_locked;
+ __copy_sm_root(cmd, disk_super);
disk_super->mapping_root = cpu_to_le64(cmd->root);
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
- disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
+ disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks));
disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
disk_super->data_block_size = cpu_to_le32(cmd->data_block_size);
disk_super->cache_blocks = cpu_to_le32(0);
@@ -313,10 +340,6 @@ static int __write_initial_superblock(struct dm_cache_metadata *cmd)
disk_super->write_misses = cpu_to_le32(0);
return dm_tm_commit(cmd->tm, sblock);
-
-bad_locked:
- dm_bm_unlock(sblock);
- return r;
}
static int __format_metadata(struct dm_cache_metadata *cmd)
@@ -496,7 +519,7 @@ static void read_superblock_fields(struct dm_cache_metadata *cmd,
cmd->hint_root = le64_to_cpu(disk_super->hint_root);
cmd->discard_root = le64_to_cpu(disk_super->discard_root);
cmd->discard_block_size = le64_to_cpu(disk_super->discard_block_size);
- cmd->discard_nr_blocks = to_dblock(le64_to_cpu(disk_super->discard_nr_blocks));
+ cmd->discard_nr_blocks = to_oblock(le64_to_cpu(disk_super->discard_nr_blocks));
cmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
cmd->cache_blocks = to_cblock(le32_to_cpu(disk_super->cache_blocks));
strncpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name));
@@ -530,8 +553,9 @@ static int __begin_transaction_flags(struct dm_cache_metadata *cmd,
disk_super = dm_block_data(sblock);
update_flags(disk_super, mutator);
read_superblock_fields(cmd, disk_super);
+ dm_bm_unlock(sblock);
- return dm_bm_flush_and_unlock(cmd->bm, sblock);
+ return dm_bm_flush(cmd->bm);
}
static int __begin_transaction(struct dm_cache_metadata *cmd)
@@ -559,7 +583,6 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
flags_mutator mutator)
{
int r;
- size_t metadata_len;
struct cache_disk_superblock *disk_super;
struct dm_block *sblock;
@@ -577,8 +600,8 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
if (r < 0)
return r;
- r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
- if (r < 0)
+ r = __save_sm_root(cmd);
+ if (r)
return r;
r = superblock_lock(cmd, &sblock);
@@ -594,7 +617,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
- disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
+ disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks));
disk_super->cache_blocks = cpu_to_le32(from_cblock(cmd->cache_blocks));
strncpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name));
disk_super->policy_version[0] = cpu_to_le32(cmd->policy_version[0]);
@@ -605,13 +628,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
disk_super->read_misses = cpu_to_le32(cmd->stats.read_misses);
disk_super->write_hits = cpu_to_le32(cmd->stats.write_hits);
disk_super->write_misses = cpu_to_le32(cmd->stats.write_misses);
-
- r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0) {
- dm_bm_unlock(sblock);
- return r;
- }
+ __copy_sm_root(cmd, disk_super);
return dm_tm_commit(cmd->tm, sblock);
}
@@ -771,15 +788,15 @@ out:
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
- dm_dblock_t new_nr_entries)
+ dm_oblock_t new_nr_entries)
{
int r;
down_write(&cmd->root_lock);
r = dm_bitset_resize(&cmd->discard_info,
cmd->discard_root,
- from_dblock(cmd->discard_nr_blocks),
- from_dblock(new_nr_entries),
+ from_oblock(cmd->discard_nr_blocks),
+ from_oblock(new_nr_entries),
false, &cmd->discard_root);
if (!r) {
cmd->discard_block_size = discard_block_size;
@@ -792,28 +809,28 @@ int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
return r;
}
-static int __set_discard(struct dm_cache_metadata *cmd, dm_dblock_t b)
+static int __set_discard(struct dm_cache_metadata *cmd, dm_oblock_t b)
{
return dm_bitset_set_bit(&cmd->discard_info, cmd->discard_root,
- from_dblock(b), &cmd->discard_root);
+ from_oblock(b), &cmd->discard_root);
}
-static int __clear_discard(struct dm_cache_metadata *cmd, dm_dblock_t b)
+static int __clear_discard(struct dm_cache_metadata *cmd, dm_oblock_t b)
{
return dm_bitset_clear_bit(&cmd->discard_info, cmd->discard_root,
- from_dblock(b), &cmd->discard_root);
+ from_oblock(b), &cmd->discard_root);
}
-static int __is_discarded(struct dm_cache_metadata *cmd, dm_dblock_t b,
+static int __is_discarded(struct dm_cache_metadata *cmd, dm_oblock_t b,
bool *is_discarded)
{
return dm_bitset_test_bit(&cmd->discard_info, cmd->discard_root,
- from_dblock(b), &cmd->discard_root,
+ from_oblock(b), &cmd->discard_root,
is_discarded);
}
static int __discard(struct dm_cache_metadata *cmd,
- dm_dblock_t dblock, bool discard)
+ dm_oblock_t dblock, bool discard)
{
int r;
@@ -826,7 +843,7 @@ static int __discard(struct dm_cache_metadata *cmd,
}
int dm_cache_set_discard(struct dm_cache_metadata *cmd,
- dm_dblock_t dblock, bool discard)
+ dm_oblock_t dblock, bool discard)
{
int r;
@@ -844,8 +861,8 @@ static int __load_discards(struct dm_cache_metadata *cmd,
dm_block_t b;
bool discard;
- for (b = 0; b < from_dblock(cmd->discard_nr_blocks); b++) {
- dm_dblock_t dblock = to_dblock(b);
+ for (b = 0; b < from_oblock(cmd->discard_nr_blocks); b++) {
+ dm_oblock_t dblock = to_oblock(b);
if (cmd->clean_when_opened) {
r = __is_discarded(cmd, dblock, &discard);
@@ -1228,22 +1245,12 @@ static int begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *po
return 0;
}
-int dm_cache_begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy)
+static int save_hint(void *context, dm_cblock_t cblock, dm_oblock_t oblock, uint32_t hint)
{
+ struct dm_cache_metadata *cmd = context;
+ __le32 value = cpu_to_le32(hint);
int r;
- down_write(&cmd->root_lock);
- r = begin_hints(cmd, policy);
- up_write(&cmd->root_lock);
-
- return r;
-}
-
-static int save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock,
- uint32_t hint)
-{
- int r;
- __le32 value = cpu_to_le32(hint);
__dm_bless_for_disk(&value);
r = dm_array_set_value(&cmd->hint_info, cmd->hint_root,
@@ -1253,16 +1260,25 @@ static int save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock,
return r;
}
-int dm_cache_save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock,
- uint32_t hint)
+static int write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy)
{
int r;
- if (!hints_array_initialized(cmd))
- return 0;
+ r = begin_hints(cmd, policy);
+ if (r) {
+ DMERR("begin_hints failed");
+ return r;
+ }
+
+ return policy_walk_mappings(policy, save_hint, cmd);
+}
+
+int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy)
+{
+ int r;
down_write(&cmd->root_lock);
- r = save_hint(cmd, cblock, hint);
+ r = write_hints(cmd, policy);
up_write(&cmd->root_lock);
return r;
diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h
index cd906f14f98d..cd70a78623a3 100644
--- a/drivers/md/dm-cache-metadata.h
+++ b/drivers/md/dm-cache-metadata.h
@@ -72,14 +72,14 @@ dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd);
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
- dm_dblock_t new_nr_entries);
+ dm_oblock_t new_nr_entries);
typedef int (*load_discard_fn)(void *context, sector_t discard_block_size,
- dm_dblock_t dblock, bool discarded);
+ dm_oblock_t dblock, bool discarded);
int dm_cache_load_discards(struct dm_cache_metadata *cmd,
load_discard_fn fn, void *context);
-int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_dblock_t dblock, bool discard);
+int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_oblock_t dblock, bool discard);
int dm_cache_remove_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock);
int dm_cache_insert_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock, dm_oblock_t oblock);
@@ -128,14 +128,7 @@ void dm_cache_dump(struct dm_cache_metadata *cmd);
* rather than querying the policy for each cblock, we let it walk its data
* structures and fill in the hints in whatever order it wishes.
*/
-
-int dm_cache_begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *p);
-
-/*
- * requests hints for every cblock and stores in the metadata device.
- */
-int dm_cache_save_hint(struct dm_cache_metadata *cmd,
- dm_cblock_t cblock, uint32_t hint);
+int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *p);
/*
* Query method. Are all the blocks in the cache clean?
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 074b9c8e4cf0..1bf4a71919ec 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -237,9 +237,8 @@ struct cache {
/*
* origin_blocks entries, discarded if set.
*/
- dm_dblock_t discard_nr_blocks;
+ dm_oblock_t discard_nr_blocks;
unsigned long *discard_bitset;
- uint32_t discard_block_size; /* a power of 2 times sectors per block */
/*
* Rather than reconstructing the table line for the status we just
@@ -526,48 +525,33 @@ static dm_block_t block_div(dm_block_t b, uint32_t n)
return b;
}
-static dm_dblock_t oblock_to_dblock(struct cache *cache, dm_oblock_t oblock)
-{
- uint32_t discard_blocks = cache->discard_block_size;
- dm_block_t b = from_oblock(oblock);
-
- if (!block_size_is_power_of_two(cache))
- discard_blocks = discard_blocks / cache->sectors_per_block;
- else
- discard_blocks >>= cache->sectors_per_block_shift;
-
- b = block_div(b, discard_blocks);
-
- return to_dblock(b);
-}
-
-static void set_discard(struct cache *cache, dm_dblock_t b)
+static void set_discard(struct cache *cache, dm_oblock_t b)
{
unsigned long flags;
atomic_inc(&cache->stats.discard_count);
spin_lock_irqsave(&cache->lock, flags);
- set_bit(from_dblock(b), cache->discard_bitset);
+ set_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
}
-static void clear_discard(struct cache *cache, dm_dblock_t b)
+static void clear_discard(struct cache *cache, dm_oblock_t b)
{
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- clear_bit(from_dblock(b), cache->discard_bitset);
+ clear_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
}
-static bool is_discarded(struct cache *cache, dm_dblock_t b)
+static bool is_discarded(struct cache *cache, dm_oblock_t b)
{
int r;
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- r = test_bit(from_dblock(b), cache->discard_bitset);
+ r = test_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
return r;
@@ -579,8 +563,7 @@ static bool is_discarded_oblock(struct cache *cache, dm_oblock_t b)
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- r = test_bit(from_dblock(oblock_to_dblock(cache, b)),
- cache->discard_bitset);
+ r = test_bit(from_oblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
return r;
@@ -705,7 +688,7 @@ static void remap_to_origin_clear_discard(struct cache *cache, struct bio *bio,
check_if_tick_bio_needed(cache, bio);
remap_to_origin(cache, bio);
if (bio_data_dir(bio) == WRITE)
- clear_discard(cache, oblock_to_dblock(cache, oblock));
+ clear_discard(cache, oblock);
}
static void remap_to_cache_dirty(struct cache *cache, struct bio *bio,
@@ -715,7 +698,7 @@ static void remap_to_cache_dirty(struct cache *cache, struct bio *bio,
remap_to_cache(cache, bio, cblock);
if (bio_data_dir(bio) == WRITE) {
set_dirty(cache, oblock, cblock);
- clear_discard(cache, oblock_to_dblock(cache, oblock));
+ clear_discard(cache, oblock);
}
}
@@ -1288,14 +1271,14 @@ static void process_flush_bio(struct cache *cache, struct bio *bio)
static void process_discard_bio(struct cache *cache, struct bio *bio)
{
dm_block_t start_block = dm_sector_div_up(bio->bi_iter.bi_sector,
- cache->discard_block_size);
+ cache->sectors_per_block);
dm_block_t end_block = bio_end_sector(bio);
dm_block_t b;
- end_block = block_div(end_block, cache->discard_block_size);
+ end_block = block_div(end_block, cache->sectors_per_block);
for (b = start_block; b < end_block; b++)
- set_discard(cache, to_dblock(b));
+ set_discard(cache, to_oblock(b));
bio_endio(bio, 0);
}
@@ -2171,35 +2154,6 @@ static int create_cache_policy(struct cache *cache, struct cache_args *ca,
return 0;
}
-/*
- * We want the discard block size to be a power of two, at least the size
- * of the cache block size, and have no more than 2^14 discard blocks
- * across the origin.
- */
-#define MAX_DISCARD_BLOCKS (1 << 14)
-
-static bool too_many_discard_blocks(sector_t discard_block_size,
- sector_t origin_size)
-{
- (void) sector_div(origin_size, discard_block_size);
-
- return origin_size > MAX_DISCARD_BLOCKS;
-}
-
-static sector_t calculate_discard_block_size(sector_t cache_block_size,
- sector_t origin_size)
-{
- sector_t discard_block_size;
-
- discard_block_size = roundup_pow_of_two(cache_block_size);
-
- if (origin_size)
- while (too_many_discard_blocks(discard_block_size, origin_size))
- discard_block_size *= 2;
-
- return discard_block_size;
-}
-
#define DEFAULT_MIGRATION_THRESHOLD 2048
static int cache_create(struct cache_args *ca, struct cache **result)
@@ -2321,16 +2275,13 @@ static int cache_create(struct cache_args *ca, struct cache **result)
}
clear_bitset(cache->dirty_bitset, from_cblock(cache->cache_size));
- cache->discard_block_size =
- calculate_discard_block_size(cache->sectors_per_block,
- cache->origin_sectors);
- cache->discard_nr_blocks = oblock_to_dblock(cache, cache->origin_blocks);
- cache->discard_bitset = alloc_bitset(from_dblock(cache->discard_nr_blocks));
+ cache->discard_nr_blocks = cache->origin_blocks;
+ cache->discard_bitset = alloc_bitset(from_oblock(cache->discard_nr_blocks));
if (!cache->discard_bitset) {
*error = "could not allocate discard bitset";
goto bad;
}
- clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks));
+ clear_bitset(cache->discard_bitset, from_oblock(cache->discard_nr_blocks));
cache->copier = dm_kcopyd_client_create(&dm_kcopyd_throttle);
if (IS_ERR(cache->copier)) {
@@ -2614,16 +2565,16 @@ static int write_discard_bitset(struct cache *cache)
{
unsigned i, r;
- r = dm_cache_discard_bitset_resize(cache->cmd, cache->discard_block_size,
- cache->discard_nr_blocks);
+ r = dm_cache_discard_bitset_resize(cache->cmd, cache->sectors_per_block,
+ cache->origin_blocks);
if (r) {
DMERR("could not resize on-disk discard bitset");
return r;
}
- for (i = 0; i < from_dblock(cache->discard_nr_blocks); i++) {
- r = dm_cache_set_discard(cache->cmd, to_dblock(i),
- is_discarded(cache, to_dblock(i)));
+ for (i = 0; i < from_oblock(cache->discard_nr_blocks); i++) {
+ r = dm_cache_set_discard(cache->cmd, to_oblock(i),
+ is_discarded(cache, to_oblock(i)));
if (r)
return r;
}
@@ -2631,30 +2582,6 @@ static int write_discard_bitset(struct cache *cache)
return 0;
}
-static int save_hint(void *context, dm_cblock_t cblock, dm_oblock_t oblock,
- uint32_t hint)
-{
- struct cache *cache = context;
- return dm_cache_save_hint(cache->cmd, cblock, hint);
-}
-
-static int write_hints(struct cache *cache)
-{
- int r;
-
- r = dm_cache_begin_hints(cache->cmd, cache->policy);
- if (r) {
- DMERR("dm_cache_begin_hints failed");
- return r;
- }
-
- r = policy_walk_mappings(cache->policy, save_hint, cache);
- if (r)
- DMERR("policy_walk_mappings failed");
-
- return r;
-}
-
/*
* returns true on success
*/
@@ -2672,7 +2599,7 @@ static bool sync_metadata(struct cache *cache)
save_stats(cache);
- r3 = write_hints(cache);
+ r3 = dm_cache_write_hints(cache->cmd, cache->policy);
if (r3)
DMERR("could not write hints");
@@ -2720,16 +2647,14 @@ static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock,
}
static int load_discard(void *context, sector_t discard_block_size,
- dm_dblock_t dblock, bool discard)
+ dm_oblock_t oblock, bool discard)
{
struct cache *cache = context;
- /* FIXME: handle mis-matched block size */
-
if (discard)
- set_discard(cache, dblock);
+ set_discard(cache, oblock);
else
- clear_discard(cache, dblock);
+ clear_discard(cache, oblock);
return 0;
}
@@ -3120,8 +3045,8 @@ static void set_discard_limits(struct cache *cache, struct queue_limits *limits)
/*
* FIXME: these limits may be incompatible with the cache device
*/
- limits->max_discard_sectors = cache->discard_block_size * 1024;
- limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
+ limits->max_discard_sectors = cache->sectors_per_block;
+ limits->discard_granularity = cache->sectors_per_block << SECTOR_SHIFT;
}
static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -3145,7 +3070,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type cache_target = {
.name = "cache",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = cache_ctr,
.dtr = cache_dtr,
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
new file mode 100644
index 000000000000..414dad4cb49b
--- /dev/null
+++ b/drivers/md/dm-era-target.c
@@ -0,0 +1,1746 @@
+#include "dm.h"
+#include "persistent-data/dm-transaction-manager.h"
+#include "persistent-data/dm-bitset.h"
+#include "persistent-data/dm-space-map.h"
+
+#include <linux/dm-io.h>
+#include <linux/dm-kcopyd.h>
+#include <linux/init.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#define DM_MSG_PREFIX "era"
+
+#define SUPERBLOCK_LOCATION 0
+#define SUPERBLOCK_MAGIC 2126579579
+#define SUPERBLOCK_CSUM_XOR 146538381
+#define MIN_ERA_VERSION 1
+#define MAX_ERA_VERSION 1
+#define INVALID_WRITESET_ROOT SUPERBLOCK_LOCATION
+#define MIN_BLOCK_SIZE 8
+
+/*----------------------------------------------------------------
+ * Writeset
+ *--------------------------------------------------------------*/
+struct writeset_metadata {
+ uint32_t nr_bits;
+ dm_block_t root;
+};
+
+struct writeset {
+ struct writeset_metadata md;
+
+ /*
+ * An in core copy of the bits to save constantly doing look ups on
+ * disk.
+ */
+ unsigned long *bits;
+};
+
+/*
+ * This does not free off the on disk bitset as this will normally be done
+ * after digesting into the era array.
+ */
+static void writeset_free(struct writeset *ws)
+{
+ vfree(ws->bits);
+}
+
+static int setup_on_disk_bitset(struct dm_disk_bitset *info,
+ unsigned nr_bits, dm_block_t *root)
+{
+ int r;
+
+ r = dm_bitset_empty(info, root);
+ if (r)
+ return r;
+
+ return dm_bitset_resize(info, *root, 0, nr_bits, false, root);
+}
+
+static size_t bitset_size(unsigned nr_bits)
+{
+ return sizeof(unsigned long) * dm_div_up(nr_bits, BITS_PER_LONG);
+}
+
+/*
+ * Allocates memory for the in core bitset.
+ */
+static int writeset_alloc(struct writeset *ws, dm_block_t nr_blocks)
+{
+ ws->md.nr_bits = nr_blocks;
+ ws->md.root = INVALID_WRITESET_ROOT;
+ ws->bits = vzalloc(bitset_size(nr_blocks));
+ if (!ws->bits) {
+ DMERR("%s: couldn't allocate in memory bitset", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Wipes the in-core bitset, and creates a new on disk bitset.
+ */
+static int writeset_init(struct dm_disk_bitset *info, struct writeset *ws)
+{
+ int r;
+
+ memset(ws->bits, 0, bitset_size(ws->md.nr_bits));
+
+ r = setup_on_disk_bitset(info, ws->md.nr_bits, &ws->md.root);
+ if (r) {
+ DMERR("%s: setup_on_disk_bitset failed", __func__);
+ return r;
+ }
+
+ return 0;
+}
+
+static bool writeset_marked(struct writeset *ws, dm_block_t block)
+{
+ return test_bit(block, ws->bits);
+}
+
+static int writeset_marked_on_disk(struct dm_disk_bitset *info,
+ struct writeset_metadata *m, dm_block_t block,
+ bool *result)
+{
+ dm_block_t old = m->root;
+
+ /*
+ * The bitset was flushed when it was archived, so we know there'll
+ * be no change to the root.
+ */
+ int r = dm_bitset_test_bit(info, m->root, block, &m->root, result);
+ if (r) {
+ DMERR("%s: dm_bitset_test_bit failed", __func__);
+ return r;
+ }
+
+ BUG_ON(m->root != old);
+
+ return r;
+}
+
+/*
+ * Returns < 0 on error, 0 if the bit wasn't previously set, 1 if it was.
+ */
+static int writeset_test_and_set(struct dm_disk_bitset *info,
+ struct writeset *ws, uint32_t block)
+{
+ int r;
+
+ if (!test_and_set_bit(block, ws->bits)) {
+ r = dm_bitset_set_bit(info, ws->md.root, block, &ws->md.root);
+ if (r) {
+ /* FIXME: fail mode */
+ return r;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+/*----------------------------------------------------------------
+ * On disk metadata layout
+ *--------------------------------------------------------------*/
+#define SPACE_MAP_ROOT_SIZE 128
+#define UUID_LEN 16
+
+struct writeset_disk {
+ __le32 nr_bits;
+ __le64 root;
+} __packed;
+
+struct superblock_disk {
+ __le32 csum;
+ __le32 flags;
+ __le64 blocknr;
+
+ __u8 uuid[UUID_LEN];
+ __le64 magic;
+ __le32 version;
+
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+
+ __le32 data_block_size;
+ __le32 metadata_block_size;
+ __le32 nr_blocks;
+
+ __le32 current_era;
+ struct writeset_disk current_writeset;
+
+ /*
+ * Only these two fields are valid within the metadata snapshot.
+ */
+ __le64 writeset_tree_root;
+ __le64 era_array_root;
+
+ __le64 metadata_snap;
+} __packed;
+
+/*----------------------------------------------------------------
+ * Superblock validation
+ *--------------------------------------------------------------*/
+static void sb_prepare_for_write(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t sb_block_size)
+{
+ struct superblock_disk *disk = dm_block_data(b);
+
+ disk->blocknr = cpu_to_le64(dm_block_location(b));
+ disk->csum = cpu_to_le32(dm_bm_checksum(&disk->flags,
+ sb_block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+}
+
+static int check_metadata_version(struct superblock_disk *disk)
+{
+ uint32_t metadata_version = le32_to_cpu(disk->version);
+ if (metadata_version < MIN_ERA_VERSION || metadata_version > MAX_ERA_VERSION) {
+ DMERR("Era metadata version %u found, but only versions between %u and %u supported.",
+ metadata_version, MIN_ERA_VERSION, MAX_ERA_VERSION);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sb_check(struct dm_block_validator *v,
+ struct dm_block *b,
+ size_t sb_block_size)
+{
+ struct superblock_disk *disk = dm_block_data(b);
+ __le32 csum_le;
+
+ if (dm_block_location(b) != le64_to_cpu(disk->blocknr)) {
+ DMERR("sb_check failed: blocknr %llu: wanted %llu",
+ le64_to_cpu(disk->blocknr),
+ (unsigned long long)dm_block_location(b));
+ return -ENOTBLK;
+ }
+
+ if (le64_to_cpu(disk->magic) != SUPERBLOCK_MAGIC) {
+ DMERR("sb_check failed: magic %llu: wanted %llu",
+ le64_to_cpu(disk->magic),
+ (unsigned long long) SUPERBLOCK_MAGIC);
+ return -EILSEQ;
+ }
+
+ csum_le = cpu_to_le32(dm_bm_checksum(&disk->flags,
+ sb_block_size - sizeof(__le32),
+ SUPERBLOCK_CSUM_XOR));
+ if (csum_le != disk->csum) {
+ DMERR("sb_check failed: csum %u: wanted %u",
+ le32_to_cpu(csum_le), le32_to_cpu(disk->csum));
+ return -EILSEQ;
+ }
+
+ return check_metadata_version(disk);
+}
+
+static struct dm_block_validator sb_validator = {
+ .name = "superblock",
+ .prepare_for_write = sb_prepare_for_write,
+ .check = sb_check
+};
+
+/*----------------------------------------------------------------
+ * Low level metadata handling
+ *--------------------------------------------------------------*/
+#define DM_ERA_METADATA_BLOCK_SIZE 4096
+#define DM_ERA_METADATA_CACHE_SIZE 64
+#define ERA_MAX_CONCURRENT_LOCKS 5
+
+struct era_metadata {
+ struct block_device *bdev;
+ struct dm_block_manager *bm;
+ struct dm_space_map *sm;
+ struct dm_transaction_manager *tm;
+
+ dm_block_t block_size;
+ uint32_t nr_blocks;
+
+ uint32_t current_era;
+
+ /*
+ * We preallocate 2 writesets. When an era rolls over we
+ * switch between them. This means the allocation is done at
+ * preresume time, rather than on the io path.
+ */
+ struct writeset writesets[2];
+ struct writeset *current_writeset;
+
+ dm_block_t writeset_tree_root;
+ dm_block_t era_array_root;
+
+ struct dm_disk_bitset bitset_info;
+ struct dm_btree_info writeset_tree_info;
+ struct dm_array_info era_array_info;
+
+ dm_block_t metadata_snap;
+
+ /*
+ * A flag that is set whenever a writeset has been archived.
+ */
+ bool archived_writesets;
+
+ /*
+ * Reading the space map root can fail, so we read it into this
+ * buffer before the superblock is locked and updated.
+ */
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+};
+
+static int superblock_read_lock(struct era_metadata *md,
+ struct dm_block **sblock)
+{
+ return dm_bm_read_lock(md->bm, SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+static int superblock_lock_zero(struct era_metadata *md,
+ struct dm_block **sblock)
+{
+ return dm_bm_write_lock_zero(md->bm, SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+static int superblock_lock(struct era_metadata *md,
+ struct dm_block **sblock)
+{
+ return dm_bm_write_lock(md->bm, SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+/* FIXME: duplication with cache and thin */
+static int superblock_all_zeroes(struct dm_block_manager *bm, bool *result)
+{
+ int r;
+ unsigned i;
+ struct dm_block *b;
+ __le64 *data_le, zero = cpu_to_le64(0);
+ unsigned sb_block_size = dm_bm_block_size(bm) / sizeof(__le64);
+
+ /*
+ * We can't use a validator here - it may be all zeroes.
+ */
+ r = dm_bm_read_lock(bm, SUPERBLOCK_LOCATION, NULL, &b);
+ if (r)
+ return r;
+
+ data_le = dm_block_data(b);
+ *result = true;
+ for (i = 0; i < sb_block_size; i++) {
+ if (data_le[i] != zero) {
+ *result = false;
+ break;
+ }
+ }
+
+ return dm_bm_unlock(b);
+}
+
+/*----------------------------------------------------------------*/
+
+static void ws_pack(const struct writeset_metadata *core, struct writeset_disk *disk)
+{
+ disk->nr_bits = cpu_to_le32(core->nr_bits);
+ disk->root = cpu_to_le64(core->root);
+}
+
+static void ws_unpack(const struct writeset_disk *disk, struct writeset_metadata *core)
+{
+ core->nr_bits = le32_to_cpu(disk->nr_bits);
+ core->root = le64_to_cpu(disk->root);
+}
+
+static void ws_inc(void *context, const void *value)
+{
+ struct era_metadata *md = context;
+ struct writeset_disk ws_d;
+ dm_block_t b;
+
+ memcpy(&ws_d, value, sizeof(ws_d));
+ b = le64_to_cpu(ws_d.root);
+
+ dm_tm_inc(md->tm, b);
+}
+
+static void ws_dec(void *context, const void *value)
+{
+ struct era_metadata *md = context;
+ struct writeset_disk ws_d;
+ dm_block_t b;
+
+ memcpy(&ws_d, value, sizeof(ws_d));
+ b = le64_to_cpu(ws_d.root);
+
+ dm_bitset_del(&md->bitset_info, b);
+}
+
+static int ws_eq(void *context, const void *value1, const void *value2)
+{
+ return !memcmp(value1, value2, sizeof(struct writeset_metadata));
+}
+
+/*----------------------------------------------------------------*/
+
+static void setup_writeset_tree_info(struct era_metadata *md)
+{
+ struct dm_btree_value_type *vt = &md->writeset_tree_info.value_type;
+ md->writeset_tree_info.tm = md->tm;
+ md->writeset_tree_info.levels = 1;
+ vt->context = md;
+ vt->size = sizeof(struct writeset_disk);
+ vt->inc = ws_inc;
+ vt->dec = ws_dec;
+ vt->equal = ws_eq;
+}
+
+static void setup_era_array_info(struct era_metadata *md)
+
+{
+ struct dm_btree_value_type vt;
+ vt.context = NULL;
+ vt.size = sizeof(__le32);
+ vt.inc = NULL;
+ vt.dec = NULL;
+ vt.equal = NULL;
+
+ dm_array_info_init(&md->era_array_info, md->tm, &vt);
+}
+
+static void setup_infos(struct era_metadata *md)
+{
+ dm_disk_bitset_init(md->tm, &md->bitset_info);
+ setup_writeset_tree_info(md);
+ setup_era_array_info(md);
+}
+
+/*----------------------------------------------------------------*/
+
+static int create_fresh_metadata(struct era_metadata *md)
+{
+ int r;
+
+ r = dm_tm_create_with_sm(md->bm, SUPERBLOCK_LOCATION,
+ &md->tm, &md->sm);
+ if (r < 0) {
+ DMERR("dm_tm_create_with_sm failed");
+ return r;
+ }
+
+ setup_infos(md);
+
+ r = dm_btree_empty(&md->writeset_tree_info, &md->writeset_tree_root);
+ if (r) {
+ DMERR("couldn't create new writeset tree");
+ goto bad;
+ }
+
+ r = dm_array_empty(&md->era_array_info, &md->era_array_root);
+ if (r) {
+ DMERR("couldn't create era array");
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ dm_sm_destroy(md->sm);
+ dm_tm_destroy(md->tm);
+
+ return r;
+}
+
+static int save_sm_root(struct era_metadata *md)
+{
+ int r;
+ size_t metadata_len;
+
+ r = dm_sm_root_size(md->sm, &metadata_len);
+ if (r < 0)
+ return r;
+
+ return dm_sm_copy_root(md->sm, &md->metadata_space_map_root,
+ metadata_len);
+}
+
+static void copy_sm_root(struct era_metadata *md, struct superblock_disk *disk)
+{
+ memcpy(&disk->metadata_space_map_root,
+ &md->metadata_space_map_root,
+ sizeof(md->metadata_space_map_root));
+}
+
+/*
+ * Writes a superblock, including the static fields that don't get updated
+ * with every commit (possible optimisation here). 'md' should be fully
+ * constructed when this is called.
+ */
+static void prepare_superblock(struct era_metadata *md, struct superblock_disk *disk)
+{
+ disk->magic = cpu_to_le64(SUPERBLOCK_MAGIC);
+ disk->flags = cpu_to_le32(0ul);
+
+ /* FIXME: can't keep blanking the uuid (uuid is currently unused though) */
+ memset(disk->uuid, 0, sizeof(disk->uuid));
+ disk->version = cpu_to_le32(MAX_ERA_VERSION);
+
+ copy_sm_root(md, disk);
+
+ disk->data_block_size = cpu_to_le32(md->block_size);
+ disk->metadata_block_size = cpu_to_le32(DM_ERA_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+ disk->nr_blocks = cpu_to_le32(md->nr_blocks);
+ disk->current_era = cpu_to_le32(md->current_era);
+
+ ws_pack(&md->current_writeset->md, &disk->current_writeset);
+ disk->writeset_tree_root = cpu_to_le64(md->writeset_tree_root);
+ disk->era_array_root = cpu_to_le64(md->era_array_root);
+ disk->metadata_snap = cpu_to_le64(md->metadata_snap);
+}
+
+static int write_superblock(struct era_metadata *md)
+{
+ int r;
+ struct dm_block *sblock;
+ struct superblock_disk *disk;
+
+ r = save_sm_root(md);
+ if (r) {
+ DMERR("%s: save_sm_root failed", __func__);
+ return r;
+ }
+
+ r = superblock_lock_zero(md, &sblock);
+ if (r)
+ return r;
+
+ disk = dm_block_data(sblock);
+ prepare_superblock(md, disk);
+
+ return dm_tm_commit(md->tm, sblock);
+}
+
+/*
+ * Assumes block_size and the infos are set.
+ */
+static int format_metadata(struct era_metadata *md)
+{
+ int r;
+
+ r = create_fresh_metadata(md);
+ if (r)
+ return r;
+
+ r = write_superblock(md);
+ if (r) {
+ dm_sm_destroy(md->sm);
+ dm_tm_destroy(md->tm);
+ return r;
+ }
+
+ return 0;
+}
+
+static int open_metadata(struct era_metadata *md)
+{
+ int r;
+ struct dm_block *sblock;
+ struct superblock_disk *disk;
+
+ r = superblock_read_lock(md, &sblock);
+ if (r) {
+ DMERR("couldn't read_lock superblock");
+ return r;
+ }
+
+ disk = dm_block_data(sblock);
+ r = dm_tm_open_with_sm(md->bm, SUPERBLOCK_LOCATION,
+ disk->metadata_space_map_root,
+ sizeof(disk->metadata_space_map_root),
+ &md->tm, &md->sm);
+ if (r) {
+ DMERR("dm_tm_open_with_sm failed");
+ goto bad;
+ }
+
+ setup_infos(md);
+
+ md->block_size = le32_to_cpu(disk->data_block_size);
+ md->nr_blocks = le32_to_cpu(disk->nr_blocks);
+ md->current_era = le32_to_cpu(disk->current_era);
+
+ md->writeset_tree_root = le64_to_cpu(disk->writeset_tree_root);
+ md->era_array_root = le64_to_cpu(disk->era_array_root);
+ md->metadata_snap = le64_to_cpu(disk->metadata_snap);
+ md->archived_writesets = true;
+
+ return dm_bm_unlock(sblock);
+
+bad:
+ dm_bm_unlock(sblock);
+ return r;
+}
+
+static int open_or_format_metadata(struct era_metadata *md,
+ bool may_format)
+{
+ int r;
+ bool unformatted = false;
+
+ r = superblock_all_zeroes(md->bm, &unformatted);
+ if (r)
+ return r;
+
+ if (unformatted)
+ return may_format ? format_metadata(md) : -EPERM;
+
+ return open_metadata(md);
+}
+
+static int create_persistent_data_objects(struct era_metadata *md,
+ bool may_format)
+{
+ int r;
+
+ md->bm = dm_block_manager_create(md->bdev, DM_ERA_METADATA_BLOCK_SIZE,
+ DM_ERA_METADATA_CACHE_SIZE,
+ ERA_MAX_CONCURRENT_LOCKS);
+ if (IS_ERR(md->bm)) {
+ DMERR("could not create block manager");
+ return PTR_ERR(md->bm);
+ }
+
+ r = open_or_format_metadata(md, may_format);
+ if (r)
+ dm_block_manager_destroy(md->bm);
+
+ return r;
+}
+
+static void destroy_persistent_data_objects(struct era_metadata *md)
+{
+ dm_sm_destroy(md->sm);
+ dm_tm_destroy(md->tm);
+ dm_block_manager_destroy(md->bm);
+}
+
+/*
+ * This waits until all era_map threads have picked up the new filter.
+ */
+static void swap_writeset(struct era_metadata *md, struct writeset *new_writeset)
+{
+ rcu_assign_pointer(md->current_writeset, new_writeset);
+ synchronize_rcu();
+}
+
+/*----------------------------------------------------------------
+ * Writesets get 'digested' into the main era array.
+ *
+ * We're using a coroutine here so the worker thread can do the digestion,
+ * thus avoiding synchronisation of the metadata. Digesting a whole
+ * writeset in one go would cause too much latency.
+ *--------------------------------------------------------------*/
+struct digest {
+ uint32_t era;
+ unsigned nr_bits, current_bit;
+ struct writeset_metadata writeset;
+ __le32 value;
+ struct dm_disk_bitset info;
+
+ int (*step)(struct era_metadata *, struct digest *);
+};
+
+static int metadata_digest_lookup_writeset(struct era_metadata *md,
+ struct digest *d);
+
+static int metadata_digest_remove_writeset(struct era_metadata *md,
+ struct digest *d)
+{
+ int r;
+ uint64_t key = d->era;
+
+ r = dm_btree_remove(&md->writeset_tree_info, md->writeset_tree_root,
+ &key, &md->writeset_tree_root);
+ if (r) {
+ DMERR("%s: dm_btree_remove failed", __func__);
+ return r;
+ }
+
+ d->step = metadata_digest_lookup_writeset;
+ return 0;
+}
+
+#define INSERTS_PER_STEP 100
+
+static int metadata_digest_transcribe_writeset(struct era_metadata *md,
+ struct digest *d)
+{
+ int r;
+ bool marked;
+ unsigned b, e = min(d->current_bit + INSERTS_PER_STEP, d->nr_bits);
+
+ for (b = d->current_bit; b < e; b++) {
+ r = writeset_marked_on_disk(&d->info, &d->writeset, b, &marked);
+ if (r) {
+ DMERR("%s: writeset_marked_on_disk failed", __func__);
+ return r;
+ }
+
+ if (!marked)
+ continue;
+
+ __dm_bless_for_disk(&d->value);
+ r = dm_array_set_value(&md->era_array_info, md->era_array_root,
+ b, &d->value, &md->era_array_root);
+ if (r) {
+ DMERR("%s: dm_array_set_value failed", __func__);
+ return r;
+ }
+ }
+
+ if (b == d->nr_bits)
+ d->step = metadata_digest_remove_writeset;
+ else
+ d->current_bit = b;
+
+ return 0;
+}
+
+static int metadata_digest_lookup_writeset(struct era_metadata *md,
+ struct digest *d)
+{
+ int r;
+ uint64_t key;
+ struct writeset_disk disk;
+
+ r = dm_btree_find_lowest_key(&md->writeset_tree_info,
+ md->writeset_tree_root, &key);
+ if (r < 0)
+ return r;
+
+ d->era = key;
+
+ r = dm_btree_lookup(&md->writeset_tree_info,
+ md->writeset_tree_root, &key, &disk);
+ if (r) {
+ if (r == -ENODATA) {
+ d->step = NULL;
+ return 0;
+ }
+
+ DMERR("%s: dm_btree_lookup failed", __func__);
+ return r;
+ }
+
+ ws_unpack(&disk, &d->writeset);
+ d->value = cpu_to_le32(key);
+
+ d->nr_bits = min(d->writeset.nr_bits, md->nr_blocks);
+ d->current_bit = 0;
+ d->step = metadata_digest_transcribe_writeset;
+
+ return 0;
+}
+
+static int metadata_digest_start(struct era_metadata *md, struct digest *d)
+{
+ if (d->step)
+ return 0;
+
+ memset(d, 0, sizeof(*d));
+
+ /*
+ * We initialise another bitset info to avoid any caching side
+ * effects with the previous one.
+ */
+ dm_disk_bitset_init(md->tm, &d->info);
+ d->step = metadata_digest_lookup_writeset;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+ * High level metadata interface. Target methods should use these, and not
+ * the lower level ones.
+ *--------------------------------------------------------------*/
+static struct era_metadata *metadata_open(struct block_device *bdev,
+ sector_t block_size,
+ bool may_format)
+{
+ int r;
+ struct era_metadata *md = kzalloc(sizeof(*md), GFP_KERNEL);
+
+ if (!md)
+ return NULL;
+
+ md->bdev = bdev;
+ md->block_size = block_size;
+
+ md->writesets[0].md.root = INVALID_WRITESET_ROOT;
+ md->writesets[1].md.root = INVALID_WRITESET_ROOT;
+ md->current_writeset = &md->writesets[0];
+
+ r = create_persistent_data_objects(md, may_format);
+ if (r) {
+ kfree(md);
+ return ERR_PTR(r);
+ }
+
+ return md;
+}
+
+static void metadata_close(struct era_metadata *md)
+{
+ destroy_persistent_data_objects(md);
+ kfree(md);
+}
+
+static bool valid_nr_blocks(dm_block_t n)
+{
+ /*
+ * dm_bitset restricts us to 2^32. test_bit & co. restrict us
+ * further to 2^31 - 1
+ */
+ return n < (1ull << 31);
+}
+
+static int metadata_resize(struct era_metadata *md, void *arg)
+{
+ int r;
+ dm_block_t *new_size = arg;
+ __le32 value;
+
+ if (!valid_nr_blocks(*new_size)) {
+ DMERR("Invalid number of origin blocks %llu",
+ (unsigned long long) *new_size);
+ return -EINVAL;
+ }
+
+ writeset_free(&md->writesets[0]);
+ writeset_free(&md->writesets[1]);
+
+ r = writeset_alloc(&md->writesets[0], *new_size);
+ if (r) {
+ DMERR("%s: writeset_alloc failed for writeset 0", __func__);
+ return r;
+ }
+
+ r = writeset_alloc(&md->writesets[1], *new_size);
+ if (r) {
+ DMERR("%s: writeset_alloc failed for writeset 1", __func__);
+ return r;
+ }
+
+ value = cpu_to_le32(0u);
+ __dm_bless_for_disk(&value);
+ r = dm_array_resize(&md->era_array_info, md->era_array_root,
+ md->nr_blocks, *new_size,
+ &value, &md->era_array_root);
+ if (r) {
+ DMERR("%s: dm_array_resize failed", __func__);
+ return r;
+ }
+
+ md->nr_blocks = *new_size;
+ return 0;
+}
+
+static int metadata_era_archive(struct era_metadata *md)
+{
+ int r;
+ uint64_t keys[1];
+ struct writeset_disk value;
+
+ r = dm_bitset_flush(&md->bitset_info, md->current_writeset->md.root,
+ &md->current_writeset->md.root);
+ if (r) {
+ DMERR("%s: dm_bitset_flush failed", __func__);
+ return r;
+ }
+
+ ws_pack(&md->current_writeset->md, &value);
+ md->current_writeset->md.root = INVALID_WRITESET_ROOT;
+
+ keys[0] = md->current_era;
+ __dm_bless_for_disk(&value);
+ r = dm_btree_insert(&md->writeset_tree_info, md->writeset_tree_root,
+ keys, &value, &md->writeset_tree_root);
+ if (r) {
+ DMERR("%s: couldn't insert writeset into btree", __func__);
+ /* FIXME: fail mode */
+ return r;
+ }
+
+ md->archived_writesets = true;
+
+ return 0;
+}
+
+static struct writeset *next_writeset(struct era_metadata *md)
+{
+ return (md->current_writeset == &md->writesets[0]) ?
+ &md->writesets[1] : &md->writesets[0];
+}
+
+static int metadata_new_era(struct era_metadata *md)
+{
+ int r;
+ struct writeset *new_writeset = next_writeset(md);
+
+ r = writeset_init(&md->bitset_info, new_writeset);
+ if (r) {
+ DMERR("%s: writeset_init failed", __func__);
+ return r;
+ }
+
+ swap_writeset(md, new_writeset);
+ md->current_era++;
+
+ return 0;
+}
+
+static int metadata_era_rollover(struct era_metadata *md)
+{
+ int r;
+
+ if (md->current_writeset->md.root != INVALID_WRITESET_ROOT) {
+ r = metadata_era_archive(md);
+ if (r) {
+ DMERR("%s: metadata_archive_era failed", __func__);
+ /* FIXME: fail mode? */
+ return r;
+ }
+ }
+
+ r = metadata_new_era(md);
+ if (r) {
+ DMERR("%s: new era failed", __func__);
+ /* FIXME: fail mode */
+ return r;
+ }
+
+ return 0;
+}
+
+static bool metadata_current_marked(struct era_metadata *md, dm_block_t block)
+{
+ bool r;
+ struct writeset *ws;
+
+ rcu_read_lock();
+ ws = rcu_dereference(md->current_writeset);
+ r = writeset_marked(ws, block);
+ rcu_read_unlock();
+
+ return r;
+}
+
+static int metadata_commit(struct era_metadata *md)
+{
+ int r;
+ struct dm_block *sblock;
+
+ if (md->current_writeset->md.root != SUPERBLOCK_LOCATION) {
+ r = dm_bitset_flush(&md->bitset_info, md->current_writeset->md.root,
+ &md->current_writeset->md.root);
+ if (r) {
+ DMERR("%s: bitset flush failed", __func__);
+ return r;
+ }
+ }
+
+ r = save_sm_root(md);
+ if (r) {
+ DMERR("%s: save_sm_root failed", __func__);
+ return r;
+ }
+
+ r = dm_tm_pre_commit(md->tm);
+ if (r) {
+ DMERR("%s: pre commit failed", __func__);
+ return r;
+ }
+
+ r = superblock_lock(md, &sblock);
+ if (r) {
+ DMERR("%s: superblock lock failed", __func__);
+ return r;
+ }
+
+ prepare_superblock(md, dm_block_data(sblock));
+
+ return dm_tm_commit(md->tm, sblock);
+}
+
+static int metadata_checkpoint(struct era_metadata *md)
+{
+ /*
+ * For now we just rollover, but later I want to put a check in to
+ * avoid this if the filter is still pretty fresh.
+ */
+ return metadata_era_rollover(md);
+}
+
+/*
+ * Metadata snapshots allow userland to access era data.
+ */
+static int metadata_take_snap(struct era_metadata *md)
+{
+ int r, inc;
+ struct dm_block *clone;
+
+ if (md->metadata_snap != SUPERBLOCK_LOCATION) {
+ DMERR("%s: metadata snapshot already exists", __func__);
+ return -EINVAL;
+ }
+
+ r = metadata_era_rollover(md);
+ if (r) {
+ DMERR("%s: era rollover failed", __func__);
+ return r;
+ }
+
+ r = metadata_commit(md);
+ if (r) {
+ DMERR("%s: pre commit failed", __func__);
+ return r;
+ }
+
+ r = dm_sm_inc_block(md->sm, SUPERBLOCK_LOCATION);
+ if (r) {
+ DMERR("%s: couldn't increment superblock", __func__);
+ return r;
+ }
+
+ r = dm_tm_shadow_block(md->tm, SUPERBLOCK_LOCATION,
+ &sb_validator, &clone, &inc);
+ if (r) {
+ DMERR("%s: couldn't shadow superblock", __func__);
+ dm_sm_dec_block(md->sm, SUPERBLOCK_LOCATION);
+ return r;
+ }
+ BUG_ON(!inc);
+
+ r = dm_sm_inc_block(md->sm, md->writeset_tree_root);
+ if (r) {
+ DMERR("%s: couldn't inc writeset tree root", __func__);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ r = dm_sm_inc_block(md->sm, md->era_array_root);
+ if (r) {
+ DMERR("%s: couldn't inc era tree root", __func__);
+ dm_sm_dec_block(md->sm, md->writeset_tree_root);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ md->metadata_snap = dm_block_location(clone);
+
+ r = dm_tm_unlock(md->tm, clone);
+ if (r) {
+ DMERR("%s: couldn't unlock clone", __func__);
+ md->metadata_snap = SUPERBLOCK_LOCATION;
+ return r;
+ }
+
+ return 0;
+}
+
+static int metadata_drop_snap(struct era_metadata *md)
+{
+ int r;
+ dm_block_t location;
+ struct dm_block *clone;
+ struct superblock_disk *disk;
+
+ if (md->metadata_snap == SUPERBLOCK_LOCATION) {
+ DMERR("%s: no snap to drop", __func__);
+ return -EINVAL;
+ }
+
+ r = dm_tm_read_lock(md->tm, md->metadata_snap, &sb_validator, &clone);
+ if (r) {
+ DMERR("%s: couldn't read lock superblock clone", __func__);
+ return r;
+ }
+
+ /*
+ * Whatever happens now we'll commit with no record of the metadata
+ * snap.
+ */
+ md->metadata_snap = SUPERBLOCK_LOCATION;
+
+ disk = dm_block_data(clone);
+ r = dm_btree_del(&md->writeset_tree_info,
+ le64_to_cpu(disk->writeset_tree_root));
+ if (r) {
+ DMERR("%s: error deleting writeset tree clone", __func__);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ r = dm_array_del(&md->era_array_info, le64_to_cpu(disk->era_array_root));
+ if (r) {
+ DMERR("%s: error deleting era array clone", __func__);
+ dm_tm_unlock(md->tm, clone);
+ return r;
+ }
+
+ location = dm_block_location(clone);
+ dm_tm_unlock(md->tm, clone);
+
+ return dm_sm_dec_block(md->sm, location);
+}
+
+struct metadata_stats {
+ dm_block_t used;
+ dm_block_t total;
+ dm_block_t snap;
+ uint32_t era;
+};
+
+static int metadata_get_stats(struct era_metadata *md, void *ptr)
+{
+ int r;
+ struct metadata_stats *s = ptr;
+ dm_block_t nr_free, nr_total;
+
+ r = dm_sm_get_nr_free(md->sm, &nr_free);
+ if (r) {
+ DMERR("dm_sm_get_nr_free returned %d", r);
+ return r;
+ }
+
+ r = dm_sm_get_nr_blocks(md->sm, &nr_total);
+ if (r) {
+ DMERR("dm_pool_get_metadata_dev_size returned %d", r);
+ return r;
+ }
+
+ s->used = nr_total - nr_free;
+ s->total = nr_total;
+ s->snap = md->metadata_snap;
+ s->era = md->current_era;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+struct era {
+ struct dm_target *ti;
+ struct dm_target_callbacks callbacks;
+
+ struct dm_dev *metadata_dev;
+ struct dm_dev *origin_dev;
+
+ dm_block_t nr_blocks;
+ uint32_t sectors_per_block;
+ int sectors_per_block_shift;
+ struct era_metadata *md;
+
+ struct workqueue_struct *wq;
+ struct work_struct worker;
+
+ spinlock_t deferred_lock;
+ struct bio_list deferred_bios;
+
+ spinlock_t rpc_lock;
+ struct list_head rpc_calls;
+
+ struct digest digest;
+ atomic_t suspended;
+};
+
+struct rpc {
+ struct list_head list;
+
+ int (*fn0)(struct era_metadata *);
+ int (*fn1)(struct era_metadata *, void *);
+ void *arg;
+ int result;
+
+ struct completion complete;
+};
+
+/*----------------------------------------------------------------
+ * Remapping.
+ *---------------------------------------------------------------*/
+static bool block_size_is_power_of_two(struct era *era)
+{
+ return era->sectors_per_block_shift >= 0;
+}
+
+static dm_block_t get_block(struct era *era, struct bio *bio)
+{
+ sector_t block_nr = bio->bi_iter.bi_sector;
+
+ if (!block_size_is_power_of_two(era))
+ (void) sector_div(block_nr, era->sectors_per_block);
+ else
+ block_nr >>= era->sectors_per_block_shift;
+
+ return block_nr;
+}
+
+static void remap_to_origin(struct era *era, struct bio *bio)
+{
+ bio->bi_bdev = era->origin_dev->bdev;
+}
+
+/*----------------------------------------------------------------
+ * Worker thread
+ *--------------------------------------------------------------*/
+static void wake_worker(struct era *era)
+{
+ if (!atomic_read(&era->suspended))
+ queue_work(era->wq, &era->worker);
+}
+
+static void process_old_eras(struct era *era)
+{
+ int r;
+
+ if (!era->digest.step)
+ return;
+
+ r = era->digest.step(era->md, &era->digest);
+ if (r < 0) {
+ DMERR("%s: digest step failed, stopping digestion", __func__);
+ era->digest.step = NULL;
+
+ } else if (era->digest.step)
+ wake_worker(era);
+}
+
+static void process_deferred_bios(struct era *era)
+{
+ int r;
+ struct bio_list deferred_bios, marked_bios;
+ struct bio *bio;
+ bool commit_needed = false;
+ bool failed = false;
+
+ bio_list_init(&deferred_bios);
+ bio_list_init(&marked_bios);
+
+ spin_lock(&era->deferred_lock);
+ bio_list_merge(&deferred_bios, &era->deferred_bios);
+ bio_list_init(&era->deferred_bios);
+ spin_unlock(&era->deferred_lock);
+
+ while ((bio = bio_list_pop(&deferred_bios))) {
+ r = writeset_test_and_set(&era->md->bitset_info,
+ era->md->current_writeset,
+ get_block(era, bio));
+ if (r < 0) {
+ /*
+ * This is bad news, we need to rollback.
+ * FIXME: finish.
+ */
+ failed = true;
+
+ } else if (r == 0)
+ commit_needed = true;
+
+ bio_list_add(&marked_bios, bio);
+ }
+
+ if (commit_needed) {
+ r = metadata_commit(era->md);
+ if (r)
+ failed = true;
+ }
+
+ if (failed)
+ while ((bio = bio_list_pop(&marked_bios)))
+ bio_io_error(bio);
+ else
+ while ((bio = bio_list_pop(&marked_bios)))
+ generic_make_request(bio);
+}
+
+static void process_rpc_calls(struct era *era)
+{
+ int r;
+ bool need_commit = false;
+ struct list_head calls;
+ struct rpc *rpc, *tmp;
+
+ INIT_LIST_HEAD(&calls);
+ spin_lock(&era->rpc_lock);
+ list_splice_init(&era->rpc_calls, &calls);
+ spin_unlock(&era->rpc_lock);
+
+ list_for_each_entry_safe(rpc, tmp, &calls, list) {
+ rpc->result = rpc->fn0 ? rpc->fn0(era->md) : rpc->fn1(era->md, rpc->arg);
+ need_commit = true;
+ }
+
+ if (need_commit) {
+ r = metadata_commit(era->md);
+ if (r)
+ list_for_each_entry_safe(rpc, tmp, &calls, list)
+ rpc->result = r;
+ }
+
+ list_for_each_entry_safe(rpc, tmp, &calls, list)
+ complete(&rpc->complete);
+}
+
+static void kick_off_digest(struct era *era)
+{
+ if (era->md->archived_writesets) {
+ era->md->archived_writesets = false;
+ metadata_digest_start(era->md, &era->digest);
+ }
+}
+
+static void do_work(struct work_struct *ws)
+{
+ struct era *era = container_of(ws, struct era, worker);
+
+ kick_off_digest(era);
+ process_old_eras(era);
+ process_deferred_bios(era);
+ process_rpc_calls(era);
+}
+
+static void defer_bio(struct era *era, struct bio *bio)
+{
+ spin_lock(&era->deferred_lock);
+ bio_list_add(&era->deferred_bios, bio);
+ spin_unlock(&era->deferred_lock);
+
+ wake_worker(era);
+}
+
+/*
+ * Make an rpc call to the worker to change the metadata.
+ */
+static int perform_rpc(struct era *era, struct rpc *rpc)
+{
+ rpc->result = 0;
+ init_completion(&rpc->complete);
+
+ spin_lock(&era->rpc_lock);
+ list_add(&rpc->list, &era->rpc_calls);
+ spin_unlock(&era->rpc_lock);
+
+ wake_worker(era);
+ wait_for_completion(&rpc->complete);
+
+ return rpc->result;
+}
+
+static int in_worker0(struct era *era, int (*fn)(struct era_metadata *))
+{
+ struct rpc rpc;
+ rpc.fn0 = fn;
+ rpc.fn1 = NULL;
+
+ return perform_rpc(era, &rpc);
+}
+
+static int in_worker1(struct era *era,
+ int (*fn)(struct era_metadata *, void *), void *arg)
+{
+ struct rpc rpc;
+ rpc.fn0 = NULL;
+ rpc.fn1 = fn;
+ rpc.arg = arg;
+
+ return perform_rpc(era, &rpc);
+}
+
+static void start_worker(struct era *era)
+{
+ atomic_set(&era->suspended, 0);
+}
+
+static void stop_worker(struct era *era)
+{
+ atomic_set(&era->suspended, 1);
+ flush_workqueue(era->wq);
+}
+
+/*----------------------------------------------------------------
+ * Target methods
+ *--------------------------------------------------------------*/
+static int dev_is_congested(struct dm_dev *dev, int bdi_bits)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+ return bdi_congested(&q->backing_dev_info, bdi_bits);
+}
+
+static int era_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
+{
+ struct era *era = container_of(cb, struct era, callbacks);
+ return dev_is_congested(era->origin_dev, bdi_bits);
+}
+
+static void era_destroy(struct era *era)
+{
+ metadata_close(era->md);
+
+ if (era->wq)
+ destroy_workqueue(era->wq);
+
+ if (era->origin_dev)
+ dm_put_device(era->ti, era->origin_dev);
+
+ if (era->metadata_dev)
+ dm_put_device(era->ti, era->metadata_dev);
+
+ kfree(era);
+}
+
+static dm_block_t calc_nr_blocks(struct era *era)
+{
+ return dm_sector_div_up(era->ti->len, era->sectors_per_block);
+}
+
+static bool valid_block_size(dm_block_t block_size)
+{
+ bool greater_than_zero = block_size > 0;
+ bool multiple_of_min_block_size = (block_size & (MIN_BLOCK_SIZE - 1)) == 0;
+
+ return greater_than_zero && multiple_of_min_block_size;
+}
+
+/*
+ * <metadata dev> <data dev> <data block size (sectors)>
+ */
+static int era_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ int r;
+ char dummy;
+ struct era *era;
+ struct era_metadata *md;
+
+ if (argc != 3) {
+ ti->error = "Invalid argument count";
+ return -EINVAL;
+ }
+
+ era = kzalloc(sizeof(*era), GFP_KERNEL);
+ if (!era) {
+ ti->error = "Error allocating era structure";
+ return -ENOMEM;
+ }
+
+ era->ti = ti;
+
+ r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &era->metadata_dev);
+ if (r) {
+ ti->error = "Error opening metadata device";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ r = dm_get_device(ti, argv[1], FMODE_READ | FMODE_WRITE, &era->origin_dev);
+ if (r) {
+ ti->error = "Error opening data device";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ r = sscanf(argv[2], "%u%c", &era->sectors_per_block, &dummy);
+ if (r != 1) {
+ ti->error = "Error parsing block size";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ r = dm_set_target_max_io_len(ti, era->sectors_per_block);
+ if (r) {
+ ti->error = "could not set max io len";
+ era_destroy(era);
+ return -EINVAL;
+ }
+
+ if (!valid_block_size(era->sectors_per_block)) {
+ ti->error = "Invalid block size";
+ era_destroy(era);
+ return -EINVAL;
+ }
+ if (era->sectors_per_block & (era->sectors_per_block - 1))
+ era->sectors_per_block_shift = -1;
+ else
+ era->sectors_per_block_shift = __ffs(era->sectors_per_block);
+
+ md = metadata_open(era->metadata_dev->bdev, era->sectors_per_block, true);
+ if (IS_ERR(md)) {
+ ti->error = "Error reading metadata";
+ era_destroy(era);
+ return PTR_ERR(md);
+ }
+ era->md = md;
+
+ era->nr_blocks = calc_nr_blocks(era);
+
+ r = metadata_resize(era->md, &era->nr_blocks);
+ if (r) {
+ ti->error = "couldn't resize metadata";
+ era_destroy(era);
+ return -ENOMEM;
+ }
+
+ era->wq = alloc_ordered_workqueue("dm-" DM_MSG_PREFIX, WQ_MEM_RECLAIM);
+ if (!era->wq) {
+ ti->error = "could not create workqueue for metadata object";
+ era_destroy(era);
+ return -ENOMEM;
+ }
+ INIT_WORK(&era->worker, do_work);
+
+ spin_lock_init(&era->deferred_lock);
+ bio_list_init(&era->deferred_bios);
+
+ spin_lock_init(&era->rpc_lock);
+ INIT_LIST_HEAD(&era->rpc_calls);
+
+ ti->private = era;
+ ti->num_flush_bios = 1;
+ ti->flush_supported = true;
+
+ ti->num_discard_bios = 1;
+ ti->discards_supported = true;
+ era->callbacks.congested_fn = era_is_congested;
+ dm_table_add_target_callbacks(ti->table, &era->callbacks);
+
+ return 0;
+}
+
+static void era_dtr(struct dm_target *ti)
+{
+ era_destroy(ti->private);
+}
+
+static int era_map(struct dm_target *ti, struct bio *bio)
+{
+ struct era *era = ti->private;
+ dm_block_t block = get_block(era, bio);
+
+ /*
+ * All bios get remapped to the origin device. We do this now, but
+ * it may not get issued until later. Depending on whether the
+ * block is marked in this era.
+ */
+ remap_to_origin(era, bio);
+
+ /*
+ * REQ_FLUSH bios carry no data, so we're not interested in them.
+ */
+ if (!(bio->bi_rw & REQ_FLUSH) &&
+ (bio_data_dir(bio) == WRITE) &&
+ !metadata_current_marked(era->md, block)) {
+ defer_bio(era, bio);
+ return DM_MAPIO_SUBMITTED;
+ }
+
+ return DM_MAPIO_REMAPPED;
+}
+
+static void era_postsuspend(struct dm_target *ti)
+{
+ int r;
+ struct era *era = ti->private;
+
+ r = in_worker0(era, metadata_era_archive);
+ if (r) {
+ DMERR("%s: couldn't archive current era", __func__);
+ /* FIXME: fail mode */
+ }
+
+ stop_worker(era);
+}
+
+static int era_preresume(struct dm_target *ti)
+{
+ int r;
+ struct era *era = ti->private;
+ dm_block_t new_size = calc_nr_blocks(era);
+
+ if (era->nr_blocks != new_size) {
+ r = in_worker1(era, metadata_resize, &new_size);
+ if (r)
+ return r;
+
+ era->nr_blocks = new_size;
+ }
+
+ start_worker(era);
+
+ r = in_worker0(era, metadata_new_era);
+ if (r) {
+ DMERR("%s: metadata_era_rollover failed", __func__);
+ return r;
+ }
+
+ return 0;
+}
+
+/*
+ * Status format:
+ *
+ * <metadata block size> <#used metadata blocks>/<#total metadata blocks>
+ * <current era> <held metadata root | '-'>
+ */
+static void era_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen)
+{
+ int r;
+ struct era *era = ti->private;
+ ssize_t sz = 0;
+ struct metadata_stats stats;
+ char buf[BDEVNAME_SIZE];
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ r = in_worker1(era, metadata_get_stats, &stats);
+ if (r)
+ goto err;
+
+ DMEMIT("%u %llu/%llu %u",
+ (unsigned) (DM_ERA_METADATA_BLOCK_SIZE >> SECTOR_SHIFT),
+ (unsigned long long) stats.used,
+ (unsigned long long) stats.total,
+ (unsigned) stats.era);
+
+ if (stats.snap != SUPERBLOCK_LOCATION)
+ DMEMIT(" %llu", stats.snap);
+ else
+ DMEMIT(" -");
+ break;
+
+ case STATUSTYPE_TABLE:
+ format_dev_t(buf, era->metadata_dev->bdev->bd_dev);
+ DMEMIT("%s ", buf);
+ format_dev_t(buf, era->origin_dev->bdev->bd_dev);
+ DMEMIT("%s %u", buf, era->sectors_per_block);
+ break;
+ }
+
+ return;
+
+err:
+ DMEMIT("Error");
+}
+
+static int era_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+ struct era *era = ti->private;
+
+ if (argc != 1) {
+ DMERR("incorrect number of message arguments");
+ return -EINVAL;
+ }
+
+ if (!strcasecmp(argv[0], "checkpoint"))
+ return in_worker0(era, metadata_checkpoint);
+
+ if (!strcasecmp(argv[0], "take_metadata_snap"))
+ return in_worker0(era, metadata_take_snap);
+
+ if (!strcasecmp(argv[0], "drop_metadata_snap"))
+ return in_worker0(era, metadata_drop_snap);
+
+ DMERR("unsupported message '%s'", argv[0]);
+ return -EINVAL;
+}
+
+static sector_t get_dev_size(struct dm_dev *dev)
+{
+ return i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT;
+}
+
+static int era_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct era *era = ti->private;
+ return fn(ti, era->origin_dev, 0, get_dev_size(era->origin_dev), data);
+}
+
+static int era_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct era *era = ti->private;
+ struct request_queue *q = bdev_get_queue(era->origin_dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = era->origin_dev->bdev;
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
+static void era_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct era *era = ti->private;
+ uint64_t io_opt_sectors = limits->io_opt >> SECTOR_SHIFT;
+
+ /*
+ * If the system-determined stacked limits are compatible with the
+ * era device's blocksize (io_opt is a factor) do not override them.
+ */
+ if (io_opt_sectors < era->sectors_per_block ||
+ do_div(io_opt_sectors, era->sectors_per_block)) {
+ blk_limits_io_min(limits, 0);
+ blk_limits_io_opt(limits, era->sectors_per_block << SECTOR_SHIFT);
+ }
+}
+
+/*----------------------------------------------------------------*/
+
+static struct target_type era_target = {
+ .name = "era",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = era_ctr,
+ .dtr = era_dtr,
+ .map = era_map,
+ .postsuspend = era_postsuspend,
+ .preresume = era_preresume,
+ .status = era_status,
+ .message = era_message,
+ .iterate_devices = era_iterate_devices,
+ .merge = era_merge,
+ .io_hints = era_io_hints
+};
+
+static int __init dm_era_init(void)
+{
+ int r;
+
+ r = dm_register_target(&era_target);
+ if (r) {
+ DMERR("era target registration failed: %d", r);
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit dm_era_exit(void)
+{
+ dm_unregister_target(&era_target);
+}
+
+module_init(dm_era_init);
+module_exit(dm_era_exit);
+
+MODULE_DESCRIPTION(DM_NAME " era target");
+MODULE_AUTHOR("Joe Thornber <ejt@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 422a9fdeb53e..aa009e865871 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -93,10 +93,6 @@ struct multipath {
unsigned pg_init_count; /* Number of times pg_init called */
unsigned pg_init_delay_msecs; /* Number of msecs before pg_init retry */
- unsigned queue_size;
- struct work_struct process_queued_ios;
- struct list_head queued_ios;
-
struct work_struct trigger_event;
/*
@@ -121,9 +117,9 @@ typedef int (*action_fn) (struct pgpath *pgpath);
static struct kmem_cache *_mpio_cache;
static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
-static void process_queued_ios(struct work_struct *work);
static void trigger_event(struct work_struct *work);
static void activate_path(struct work_struct *work);
+static int __pgpath_busy(struct pgpath *pgpath);
/*-----------------------------------------------
@@ -195,11 +191,9 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (m) {
INIT_LIST_HEAD(&m->priority_groups);
- INIT_LIST_HEAD(&m->queued_ios);
spin_lock_init(&m->lock);
m->queue_io = 1;
m->pg_init_delay_msecs = DM_PG_INIT_DELAY_DEFAULT;
- INIT_WORK(&m->process_queued_ios, process_queued_ios);
INIT_WORK(&m->trigger_event, trigger_event);
init_waitqueue_head(&m->pg_init_wait);
mutex_init(&m->work_mutex);
@@ -256,13 +250,21 @@ static void clear_mapinfo(struct multipath *m, union map_info *info)
* Path selection
*-----------------------------------------------*/
-static void __pg_init_all_paths(struct multipath *m)
+static int __pg_init_all_paths(struct multipath *m)
{
struct pgpath *pgpath;
unsigned long pg_init_delay = 0;
+ if (m->pg_init_in_progress || m->pg_init_disabled)
+ return 0;
+
m->pg_init_count++;
m->pg_init_required = 0;
+
+ /* Check here to reset pg_init_required */
+ if (!m->current_pg)
+ return 0;
+
if (m->pg_init_delay_retry)
pg_init_delay = msecs_to_jiffies(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT ?
m->pg_init_delay_msecs : DM_PG_INIT_DELAY_MSECS);
@@ -274,6 +276,7 @@ static void __pg_init_all_paths(struct multipath *m)
pg_init_delay))
m->pg_init_in_progress++;
}
+ return m->pg_init_in_progress;
}
static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
@@ -365,19 +368,26 @@ failed:
*/
static int __must_push_back(struct multipath *m)
{
- return (m->queue_if_no_path != m->saved_queue_if_no_path &&
- dm_noflush_suspending(m->ti));
+ return (m->queue_if_no_path ||
+ (m->queue_if_no_path != m->saved_queue_if_no_path &&
+ dm_noflush_suspending(m->ti)));
}
-static int map_io(struct multipath *m, struct request *clone,
- union map_info *map_context, unsigned was_queued)
+#define pg_ready(m) (!(m)->queue_io && !(m)->pg_init_required)
+
+/*
+ * Map cloned requests
+ */
+static int multipath_map(struct dm_target *ti, struct request *clone,
+ union map_info *map_context)
{
- int r = DM_MAPIO_REMAPPED;
+ struct multipath *m = (struct multipath *) ti->private;
+ int r = DM_MAPIO_REQUEUE;
size_t nr_bytes = blk_rq_bytes(clone);
unsigned long flags;
struct pgpath *pgpath;
struct block_device *bdev;
- struct dm_mpath_io *mpio = map_context->ptr;
+ struct dm_mpath_io *mpio;
spin_lock_irqsave(&m->lock, flags);
@@ -388,38 +398,33 @@ static int map_io(struct multipath *m, struct request *clone,
pgpath = m->current_pgpath;
- if (was_queued)
- m->queue_size--;
-
- if (m->pg_init_required) {
- if (!m->pg_init_in_progress)
- queue_work(kmultipathd, &m->process_queued_ios);
- r = DM_MAPIO_REQUEUE;
- } else if ((pgpath && m->queue_io) ||
- (!pgpath && m->queue_if_no_path)) {
- /* Queue for the daemon to resubmit */
- list_add_tail(&clone->queuelist, &m->queued_ios);
- m->queue_size++;
- if (!m->queue_io)
- queue_work(kmultipathd, &m->process_queued_ios);
- pgpath = NULL;
- r = DM_MAPIO_SUBMITTED;
- } else if (pgpath) {
- bdev = pgpath->path.dev->bdev;
- clone->q = bdev_get_queue(bdev);
- clone->rq_disk = bdev->bd_disk;
- } else if (__must_push_back(m))
- r = DM_MAPIO_REQUEUE;
- else
- r = -EIO; /* Failed */
+ if (!pgpath) {
+ if (!__must_push_back(m))
+ r = -EIO; /* Failed */
+ goto out_unlock;
+ }
+ if (!pg_ready(m)) {
+ __pg_init_all_paths(m);
+ goto out_unlock;
+ }
+ if (set_mapinfo(m, map_context) < 0)
+ /* ENOMEM, requeue */
+ goto out_unlock;
+ bdev = pgpath->path.dev->bdev;
+ clone->q = bdev_get_queue(bdev);
+ clone->rq_disk = bdev->bd_disk;
+ clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
+ mpio = map_context->ptr;
mpio->pgpath = pgpath;
mpio->nr_bytes = nr_bytes;
-
- if (r == DM_MAPIO_REMAPPED && pgpath->pg->ps.type->start_io)
- pgpath->pg->ps.type->start_io(&pgpath->pg->ps, &pgpath->path,
+ if (pgpath->pg->ps.type->start_io)
+ pgpath->pg->ps.type->start_io(&pgpath->pg->ps,
+ &pgpath->path,
nr_bytes);
+ r = DM_MAPIO_REMAPPED;
+out_unlock:
spin_unlock_irqrestore(&m->lock, flags);
return r;
@@ -440,76 +445,14 @@ static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path,
else
m->saved_queue_if_no_path = queue_if_no_path;
m->queue_if_no_path = queue_if_no_path;
- if (!m->queue_if_no_path && m->queue_size)
- queue_work(kmultipathd, &m->process_queued_ios);
+ if (!m->queue_if_no_path)
+ dm_table_run_md_queue_async(m->ti->table);
spin_unlock_irqrestore(&m->lock, flags);
return 0;
}
-/*-----------------------------------------------------------------
- * The multipath daemon is responsible for resubmitting queued ios.
- *---------------------------------------------------------------*/
-
-static void dispatch_queued_ios(struct multipath *m)
-{
- int r;
- unsigned long flags;
- union map_info *info;
- struct request *clone, *n;
- LIST_HEAD(cl);
-
- spin_lock_irqsave(&m->lock, flags);
- list_splice_init(&m->queued_ios, &cl);
- spin_unlock_irqrestore(&m->lock, flags);
-
- list_for_each_entry_safe(clone, n, &cl, queuelist) {
- list_del_init(&clone->queuelist);
-
- info = dm_get_rq_mapinfo(clone);
-
- r = map_io(m, clone, info, 1);
- if (r < 0) {
- clear_mapinfo(m, info);
- dm_kill_unmapped_request(clone, r);
- } else if (r == DM_MAPIO_REMAPPED)
- dm_dispatch_request(clone);
- else if (r == DM_MAPIO_REQUEUE) {
- clear_mapinfo(m, info);
- dm_requeue_unmapped_request(clone);
- }
- }
-}
-
-static void process_queued_ios(struct work_struct *work)
-{
- struct multipath *m =
- container_of(work, struct multipath, process_queued_ios);
- struct pgpath *pgpath = NULL;
- unsigned must_queue = 1;
- unsigned long flags;
-
- spin_lock_irqsave(&m->lock, flags);
-
- if (!m->current_pgpath)
- __choose_pgpath(m, 0);
-
- pgpath = m->current_pgpath;
-
- if ((pgpath && !m->queue_io) ||
- (!pgpath && !m->queue_if_no_path))
- must_queue = 0;
-
- if (m->pg_init_required && !m->pg_init_in_progress && pgpath &&
- !m->pg_init_disabled)
- __pg_init_all_paths(m);
-
- spin_unlock_irqrestore(&m->lock, flags);
- if (!must_queue)
- dispatch_queued_ios(m);
-}
-
/*
* An event is triggered whenever a path is taken out of use.
* Includes path failure and PG bypass.
@@ -972,27 +915,6 @@ static void multipath_dtr(struct dm_target *ti)
}
/*
- * Map cloned requests
- */
-static int multipath_map(struct dm_target *ti, struct request *clone,
- union map_info *map_context)
-{
- int r;
- struct multipath *m = (struct multipath *) ti->private;
-
- if (set_mapinfo(m, map_context) < 0)
- /* ENOMEM, requeue */
- return DM_MAPIO_REQUEUE;
-
- clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
- r = map_io(m, clone, map_context, 0);
- if (r < 0 || r == DM_MAPIO_REQUEUE)
- clear_mapinfo(m, map_context);
-
- return r;
-}
-
-/*
* Take a path out of use.
*/
static int fail_path(struct pgpath *pgpath)
@@ -1054,9 +976,9 @@ static int reinstate_path(struct pgpath *pgpath)
pgpath->is_active = 1;
- if (!m->nr_valid_paths++ && m->queue_size) {
+ if (!m->nr_valid_paths++) {
m->current_pgpath = NULL;
- queue_work(kmultipathd, &m->process_queued_ios);
+ dm_table_run_md_queue_async(m->ti->table);
} else if (m->hw_handler_name && (m->current_pg == pgpath->pg)) {
if (queue_work(kmpath_handlerd, &pgpath->activate_path.work))
m->pg_init_in_progress++;
@@ -1252,11 +1174,12 @@ static void pg_init_done(void *data, int errors)
/* Activations of other paths are still on going */
goto out;
- if (!m->pg_init_required)
- m->queue_io = 0;
-
- m->pg_init_delay_retry = delay_retry;
- queue_work(kmultipathd, &m->process_queued_ios);
+ if (m->pg_init_required) {
+ m->pg_init_delay_retry = delay_retry;
+ if (__pg_init_all_paths(m))
+ goto out;
+ }
+ m->queue_io = 0;
/*
* Wake up any thread waiting to suspend.
@@ -1272,8 +1195,11 @@ static void activate_path(struct work_struct *work)
struct pgpath *pgpath =
container_of(work, struct pgpath, activate_path.work);
- scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
- pg_init_done, pgpath);
+ if (pgpath->is_active)
+ scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
+ pg_init_done, pgpath);
+ else
+ pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED);
}
static int noretry_error(int error)
@@ -1433,7 +1359,7 @@ static void multipath_status(struct dm_target *ti, status_type_t type,
/* Features */
if (type == STATUSTYPE_INFO)
- DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
+ DMEMIT("2 %u %u ", m->queue_io, m->pg_init_count);
else {
DMEMIT("%u ", m->queue_if_no_path +
(m->pg_init_retries > 0) * 2 +
@@ -1552,7 +1478,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
}
if (argc != 2) {
- DMWARN("Unrecognised multipath message received.");
+ DMWARN("Invalid multipath message arguments. Expected 2 arguments, got %d.", argc);
goto out;
}
@@ -1570,7 +1496,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
else if (!strcasecmp(argv[0], "fail_path"))
action = fail_path;
else {
- DMWARN("Unrecognised multipath message received.");
+ DMWARN("Unrecognised multipath message received: %s", argv[0]);
goto out;
}
@@ -1632,8 +1558,17 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
r = err;
}
- if (r == -ENOTCONN && !fatal_signal_pending(current))
- queue_work(kmultipathd, &m->process_queued_ios);
+ if (r == -ENOTCONN && !fatal_signal_pending(current)) {
+ spin_lock_irqsave(&m->lock, flags);
+ if (!m->current_pg) {
+ /* Path status changed, redo selection */
+ __choose_pgpath(m, 0);
+ }
+ if (m->pg_init_required)
+ __pg_init_all_paths(m);
+ spin_unlock_irqrestore(&m->lock, flags);
+ dm_table_run_md_queue_async(m->ti->table);
+ }
return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
}
@@ -1684,7 +1619,7 @@ static int multipath_busy(struct dm_target *ti)
spin_lock_irqsave(&m->lock, flags);
/* pg_init in progress, requeue until done */
- if (m->pg_init_in_progress) {
+ if (!pg_ready(m)) {
busy = 1;
goto out;
}
@@ -1737,7 +1672,7 @@ out:
*---------------------------------------------------------------*/
static struct target_type multipath_target = {
.name = "multipath",
- .version = {1, 6, 0},
+ .version = {1, 7, 0},
.module = THIS_MODULE,
.ctr = multipath_ctr,
.dtr = multipath_dtr,
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 6a7f2b83a126..50601ec7017a 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -945,7 +945,7 @@ bool dm_table_request_based(struct dm_table *t)
return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED;
}
-int dm_table_alloc_md_mempools(struct dm_table *t)
+static int dm_table_alloc_md_mempools(struct dm_table *t)
{
unsigned type = dm_table_get_type(t);
unsigned per_bio_data_size = 0;
@@ -1618,6 +1618,25 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
}
EXPORT_SYMBOL(dm_table_get_md);
+void dm_table_run_md_queue_async(struct dm_table *t)
+{
+ struct mapped_device *md;
+ struct request_queue *queue;
+ unsigned long flags;
+
+ if (!dm_table_request_based(t))
+ return;
+
+ md = dm_table_get_md(t);
+ queue = dm_get_md_queue(md);
+ if (queue) {
+ spin_lock_irqsave(queue->queue_lock, flags);
+ blk_run_queue_async(queue);
+ spin_unlock_irqrestore(queue->queue_lock, flags);
+ }
+}
+EXPORT_SYMBOL(dm_table_run_md_queue_async);
+
static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
{
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index fb9efc829182..b086a945edcb 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -192,6 +192,13 @@ struct dm_pool_metadata {
* operation possible in this state is the closing of the device.
*/
bool fail_io:1;
+
+ /*
+ * Reading the space map roots can fail, so we read it into these
+ * buffers before the superblock is locked and updated.
+ */
+ __u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
+ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
};
struct dm_thin_device {
@@ -431,26 +438,53 @@ static void __setup_btree_details(struct dm_pool_metadata *pmd)
pmd->details_info.value_type.equal = NULL;
}
+static int save_sm_roots(struct dm_pool_metadata *pmd)
+{
+ int r;
+ size_t len;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &len);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_copy_root(pmd->metadata_sm, &pmd->metadata_space_map_root, len);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_root_size(pmd->data_sm, &len);
+ if (r < 0)
+ return r;
+
+ return dm_sm_copy_root(pmd->data_sm, &pmd->data_space_map_root, len);
+}
+
+static void copy_sm_roots(struct dm_pool_metadata *pmd,
+ struct thin_disk_superblock *disk)
+{
+ memcpy(&disk->metadata_space_map_root,
+ &pmd->metadata_space_map_root,
+ sizeof(pmd->metadata_space_map_root));
+
+ memcpy(&disk->data_space_map_root,
+ &pmd->data_space_map_root,
+ sizeof(pmd->data_space_map_root));
+}
+
static int __write_initial_superblock(struct dm_pool_metadata *pmd)
{
int r;
struct dm_block *sblock;
- size_t metadata_len, data_len;
struct thin_disk_superblock *disk_super;
sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
if (bdev_size > THIN_METADATA_MAX_SECTORS)
bdev_size = THIN_METADATA_MAX_SECTORS;
- r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
- if (r < 0)
- return r;
-
- r = dm_sm_root_size(pmd->data_sm, &data_len);
+ r = dm_sm_commit(pmd->data_sm);
if (r < 0)
return r;
- r = dm_sm_commit(pmd->data_sm);
+ r = save_sm_roots(pmd);
if (r < 0)
return r;
@@ -471,15 +505,7 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd)
disk_super->trans_id = 0;
disk_super->held_root = 0;
- r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0)
- goto bad_locked;
-
- r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
- data_len);
- if (r < 0)
- goto bad_locked;
+ copy_sm_roots(pmd, disk_super);
disk_super->data_mapping_root = cpu_to_le64(pmd->root);
disk_super->device_details_root = cpu_to_le64(pmd->details_root);
@@ -488,10 +514,6 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd)
disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
return dm_tm_commit(pmd->tm, sblock);
-
-bad_locked:
- dm_bm_unlock(sblock);
- return r;
}
static int __format_metadata(struct dm_pool_metadata *pmd)
@@ -769,6 +791,10 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
if (r < 0)
return r;
+ r = save_sm_roots(pmd);
+ if (r < 0)
+ return r;
+
r = superblock_lock(pmd, &sblock);
if (r)
return r;
@@ -780,21 +806,9 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
disk_super->trans_id = cpu_to_le64(pmd->trans_id);
disk_super->flags = cpu_to_le32(pmd->flags);
- r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
- metadata_len);
- if (r < 0)
- goto out_locked;
-
- r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
- data_len);
- if (r < 0)
- goto out_locked;
+ copy_sm_roots(pmd, disk_super);
return dm_tm_commit(pmd->tm, sblock);
-
-out_locked:
- dm_bm_unlock(sblock);
- return r;
}
struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index be70d38745f7..53728be84dee 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -12,9 +12,11 @@
#include <linux/dm-io.h>
#include <linux/dm-kcopyd.h>
#include <linux/list.h>
+#include <linux/rculist.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/rbtree.h>
#define DM_MSG_PREFIX "thin"
@@ -178,12 +180,10 @@ struct pool {
unsigned ref_count;
spinlock_t lock;
- struct bio_list deferred_bios;
struct bio_list deferred_flush_bios;
struct list_head prepared_mappings;
struct list_head prepared_discards;
-
- struct bio_list retry_on_resume_list;
+ struct list_head active_thins;
struct dm_deferred_set *shared_read_ds;
struct dm_deferred_set *all_io_ds;
@@ -220,6 +220,7 @@ struct pool_c {
* Target context for a thin.
*/
struct thin_c {
+ struct list_head list;
struct dm_dev *pool_dev;
struct dm_dev *origin_dev;
dm_thin_id dev_id;
@@ -227,6 +228,10 @@ struct thin_c {
struct pool *pool;
struct dm_thin_device *td;
bool requeue_mode:1;
+ spinlock_t lock;
+ struct bio_list deferred_bio_list;
+ struct bio_list retry_on_resume_list;
+ struct rb_root sort_bio_list; /* sorted list of deferred bios */
};
/*----------------------------------------------------------------*/
@@ -287,9 +292,9 @@ static void cell_defer_no_holder_no_free(struct thin_c *tc,
struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- dm_cell_release_no_holder(pool->prison, cell, &pool->deferred_bios);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ dm_cell_release_no_holder(pool->prison, cell, &tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -368,6 +373,7 @@ struct dm_thin_endio_hook {
struct dm_deferred_entry *shared_read_entry;
struct dm_deferred_entry *all_io_entry;
struct dm_thin_new_mapping *overwrite_mapping;
+ struct rb_node rb_node;
};
static void requeue_bio_list(struct thin_c *tc, struct bio_list *master)
@@ -378,30 +384,22 @@ static void requeue_bio_list(struct thin_c *tc, struct bio_list *master)
bio_list_init(&bios);
- spin_lock_irqsave(&tc->pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
bio_list_merge(&bios, master);
bio_list_init(master);
- spin_unlock_irqrestore(&tc->pool->lock, flags);
+ spin_unlock_irqrestore(&tc->lock, flags);
- while ((bio = bio_list_pop(&bios))) {
- struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
-
- if (h->tc == tc)
- bio_endio(bio, DM_ENDIO_REQUEUE);
- else
- bio_list_add(master, bio);
- }
+ while ((bio = bio_list_pop(&bios)))
+ bio_endio(bio, DM_ENDIO_REQUEUE);
}
static void requeue_io(struct thin_c *tc)
{
- struct pool *pool = tc->pool;
-
- requeue_bio_list(tc, &pool->deferred_bios);
- requeue_bio_list(tc, &pool->retry_on_resume_list);
+ requeue_bio_list(tc, &tc->deferred_bio_list);
+ requeue_bio_list(tc, &tc->retry_on_resume_list);
}
-static void error_retry_list(struct pool *pool)
+static void error_thin_retry_list(struct thin_c *tc)
{
struct bio *bio;
unsigned long flags;
@@ -409,15 +407,25 @@ static void error_retry_list(struct pool *pool)
bio_list_init(&bios);
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_merge(&bios, &pool->retry_on_resume_list);
- bio_list_init(&pool->retry_on_resume_list);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_merge(&bios, &tc->retry_on_resume_list);
+ bio_list_init(&tc->retry_on_resume_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
while ((bio = bio_list_pop(&bios)))
bio_io_error(bio);
}
+static void error_retry_list(struct pool *pool)
+{
+ struct thin_c *tc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tc, &pool->active_thins, list)
+ error_thin_retry_list(tc);
+ rcu_read_unlock();
+}
+
/*
* This section of code contains the logic for processing a thin device's IO.
* Much of the code depends on pool object resources (lists, workqueues, etc)
@@ -608,9 +616,9 @@ static void cell_defer(struct thin_c *tc, struct dm_bio_prison_cell *cell)
struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- cell_release(pool, cell, &pool->deferred_bios);
- spin_unlock_irqrestore(&tc->pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ cell_release(pool, cell, &tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -623,9 +631,9 @@ static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *c
struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- cell_release_no_holder(pool, cell, &pool->deferred_bios);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ cell_release_no_holder(pool, cell, &tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -1001,12 +1009,11 @@ static void retry_on_resume(struct bio *bio)
{
struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
struct thin_c *tc = h->tc;
- struct pool *pool = tc->pool;
unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_add(&pool->retry_on_resume_list, bio);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_add(&tc->retry_on_resume_list, bio);
+ spin_unlock_irqrestore(&tc->lock, flags);
}
static bool should_error_unserviceable_bio(struct pool *pool)
@@ -1363,38 +1370,111 @@ static int need_commit_due_to_time(struct pool *pool)
jiffies > pool->last_commit_jiffies + COMMIT_PERIOD;
}
-static void process_deferred_bios(struct pool *pool)
+#define thin_pbd(node) rb_entry((node), struct dm_thin_endio_hook, rb_node)
+#define thin_bio(pbd) dm_bio_from_per_bio_data((pbd), sizeof(struct dm_thin_endio_hook))
+
+static void __thin_bio_rb_add(struct thin_c *tc, struct bio *bio)
+{
+ struct rb_node **rbp, *parent;
+ struct dm_thin_endio_hook *pbd;
+ sector_t bi_sector = bio->bi_iter.bi_sector;
+
+ rbp = &tc->sort_bio_list.rb_node;
+ parent = NULL;
+ while (*rbp) {
+ parent = *rbp;
+ pbd = thin_pbd(parent);
+
+ if (bi_sector < thin_bio(pbd)->bi_iter.bi_sector)
+ rbp = &(*rbp)->rb_left;
+ else
+ rbp = &(*rbp)->rb_right;
+ }
+
+ pbd = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
+ rb_link_node(&pbd->rb_node, parent, rbp);
+ rb_insert_color(&pbd->rb_node, &tc->sort_bio_list);
+}
+
+static void __extract_sorted_bios(struct thin_c *tc)
+{
+ struct rb_node *node;
+ struct dm_thin_endio_hook *pbd;
+ struct bio *bio;
+
+ for (node = rb_first(&tc->sort_bio_list); node; node = rb_next(node)) {
+ pbd = thin_pbd(node);
+ bio = thin_bio(pbd);
+
+ bio_list_add(&tc->deferred_bio_list, bio);
+ rb_erase(&pbd->rb_node, &tc->sort_bio_list);
+ }
+
+ WARN_ON(!RB_EMPTY_ROOT(&tc->sort_bio_list));
+}
+
+static void __sort_thin_deferred_bios(struct thin_c *tc)
+{
+ struct bio *bio;
+ struct bio_list bios;
+
+ bio_list_init(&bios);
+ bio_list_merge(&bios, &tc->deferred_bio_list);
+ bio_list_init(&tc->deferred_bio_list);
+
+ /* Sort deferred_bio_list using rb-tree */
+ while ((bio = bio_list_pop(&bios)))
+ __thin_bio_rb_add(tc, bio);
+
+ /*
+ * Transfer the sorted bios in sort_bio_list back to
+ * deferred_bio_list to allow lockless submission of
+ * all bios.
+ */
+ __extract_sorted_bios(tc);
+}
+
+static void process_thin_deferred_bios(struct thin_c *tc)
{
+ struct pool *pool = tc->pool;
unsigned long flags;
struct bio *bio;
struct bio_list bios;
+ struct blk_plug plug;
+
+ if (tc->requeue_mode) {
+ requeue_bio_list(tc, &tc->deferred_bio_list);
+ return;
+ }
bio_list_init(&bios);
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_merge(&bios, &pool->deferred_bios);
- bio_list_init(&pool->deferred_bios);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
- while ((bio = bio_list_pop(&bios))) {
- struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
- struct thin_c *tc = h->tc;
+ if (bio_list_empty(&tc->deferred_bio_list)) {
+ spin_unlock_irqrestore(&tc->lock, flags);
+ return;
+ }
- if (tc->requeue_mode) {
- bio_endio(bio, DM_ENDIO_REQUEUE);
- continue;
- }
+ __sort_thin_deferred_bios(tc);
+
+ bio_list_merge(&bios, &tc->deferred_bio_list);
+ bio_list_init(&tc->deferred_bio_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
+
+ blk_start_plug(&plug);
+ while ((bio = bio_list_pop(&bios))) {
/*
* If we've got no free new_mapping structs, and processing
* this bio might require one, we pause until there are some
* prepared mappings to process.
*/
if (ensure_next_mapping(pool)) {
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_merge(&pool->deferred_bios, &bios);
- spin_unlock_irqrestore(&pool->lock, flags);
-
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_add(&tc->deferred_bio_list, bio);
+ bio_list_merge(&tc->deferred_bio_list, &bios);
+ spin_unlock_irqrestore(&tc->lock, flags);
break;
}
@@ -1403,6 +1483,20 @@ static void process_deferred_bios(struct pool *pool)
else
pool->process_bio(tc, bio);
}
+ blk_finish_plug(&plug);
+}
+
+static void process_deferred_bios(struct pool *pool)
+{
+ unsigned long flags;
+ struct bio *bio;
+ struct bio_list bios;
+ struct thin_c *tc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tc, &pool->active_thins, list)
+ process_thin_deferred_bios(tc);
+ rcu_read_unlock();
/*
* If there are any deferred flush bios, we must commit
@@ -1634,9 +1728,9 @@ static void thin_defer_bio(struct thin_c *tc, struct bio *bio)
unsigned long flags;
struct pool *pool = tc->pool;
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_add(&pool->deferred_bios, bio);
- spin_unlock_irqrestore(&pool->lock, flags);
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_add(&tc->deferred_bio_list, bio);
+ spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
@@ -1757,26 +1851,29 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
static int pool_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
{
- int r;
- unsigned long flags;
struct pool_c *pt = container_of(cb, struct pool_c, callbacks);
+ struct request_queue *q;
- spin_lock_irqsave(&pt->pool->lock, flags);
- r = !bio_list_empty(&pt->pool->retry_on_resume_list);
- spin_unlock_irqrestore(&pt->pool->lock, flags);
+ if (get_pool_mode(pt->pool) == PM_OUT_OF_DATA_SPACE)
+ return 1;
- if (!r) {
- struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
- r = bdi_congested(&q->backing_dev_info, bdi_bits);
- }
-
- return r;
+ q = bdev_get_queue(pt->data_dev->bdev);
+ return bdi_congested(&q->backing_dev_info, bdi_bits);
}
-static void __requeue_bios(struct pool *pool)
+static void requeue_bios(struct pool *pool)
{
- bio_list_merge(&pool->deferred_bios, &pool->retry_on_resume_list);
- bio_list_init(&pool->retry_on_resume_list);
+ unsigned long flags;
+ struct thin_c *tc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tc, &pool->active_thins, list) {
+ spin_lock_irqsave(&tc->lock, flags);
+ bio_list_merge(&tc->deferred_bio_list, &tc->retry_on_resume_list);
+ bio_list_init(&tc->retry_on_resume_list);
+ spin_unlock_irqrestore(&tc->lock, flags);
+ }
+ rcu_read_unlock();
}
/*----------------------------------------------------------------
@@ -1957,12 +2054,11 @@ static struct pool *pool_create(struct mapped_device *pool_md,
INIT_WORK(&pool->worker, do_worker);
INIT_DELAYED_WORK(&pool->waker, do_waker);
spin_lock_init(&pool->lock);
- bio_list_init(&pool->deferred_bios);
bio_list_init(&pool->deferred_flush_bios);
INIT_LIST_HEAD(&pool->prepared_mappings);
INIT_LIST_HEAD(&pool->prepared_discards);
+ INIT_LIST_HEAD(&pool->active_thins);
pool->low_water_triggered = false;
- bio_list_init(&pool->retry_on_resume_list);
pool->shared_read_ds = dm_deferred_set_create();
if (!pool->shared_read_ds) {
@@ -2507,8 +2603,8 @@ static void pool_resume(struct dm_target *ti)
spin_lock_irqsave(&pool->lock, flags);
pool->low_water_triggered = false;
- __requeue_bios(pool);
spin_unlock_irqrestore(&pool->lock, flags);
+ requeue_bios(pool);
do_waker(&pool->waker.work);
}
@@ -2947,7 +3043,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 11, 0},
+ .version = {1, 12, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -2968,6 +3064,12 @@ static struct target_type pool_target = {
static void thin_dtr(struct dm_target *ti)
{
struct thin_c *tc = ti->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc->pool->lock, flags);
+ list_del_rcu(&tc->list);
+ spin_unlock_irqrestore(&tc->pool->lock, flags);
+ synchronize_rcu();
mutex_lock(&dm_thin_pool_table.mutex);
@@ -3014,6 +3116,10 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
r = -ENOMEM;
goto out_unlock;
}
+ spin_lock_init(&tc->lock);
+ bio_list_init(&tc->deferred_bio_list);
+ bio_list_init(&tc->retry_on_resume_list);
+ tc->sort_bio_list = RB_ROOT;
if (argc == 3) {
r = dm_get_device(ti, argv[2], FMODE_READ, &origin_dev);
@@ -3085,6 +3191,17 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
mutex_unlock(&dm_thin_pool_table.mutex);
+ spin_lock(&tc->pool->lock);
+ list_add_tail_rcu(&tc->list, &tc->pool->active_thins);
+ spin_unlock(&tc->pool->lock);
+ /*
+ * This synchronize_rcu() call is needed here otherwise we risk a
+ * wake_worker() call finding no bios to process (because the newly
+ * added tc isn't yet visible). So this reduces latency since we
+ * aren't then dependent on the periodic commit to wake_worker().
+ */
+ synchronize_rcu();
+
return 0;
bad_target_max_io_len:
@@ -3250,7 +3367,7 @@ static int thin_iterate_devices(struct dm_target *ti,
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 11, 0},
+ .version = {1, 12, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 8c53b09b9a2c..455e64916498 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -94,13 +94,6 @@ struct dm_rq_clone_bio_info {
struct bio clone;
};
-union map_info *dm_get_mapinfo(struct bio *bio)
-{
- if (bio && bio->bi_private)
- return &((struct dm_target_io *)bio->bi_private)->info;
- return NULL;
-}
-
union map_info *dm_get_rq_mapinfo(struct request *rq)
{
if (rq && rq->end_io_data)
@@ -475,6 +468,11 @@ sector_t dm_get_size(struct mapped_device *md)
return get_capacity(md->disk);
}
+struct request_queue *dm_get_md_queue(struct mapped_device *md)
+{
+ return md->queue;
+}
+
struct dm_stats *dm_get_stats(struct mapped_device *md)
{
return &md->stats;
@@ -760,7 +758,7 @@ static void dec_pending(struct dm_io *io, int error)
static void clone_endio(struct bio *bio, int error)
{
int r = 0;
- struct dm_target_io *tio = bio->bi_private;
+ struct dm_target_io *tio = container_of(bio, struct dm_target_io, clone);
struct dm_io *io = tio->io;
struct mapped_device *md = tio->io->md;
dm_endio_fn endio = tio->ti->type->end_io;
@@ -794,7 +792,8 @@ static void clone_endio(struct bio *bio, int error)
*/
static void end_clone_bio(struct bio *clone, int error)
{
- struct dm_rq_clone_bio_info *info = clone->bi_private;
+ struct dm_rq_clone_bio_info *info =
+ container_of(clone, struct dm_rq_clone_bio_info, clone);
struct dm_rq_target_io *tio = info->tio;
struct bio *bio = info->orig;
unsigned int nr_bytes = info->orig->bi_iter.bi_size;
@@ -1120,7 +1119,6 @@ static void __map_bio(struct dm_target_io *tio)
struct dm_target *ti = tio->ti;
clone->bi_end_io = clone_endio;
- clone->bi_private = tio;
/*
* Map the clone. If r == 0 we don't need to do
@@ -1195,7 +1193,6 @@ static struct dm_target_io *alloc_tio(struct clone_info *ci,
tio->io = ci->io;
tio->ti = ti;
- memset(&tio->info, 0, sizeof(tio->info));
tio->target_bio_nr = target_bio_nr;
return tio;
@@ -1530,7 +1527,6 @@ static int dm_rq_bio_constructor(struct bio *bio, struct bio *bio_orig,
info->orig = bio_orig;
info->tio = tio;
bio->bi_end_io = end_clone_bio;
- bio->bi_private = info;
return 0;
}
@@ -2172,7 +2168,7 @@ static struct dm_table *__unbind(struct mapped_device *md)
return NULL;
dm_table_event_callback(map, NULL, NULL);
- rcu_assign_pointer(md->map, NULL);
+ RCU_INIT_POINTER(md->map, NULL);
dm_sync_table(md);
return map;
@@ -2873,8 +2869,6 @@ static const struct block_device_operations dm_blk_dops = {
.owner = THIS_MODULE
};
-EXPORT_SYMBOL(dm_get_mapinfo);
-
/*
* module hooks
*/
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index c4569f02f50f..ed76126aac54 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -73,7 +73,6 @@ unsigned dm_table_get_type(struct dm_table *t);
struct target_type *dm_table_get_immutable_target_type(struct dm_table *t);
bool dm_table_request_based(struct dm_table *t);
bool dm_table_supports_discards(struct dm_table *t);
-int dm_table_alloc_md_mempools(struct dm_table *t);
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
@@ -189,6 +188,7 @@ int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only
int dm_cancel_deferred_remove(struct mapped_device *md);
int dm_request_based(struct mapped_device *md);
sector_t dm_get_size(struct mapped_device *md);
+struct request_queue *dm_get_md_queue(struct mapped_device *md);
struct dm_stats *dm_get_stats(struct mapped_device *md);
int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
diff --git a/drivers/md/persistent-data/dm-bitset.c b/drivers/md/persistent-data/dm-bitset.c
index cd9a86d4cdf0..36f7cc2c7109 100644
--- a/drivers/md/persistent-data/dm-bitset.c
+++ b/drivers/md/persistent-data/dm-bitset.c
@@ -65,7 +65,7 @@ int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root,
int r;
__le64 value;
- if (!info->current_index_set)
+ if (!info->current_index_set || !info->dirty)
return 0;
value = cpu_to_le64(info->current_bits);
@@ -77,6 +77,8 @@ int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root,
return r;
info->current_index_set = false;
+ info->dirty = false;
+
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_flush);
@@ -94,6 +96,8 @@ static int read_bits(struct dm_disk_bitset *info, dm_block_t root,
info->current_bits = le64_to_cpu(value);
info->current_index_set = true;
info->current_index = array_index;
+ info->dirty = false;
+
return 0;
}
@@ -126,6 +130,8 @@ int dm_bitset_set_bit(struct dm_disk_bitset *info, dm_block_t root,
return r;
set_bit(b, (unsigned long *) &info->current_bits);
+ info->dirty = true;
+
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_set_bit);
@@ -141,6 +147,8 @@ int dm_bitset_clear_bit(struct dm_disk_bitset *info, dm_block_t root,
return r;
clear_bit(b, (unsigned long *) &info->current_bits);
+ info->dirty = true;
+
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_clear_bit);
diff --git a/drivers/md/persistent-data/dm-bitset.h b/drivers/md/persistent-data/dm-bitset.h
index e1b9bea14aa1..c2287d672ef5 100644
--- a/drivers/md/persistent-data/dm-bitset.h
+++ b/drivers/md/persistent-data/dm-bitset.h
@@ -71,6 +71,7 @@ struct dm_disk_bitset {
uint64_t current_bits;
bool current_index_set:1;
+ bool dirty:1;
};
/*
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 455f79279a16..087411c95ffc 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -595,25 +595,14 @@ int dm_bm_unlock(struct dm_block *b)
}
EXPORT_SYMBOL_GPL(dm_bm_unlock);
-int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
- struct dm_block *superblock)
+int dm_bm_flush(struct dm_block_manager *bm)
{
- int r;
-
if (bm->read_only)
return -EPERM;
- r = dm_bufio_write_dirty_buffers(bm->bufio);
- if (unlikely(r)) {
- dm_bm_unlock(superblock);
- return r;
- }
-
- dm_bm_unlock(superblock);
-
return dm_bufio_write_dirty_buffers(bm->bufio);
}
-EXPORT_SYMBOL_GPL(dm_bm_flush_and_unlock);
+EXPORT_SYMBOL_GPL(dm_bm_flush);
void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
{
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
index 13cd58e1fe69..1b95dfc17786 100644
--- a/drivers/md/persistent-data/dm-block-manager.h
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -105,8 +105,7 @@ int dm_bm_unlock(struct dm_block *b);
*
* This method always blocks.
*/
-int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
- struct dm_block *superblock);
+int dm_bm_flush(struct dm_block_manager *bm);
/*
* Request data is prefetched into the cache.
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c
index 81da1a26042e..3bc30a0ae3d6 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.c
+++ b/drivers/md/persistent-data/dm-transaction-manager.c
@@ -154,7 +154,7 @@ int dm_tm_pre_commit(struct dm_transaction_manager *tm)
if (r < 0)
return r;
- return 0;
+ return dm_bm_flush(tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_pre_commit);
@@ -164,8 +164,9 @@ int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
return -EWOULDBLOCK;
wipe_shadow_table(tm);
+ dm_bm_unlock(root);
- return dm_bm_flush_and_unlock(tm->bm, root);
+ return dm_bm_flush(tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_commit);
diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h
index b5b139076ca5..2772ed2a781a 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.h
+++ b/drivers/md/persistent-data/dm-transaction-manager.h
@@ -38,18 +38,17 @@ struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transac
/*
* We use a 2-phase commit here.
*
- * i) In the first phase the block manager is told to start flushing, and
- * the changes to the space map are written to disk. You should interrogate
- * your particular space map to get detail of its root node etc. to be
- * included in your superblock.
+ * i) Make all changes for the transaction *except* for the superblock.
+ * Then call dm_tm_pre_commit() to flush them to disk.
*
- * ii) @root will be committed last. You shouldn't use more than the
- * first 512 bytes of @root if you wish the transaction to survive a power
- * failure. You *must* have a write lock held on @root for both stage (i)
- * and (ii). The commit will drop the write lock.
+ * ii) Lock your superblock. Update. Then call dm_tm_commit() which will
+ * unlock the superblock and flush it. No other blocks should be updated
+ * during this period. Care should be taken to never unlock a partially
+ * updated superblock; perform any operations that could fail *before* you
+ * take the superblock lock.
*/
int dm_tm_pre_commit(struct dm_transaction_manager *tm);
-int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root);
+int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *superblock);
/*
* These methods are the only way to get hold of a writeable block.
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index c930be30eb7e..441053be7f55 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
camera sensor with an embedded SoC image signal processor.
+config VIDEO_S5K6A3
+ tristate "Samsung S5K6A3 sensor support"
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6A3 raw
+ camera sensor.
+
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 01b6bfc0db5b..01ae9328e582 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o
obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index e7f555cc827a..a4459301b5f8 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/sizes.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
@@ -23,7 +23,9 @@
#include <linux/init.h>
#include <linux/media.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/videodev2.h>
@@ -33,6 +35,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/s5c73m3.h>
+#include <media/v4l2-of.h>
#include "s5c73m3.h"
@@ -46,6 +49,8 @@ static int update_fw;
module_param(update_fw, int, 0644);
#define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K
+#define S5C73M3_MIPI_DATA_LANES 4
+#define S5C73M3_CLK_NAME "cis_extclk"
static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {
"vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */
@@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {
ret = regulator_enable(state->supplies[i].consumer);
if (ret)
- goto err;
+ goto err_reg_dis;
}
+ ret = clk_set_rate(state->clock, state->mclk_frequency);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ ret = clk_prepare_enable(state->clock);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n",
+ clk_get_rate(state->clock));
+
s5c73m3_gpio_deassert(state, STBY);
usleep_range(100, 200);
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
usleep_range(50, 100);
return 0;
-err:
+
+err_reg_dis:
for (--i; i >= 0; i--)
regulator_disable(state->supplies[i].consumer);
return ret;
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (s5c73m3_gpio_assert(state, STBY))
usleep_range(100, 200);
+
+ clk_disable_unprepare(state->clock);
+
state->streaming = 0;
state->isp_ready = 0;
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (ret)
goto err;
}
+
return 0;
err:
for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
@@ -1396,6 +1417,8 @@ err:
v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
state->supplies[i].supply, r);
}
+
+ clk_prepare_enable(state->clock);
return ret;
}
@@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)
S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- mutex_lock(&state->lock);
- ret = __s5c73m3_power_on(state);
- if (ret == 0)
- s5c73m3_get_fw_version(state);
-
- __s5c73m3_power_off(state);
- mutex_unlock(&state->lock);
-
- v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n",
- __func__, ret ? "failed" : "succeeded", ret);
-
return ret;
}
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_configure_gpios(struct s5c73m3 *state,
- const struct s5c73m3_platform_data *pdata)
+static int s5c73m3_configure_gpios(struct s5c73m3 *state)
+{
+ static const char * const gpio_names[] = {
+ "S5C73M3_STBY", "S5C73M3_RST"
+ };
+ struct i2c_client *c = state->i2c_client;
+ struct s5c73m3_gpio *g = state->gpio;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ unsigned int flags = GPIOF_DIR_OUT;
+ if (g[i].level)
+ flags |= GPIOF_INIT_HIGH;
+ ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags,
+ gpio_names[i]);
+ if (ret) {
+ v4l2_err(c, "failed to request gpio %s\n",
+ gpio_names[i]);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s5c73m3_parse_gpios(struct s5c73m3 *state)
+{
+ static const char * const prop_names[] = {
+ "standby-gpios", "xshutdown-gpios",
+ };
+ struct device *dev = &state->i2c_client->dev;
+ struct device_node *node = dev->of_node;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ enum of_gpio_flags of_flags;
+
+ ret = of_get_named_gpio_flags(node, prop_names[i],
+ 0, &of_flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse %s DT property\n",
+ prop_names[i]);
+ return -EINVAL;
+ }
+ state->gpio[i].gpio = ret;
+ state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW);
+ }
+ return 0;
+}
+
+static int s5c73m3_get_platform_data(struct s5c73m3 *state)
{
struct device *dev = &state->i2c_client->dev;
- const struct s5c73m3_gpio *gpio;
- unsigned long flags;
+ const struct s5c73m3_platform_data *pdata = dev->platform_data;
+ struct device_node *node = dev->of_node;
+ struct device_node *node_ep;
+ struct v4l2_of_endpoint ep;
int ret;
- state->gpio[STBY].gpio = -EINVAL;
- state->gpio[RST].gpio = -EINVAL;
+ if (!node) {
+ if (!pdata) {
+ dev_err(dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
- gpio = &pdata->gpio_stby;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_STBY");
- if (ret < 0)
- return ret;
+ state->mclk_frequency = pdata->mclk_frequency;
+ state->gpio[STBY] = pdata->gpio_stby;
+ state->gpio[RST] = pdata->gpio_reset;
+ return 0;
+ }
+
+ state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
+ if (IS_ERR(state->clock))
+ return PTR_ERR(state->clock);
- state->gpio[STBY] = *gpio;
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->mclk_frequency)) {
+ state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ state->mclk_frequency);
}
- gpio = &pdata->gpio_reset;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_RST");
- if (ret < 0)
- return ret;
+ ret = s5c73m3_parse_gpios(state);
+ if (ret < 0)
+ return -EINVAL;
- state->gpio[RST] = *gpio;
+ node_ep = v4l2_of_get_next_endpoint(node, NULL);
+ if (!node_ep) {
+ dev_warn(dev, "no endpoint defined for node: %s\n",
+ node->full_name);
+ return 0;
}
+ v4l2_of_parse_endpoint(node_ep, &ep);
+ of_node_put(node_ep);
+
+ if (ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "unsupported bus type\n");
+ return -EINVAL;
+ }
+ /*
+ * Number of MIPI CSI-2 data lanes is currently not configurable,
+ * always a default value of 4 lanes is used.
+ */
+ if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES)
+ dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n");
+
return 0;
}
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- const struct s5c73m3_platform_data *pdata = client->dev.platform_data;
struct v4l2_subdev *sd;
struct v4l2_subdev *oif_sd;
struct s5c73m3 *state;
int ret, i;
- if (pdata == NULL) {
- dev_err(&client->dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
+ state->i2c_client = client;
+ ret = s5c73m3_get_platform_data(state);
+ if (ret < 0)
+ return ret;
+
mutex_init(&state->lock);
sd = &state->sensor_sd;
oif_sd = &state->oif_sd;
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- state->mclk_frequency = pdata->mclk_frequency;
- state->bus_type = pdata->bus_type;
- state->i2c_client = client;
-
- ret = s5c73m3_configure_gpios(state, pdata);
+ ret = s5c73m3_configure_gpios(state);
if (ret)
goto out_err;
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
goto out_err;
+ oif_sd->dev = dev;
+
+ ret = __s5c73m3_power_on(state);
+ if (ret < 0)
+ goto out_err1;
+
+ ret = s5c73m3_get_fw_version(state);
+ __s5c73m3_power_off(state);
+
+ if (ret < 0) {
+ dev_err(dev, "Device detection failed: %d\n", ret);
+ goto out_err1;
+ }
+
+ ret = v4l2_async_register_subdev(oif_sd);
+ if (ret < 0)
+ goto out_err1;
+
v4l2_info(sd, "%s: completed successfully\n", __func__);
return 0;
+out_err1:
+ s5c73m3_unregister_spi_driver(state);
out_err:
media_entity_cleanup(&sd->entity);
return ret;
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)
struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);
struct v4l2_subdev *sensor_sd = &state->sensor_sd;
- v4l2_device_unregister_subdev(oif_sd);
+ v4l2_async_unregister_subdev(oif_sd);
v4l2_ctrl_handler_free(oif_sd->ctrl_handler);
media_entity_cleanup(&oif_sd->entity);
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = {
};
MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
+#ifdef CONFIG_OF
+static const struct of_device_id s5c73m3_of_match[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s5c73m3_of_match);
+#endif
+
static struct i2c_driver s5c73m3_i2c_driver = {
.driver = {
+ .of_match_table = of_match_ptr(s5c73m3_of_match),
.name = DRIVER_NAME,
},
.probe = s5c73m3_probe,
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index 8079e26eb5e2..f60b265b4da1 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -27,6 +27,11 @@
#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
+static const struct of_device_id s5c73m3_spi_ids[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+
enum spi_direction {
SPI_DIR_RX,
SPI_DIR_TX
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)
spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
spidrv->driver.bus = &spi_bus_type;
spidrv->driver.owner = THIS_MODULE;
+ spidrv->driver.of_match_table = s5c73m3_spi_ids;
return spi_register_driver(spidrv);
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 9dfa516f6944..9656b6723dc6 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -17,6 +17,7 @@
#ifndef S5C73M3_H_
#define S5C73M3_H_
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-common.h>
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {
#define S5C73M3_MAX_SUPPLIES 6
+#define S5C73M3_DEFAULT_MCLK_FREQ 24000000U
struct s5c73m3_ctrls {
struct v4l2_ctrl_handler handler;
@@ -391,6 +393,8 @@ struct s5c73m3 {
struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
struct s5c73m3_gpio gpio[GPIO_NUM];
+ struct clk *clock;
+
/* External master clock frequency */
u32 mclk_frequency;
/* Video bus type - MIPI-CSI2/parallel */
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
new file mode 100644
index 000000000000..7bc2271ca009
--- /dev/null
+++ b/drivers/media/i2c/s5k6a3.c
@@ -0,0 +1,389 @@
+/*
+ * Samsung S5K6A3 image sensor driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-subdev.h>
+
+#define S5K6A3_SENSOR_MAX_WIDTH 1412
+#define S5K6A3_SENSOR_MAX_HEIGHT 1412
+#define S5K6A3_SENSOR_MIN_WIDTH 32
+#define S5K6A3_SENSOR_MIN_HEIGHT 32
+
+#define S5K6A3_DEFAULT_WIDTH 1296
+#define S5K6A3_DEFAULT_HEIGHT 732
+
+#define S5K6A3_DRV_NAME "S5K6A3"
+#define S5K6A3_CLK_NAME "extclk"
+#define S5K6A3_DEFAULT_CLK_FREQ 24000000U
+
+enum {
+ S5K6A3_SUPP_VDDA,
+ S5K6A3_SUPP_VDDIO,
+ S5K6A3_SUPP_AFVDD,
+ S5K6A3_NUM_SUPPLIES,
+};
+
+/**
+ * struct s5k6a3 - fimc-is sensor data structure
+ * @dev: pointer to this I2C client device structure
+ * @subdev: the image sensor's v4l2 subdev
+ * @pad: subdev media source pad
+ * @supplies: image sensor's voltage regulator supplies
+ * @gpio_reset: GPIO connected to the sensor's reset pin
+ * @lock: mutex protecting the structure's members below
+ * @format: media bus format at the sensor's source pad
+ */
+struct s5k6a3 {
+ struct device *dev;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
+ int gpio_reset;
+ struct mutex lock;
+ struct v4l2_mbus_framefmt format;
+ struct clk *clock;
+ u32 clock_frequency;
+ int power_count;
+};
+
+static const char * const s5k6a3_supply_names[] = {
+ [S5K6A3_SUPP_VDDA] = "svdda",
+ [S5K6A3_SUPP_VDDIO] = "svddio",
+ [S5K6A3_SUPP_AFVDD] = "afvdd",
+};
+
+static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6a3, subdev);
+}
+
+static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ }
+};
+
+static const struct v4l2_mbus_framefmt *find_sensor_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
+ if (mf->code == s5k6a3_formats[i].code)
+ return &s5k6a3_formats[i];
+
+ return &s5k6a3_formats[0];
+}
+
+static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6a3_formats))
+ return -EINVAL;
+
+ code->code = s5k6a3_formats[code->index].code;
+ return 0;
+}
+
+static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = find_sensor_format(mf);
+ mf->code = fmt->code;
+ v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
+ S5K6A3_SENSOR_MAX_WIDTH, 0,
+ &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
+ S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
+}
+
+static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
+ struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
+ u32 pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+ return &sensor->format;
+}
+
+static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ s5k6a3_try_format(&fmt->format);
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+ if (mf) {
+ mutex_lock(&sensor->lock);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ *mf = fmt->format;
+ mutex_unlock(&sensor->lock);
+ }
+ return 0;
+}
+
+static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *mf;
+ mutex_unlock(&sensor->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
+ .enum_mbus_code = s5k6a3_enum_mbus_code,
+ .get_fmt = s5k6a3_get_fmt,
+ .set_fmt = s5k6a3_set_fmt,
+};
+
+static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ *format = s5k6a3_formats[0];
+ format->width = S5K6A3_DEFAULT_WIDTH;
+ format->height = S5K6A3_DEFAULT_HEIGHT;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
+ .open = s5k6a3_open,
+};
+
+static int __s5k6a3_power_on(struct s5k6a3 *sensor)
+{
+ int i = S5K6A3_SUPP_VDDA;
+ int ret;
+
+ ret = clk_set_rate(sensor->clock, sensor->clock_frequency);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_get(sensor->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_rpm_put;
+
+ ret = clk_prepare_enable(sensor->clock);
+ if (ret < 0)
+ goto error_reg_dis;
+
+ for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_reg_dis;
+ }
+
+ gpio_set_value(sensor->gpio_reset, 1);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 0);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 1);
+
+ /* Delay needed for the sensor initialization */
+ msleep(20);
+ return 0;
+
+error_reg_dis:
+ for (--i; i >= 0; --i)
+ regulator_disable(sensor->supplies[i].consumer);
+error_rpm_put:
+ pm_runtime_put(sensor->dev);
+ return ret;
+}
+
+static int __s5k6a3_power_off(struct s5k6a3 *sensor)
+{
+ int i;
+
+ gpio_set_value(sensor->gpio_reset, 0);
+
+ for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
+ regulator_disable(sensor->supplies[i].consumer);
+
+ clk_disable_unprepare(sensor->clock);
+ pm_runtime_put(sensor->dev);
+ return 0;
+}
+
+static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->power_count == !on) {
+ if (on)
+ ret = __s5k6a3_power_on(sensor);
+ else
+ ret = __s5k6a3_power_off(sensor);
+
+ if (ret == 0)
+ sensor->power_count += on ? 1 : -1;
+ }
+
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops s5k6a3_core_ops = {
+ .s_power = s5k6a3_s_power,
+};
+
+static struct v4l2_subdev_ops s5k6a3_subdev_ops = {
+ .core = &s5k6a3_core_ops,
+ .pad = &s5k6a3_pad_ops,
+};
+
+static int s5k6a3_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct s5k6a3 *sensor;
+ struct v4l2_subdev *sd;
+ int gpio, i, ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+ sensor->gpio_reset = -EINVAL;
+ sensor->clock = ERR_PTR(-EINVAL);
+ sensor->dev = dev;
+
+ sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
+ if (IS_ERR(sensor->clock))
+ return PTR_ERR(sensor->clock);
+
+ gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
+ if (!gpio_is_valid(gpio))
+ return gpio;
+
+ ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
+ S5K6A3_DRV_NAME);
+ if (ret < 0)
+ return ret;
+
+ sensor->gpio_reset = gpio;
+
+ if (of_property_read_u32(dev->of_node, "clock-frequency",
+ &sensor->clock_frequency)) {
+ sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ sensor->clock_frequency);
+ }
+
+ for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
+ sensor->supplies[i].supply = s5k6a3_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ sd = &sensor->subdev;
+ v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &s5k6a3_sd_internal_ops;
+
+ sensor->format.code = s5k6a3_formats[0].code;
+ sensor->format.width = S5K6A3_DEFAULT_WIDTH;
+ sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
+
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(sd);
+
+ if (ret < 0) {
+ pm_runtime_disable(&client->dev);
+ media_entity_cleanup(&sd->entity);
+ }
+
+ return ret;
+}
+
+static int s5k6a3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ return 0;
+}
+
+static const struct i2c_device_id s5k6a3_ids[] = {
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id s5k6a3_of_match[] = {
+ { .compatible = "samsung,s5k6a3" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
+#endif
+
+static struct i2c_driver s5k6a3_driver = {
+ .driver = {
+ .of_match_table = of_match_ptr(s5k6a3_of_match),
+ .name = S5K6A3_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = s5k6a3_probe,
+ .remove = s5k6a3_remove,
+ .id_table = s5k6a3_ids,
+};
+
+module_i2c_driver(s5k6a3_driver);
+
+MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 01ed1ecdff7e..e1b2ceba00c1 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -64,4 +64,13 @@ config VIDEO_EXYNOS4_FIMC_IS
To compile this driver as a module, choose M here: the
module will be called exynos4-fimc-is.
+config VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+ bool "EXYNOS4x12 FIMC-IS ISP Direct DMA capture support"
+ depends on VIDEO_EXYNOS4_FIMC_IS
+ select VIDEO_EXYNOS4_IS_COMMON
+ default y
+ help
+ This option enables an additional video device node exposing a V4L2
+ video capture interface for the FIMC-IS ISP raw (Bayer) capture DMA.
+
endif # VIDEO_SAMSUNG_EXYNOS4_IS
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile
index c2ff29ba6856..eed1b185d813 100644
--- a/drivers/media/platform/exynos4-is/Makefile
+++ b/drivers/media/platform/exynos4-is/Makefile
@@ -6,6 +6,10 @@ exynos4-is-common-objs := common.o
exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
+ifeq ($(CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE),y)
+exynos-fimc-is-objs += fimc-isp-video.o
+endif
+
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c
index 9bf3ddd9e028..bf1465d1bf6d 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.c
@@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
__hw_param_copy(dst, src);
}
-static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
{
struct is_param_region *par = &is->is_p_region->parameter;
struct chain_config *cfg = &is->config[is->config_index];
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h
index f9358c27ae2d..8e31f7642776 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.h
@@ -911,6 +911,10 @@ struct is_region {
u32 shared[MAX_SHARED_COUNT];
} __packed;
+/* Offset to the ISP DMA2 output buffer address array. */
+#define DMA2_OUTPUT_ADDR_ARRAY_OFFS \
+ (offsetof(struct is_region, shared) + 32 * sizeof(u32))
+
struct is_debug_frame_descriptor {
u32 sensor_frame_time;
u32 sensor_exposure_time;
@@ -988,6 +992,7 @@ struct sensor_open_extended {
struct fimc_is;
int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is);
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset);
void fimc_is_set_initial_params(struct fimc_is *is);
unsigned int __get_pending_param_count(struct fimc_is *is);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index 2628733c4e10..cfe4406a83ff 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -105,6 +105,20 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)
return 0;
}
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask)
+{
+ if (hweight32(mask) == 1) {
+ dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n",
+ __func__, mask);
+ return;
+ }
+
+ if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0)
+ dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n");
+
+ mcuctl_write(mask, is, MCUCTL_REG_ISSR(23));
+}
+
void fimc_is_hw_set_sensor_num(struct fimc_is *is)
{
pr_debug("setting sensor index to: %d\n", is->sensor_index);
@@ -112,7 +126,7 @@ void fimc_is_hw_set_sensor_num(struct fimc_is *is)
mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
- mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3));
+ mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));
}
void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h
index 1d9d4ffc6ad5..141e5ddadbeb 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h
@@ -147,6 +147,7 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num);
void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is);
int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is);
void fimc_is_hw_set_sensor_num(struct fimc_is *is);
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask);
void fimc_is_hw_stream_on(struct fimc_is *is);
void fimc_is_hw_stream_off(struct fimc_is *is);
int fimc_is_hw_set_param(struct fimc_is *is);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
index 6647421e5d3a..10e82e21b5d1 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
@@ -2,276 +2,21 @@
* Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
- *
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_gpio.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <media/v4l2-subdev.h>
-#include "fimc-is.h"
#include "fimc-is-sensor.h"
-#define DRIVER_NAME "FIMC-IS-SENSOR"
-
-static const char * const sensor_supply_names[] = {
- "svdda",
- "svddio",
-};
-
-static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = {
- {
- .code = V4L2_MBUS_FMT_SGRBG10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .field = V4L2_FIELD_NONE,
- }
-};
-
-static const struct v4l2_mbus_framefmt *find_sensor_format(
- struct v4l2_mbus_framefmt *mf)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++)
- if (mf->code == fimc_is_sensor_formats[i].code)
- return &fimc_is_sensor_formats[i];
-
- return &fimc_is_sensor_formats[0];
-}
-
-static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats))
- return -EINVAL;
-
- code->code = fimc_is_sensor_formats[code->index].code;
- return 0;
-}
-
-static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor,
- struct v4l2_mbus_framefmt *mf)
-{
- const struct sensor_drv_data *dd = sensor->drvdata;
- const struct v4l2_mbus_framefmt *fmt;
-
- fmt = find_sensor_format(mf);
- mf->code = fmt->code;
- v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0,
- &mf->height, 12 + 8, dd->height, 0, 0);
-}
-
-static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format(
- struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh,
- u32 pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
-
- return &sensor->format;
-}
-
-static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- struct v4l2_mbus_framefmt *mf;
-
- fimc_is_sensor_try_format(sensor, &fmt->format);
-
- mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
- if (mf) {
- mutex_lock(&sensor->lock);
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- *mf = fmt->format;
- mutex_unlock(&sensor->lock);
- }
- return 0;
-}
-
-static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- struct v4l2_mbus_framefmt *mf;
-
- mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
-
- mutex_lock(&sensor->lock);
- fmt->format = *mf;
- mutex_unlock(&sensor->lock);
- return 0;
-}
-
-static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = {
- .enum_mbus_code = fimc_is_sensor_enum_mbus_code,
- .get_fmt = fimc_is_sensor_get_fmt,
- .set_fmt = fimc_is_sensor_set_fmt,
-};
-
-static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-
- *format = fimc_is_sensor_formats[0];
- format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
- format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
- return 0;
-}
-
-static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = {
- .open = fimc_is_sensor_open,
-};
-
-static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- int gpio = sensor->gpio_reset;
- int ret;
-
- if (on) {
- ret = pm_runtime_get(sensor->dev);
- if (ret < 0)
- return ret;
-
- ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (ret < 0) {
- pm_runtime_put(sensor->dev);
- return ret;
- }
- if (gpio_is_valid(gpio)) {
- gpio_set_value(gpio, 1);
- usleep_range(600, 800);
- gpio_set_value(gpio, 0);
- usleep_range(10000, 11000);
- gpio_set_value(gpio, 1);
- }
-
- /* A delay needed for the sensor initialization. */
- msleep(20);
- } else {
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, 0);
-
- ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (!ret)
- pm_runtime_put(sensor->dev);
- }
-
- pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret);
-
- return ret;
-}
-
-static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = {
- .s_power = fimc_is_sensor_s_power,
-};
-
-static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = {
- .core = &fimc_is_sensor_core_ops,
- .pad = &fimc_is_sensor_pad_ops,
-};
-
-static const struct of_device_id fimc_is_sensor_of_match[];
-
-static int fimc_is_sensor_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device *dev = &client->dev;
- struct fimc_is_sensor *sensor;
- const struct of_device_id *of_id;
- struct v4l2_subdev *sd;
- int gpio, i, ret;
-
- sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
- if (!sensor)
- return -ENOMEM;
-
- mutex_init(&sensor->lock);
- sensor->gpio_reset = -EINVAL;
-
- gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
- DRIVER_NAME);
- if (ret < 0)
- return ret;
- }
- sensor->gpio_reset = gpio;
-
- for (i = 0; i < SENSOR_NUM_SUPPLIES; i++)
- sensor->supplies[i].supply = sensor_supply_names[i];
-
- ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (ret < 0)
- return ret;
-
- of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node);
- if (!of_id)
- return -ENODEV;
-
- sensor->drvdata = of_id->data;
- sensor->dev = dev;
-
- sd = &sensor->subdev;
- v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops);
- snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name);
- sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- sensor->format.code = fimc_is_sensor_formats[0].code;
- sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
- sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
- sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
- if (ret < 0)
- return ret;
-
- pm_runtime_no_callbacks(dev);
- pm_runtime_enable(dev);
-
- return ret;
-}
-
-static int fimc_is_sensor_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- media_entity_cleanup(&sd->entity);
- return 0;
-}
-
-static const struct i2c_device_id fimc_is_sensor_ids[] = {
- { }
-};
-
static const struct sensor_drv_data s5k6a3_drvdata = {
.id = FIMC_IS_SENSOR_ID_S5K6A3,
- .subdev_name = "S5K6A3",
- .width = S5K6A3_SENSOR_WIDTH,
- .height = S5K6A3_SENSOR_HEIGHT,
+ .open_timeout = S5K6A3_OPEN_TIMEOUT,
};
-static const struct of_device_id fimc_is_sensor_of_match[] = {
+static const struct of_device_id fimc_is_sensor_of_ids[] = {
{
.compatible = "samsung,s5k6a3",
.data = &s5k6a3_drvdata,
@@ -279,27 +24,11 @@ static const struct of_device_id fimc_is_sensor_of_match[] = {
{ }
};
-static struct i2c_driver fimc_is_sensor_driver = {
- .driver = {
- .of_match_table = fimc_is_sensor_of_match,
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
- .probe = fimc_is_sensor_probe,
- .remove = fimc_is_sensor_remove,
- .id_table = fimc_is_sensor_ids,
-};
-
-int fimc_is_register_sensor_driver(void)
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node)
{
- return i2c_add_driver(&fimc_is_sensor_driver);
-}
+ const struct of_device_id *of_id;
-void fimc_is_unregister_sensor_driver(void)
-{
- i2c_del_driver(&fimc_is_sensor_driver);
+ of_id = of_match_node(fimc_is_sensor_of_ids, node);
+ return of_id ? of_id->data : NULL;
}
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
index 6036d49a6c68..173ccffa4bcd 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
@@ -13,24 +13,13 @@
#ifndef FIMC_IS_SENSOR_H_
#define FIMC_IS_SENSOR_H_
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-
-#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */
-
-#define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296
-#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732
+#include <linux/of.h>
+#include <linux/types.h>
+#define S5K6A3_OPEN_TIMEOUT 2000 /* ms */
#define S5K6A3_SENSOR_WIDTH 1392
#define S5K6A3_SENSOR_HEIGHT 1392
-#define SENSOR_NUM_SUPPLIES 2
-
enum fimc_is_sensor_id {
FIMC_IS_SENSOR_ID_S5K3H2 = 1,
FIMC_IS_SENSOR_ID_S5K6A3,
@@ -45,45 +34,23 @@ enum fimc_is_sensor_id {
struct sensor_drv_data {
enum fimc_is_sensor_id id;
- const char * const subdev_name;
- unsigned int width;
- unsigned int height;
+ /* sensor open timeout in ms */
+ unsigned short open_timeout;
};
/**
* struct fimc_is_sensor - fimc-is sensor data structure
- * @dev: pointer to this I2C client device structure
- * @subdev: the image sensor's v4l2 subdev
- * @pad: subdev media source pad
- * @supplies: image sensor's voltage regulator supplies
- * @gpio_reset: GPIO connected to the sensor's reset pin
* @drvdata: a pointer to the sensor's parameters data structure
* @i2c_bus: ISP I2C bus index (0...1)
* @test_pattern: true to enable video test pattern
- * @lock: mutex protecting the structure's members below
- * @format: media bus format at the sensor's source pad
*/
struct fimc_is_sensor {
- struct device *dev;
- struct v4l2_subdev subdev;
- struct media_pad pad;
- struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES];
- int gpio_reset;
const struct sensor_drv_data *drvdata;
unsigned int i2c_bus;
- bool test_pattern;
-
- struct mutex lock;
- struct v4l2_mbus_framefmt format;
+ u8 test_pattern;
};
-static inline
-struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct fimc_is_sensor, subdev);
-}
-
-int fimc_is_register_sensor_driver(void);
-void fimc_is_unregister_sensor_driver(void);
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node);
#endif /* FIMC_IS_SENSOR_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 9bdfa4599bc3..128b73b6cce2 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -161,78 +161,69 @@ static void fimc_is_disable_clocks(struct fimc_is *is)
}
}
-static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor,
- struct device_node *np)
+static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
+ struct device_node *node)
{
+ struct fimc_is_sensor *sensor = &is->sensor[index];
u32 tmp = 0;
int ret;
- np = of_graph_get_next_endpoint(np, NULL);
- if (!np)
+ sensor->drvdata = fimc_is_sensor_get_drvdata(node);
+ if (!sensor->drvdata) {
+ dev_err(&is->pdev->dev, "no driver data found for: %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ node = of_graph_get_next_endpoint(node, NULL);
+ if (!node)
return -ENXIO;
- np = of_graph_get_remote_port(np);
- if (!np)
+
+ node = of_graph_get_remote_port(node);
+ if (!node)
return -ENXIO;
/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
- ret = of_property_read_u32(np, "reg", &tmp);
- sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ ret = of_property_read_u32(node, "reg", &tmp);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+ node->full_name);
+ return ret;
+ }
- return ret;
+ sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ return 0;
}
static int fimc_is_register_subdevs(struct fimc_is *is)
{
- struct device_node *adapter, *child;
- int ret;
+ struct device_node *i2c_bus, *child;
+ int ret, index = 0;
ret = fimc_isp_subdev_create(&is->isp);
if (ret < 0)
return ret;
- for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) {
- if (!of_find_device_by_node(adapter)) {
- of_node_put(adapter);
- return -EPROBE_DEFER;
- }
-
- for_each_available_child_of_node(adapter, child) {
- struct i2c_client *client;
- struct v4l2_subdev *sd;
-
- client = of_find_i2c_device_by_node(child);
- if (!client)
- goto e_retry;
-
- sd = i2c_get_clientdata(client);
- if (!sd)
- goto e_retry;
+ /* Initialize memory allocator context for the ISP DMA. */
+ is->isp.alloc_ctx = is->alloc_ctx;
- /* FIXME: Add support for multiple sensors. */
- if (WARN_ON(is->sensor))
- continue;
+ for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_available_child_of_node(i2c_bus, child) {
+ ret = fimc_is_parse_sensor_config(is, index, child);
- is->sensor = sd_to_fimc_is_sensor(sd);
-
- if (fimc_is_parse_sensor_config(is->sensor, child)) {
- dev_warn(&is->pdev->dev, "DT parse error: %s\n",
- child->full_name);
+ if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
+ of_node_put(child);
+ return ret;
}
- pr_debug("%s(): registered subdev: %p\n",
- __func__, sd->name);
+ index++;
}
}
return 0;
-
-e_retry:
- of_node_put(child);
- return -EPROBE_DEFER;
}
static int fimc_is_unregister_subdevs(struct fimc_is *is)
{
fimc_isp_subdev_destroy(&is->isp);
- is->sensor = NULL;
return 0;
}
@@ -647,7 +638,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is,
fimc_is_hw_set_intgr0_gd0(is);
return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
- FIMC_IS_SENSOR_OPEN_TIMEOUT);
+ sensor->drvdata->open_timeout);
}
@@ -661,8 +652,8 @@ int fimc_is_hw_initialize(struct fimc_is *is)
u32 prev_id;
int i, ret;
- /* Sensor initialization. */
- ret = fimc_is_hw_open_sensor(is, is->sensor);
+ /* Sensor initialization. Only one sensor is currently supported. */
+ ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
if (ret < 0)
return ret;
@@ -977,27 +968,20 @@ static int fimc_is_module_init(void)
{
int ret;
- ret = fimc_is_register_sensor_driver();
- if (ret < 0)
- return ret;
-
ret = fimc_is_register_i2c_driver();
if (ret < 0)
- goto err_sens;
+ return ret;
ret = platform_driver_register(&fimc_is_driver);
- if (!ret)
- return ret;
- fimc_is_unregister_i2c_driver();
-err_sens:
- fimc_is_unregister_sensor_driver();
+ if (ret < 0)
+ fimc_is_unregister_i2c_driver();
+
return ret;
}
static void fimc_is_module_exit(void)
{
- fimc_is_unregister_sensor_driver();
fimc_is_unregister_i2c_driver();
platform_driver_unregister(&fimc_is_driver);
}
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
index 61bb0127e19d..e0be691af2d3 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -39,7 +39,7 @@
#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */
#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */
-#define FIMC_IS_SENSOR_NUM 2
+#define FIMC_IS_SENSORS_NUM 2
/* Memory definitions */
#define FIMC_IS_CPU_MEM_SIZE (0xa00000)
@@ -253,7 +253,7 @@ struct fimc_is {
struct firmware *f_w;
struct fimc_isp isp;
- struct fimc_is_sensor *sensor;
+ struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM];
struct fimc_is_setfile setfile;
struct vb2_alloc_ctx *alloc_ctx;
@@ -292,6 +292,11 @@ static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp)
return container_of(isp, struct fimc_is, isp);
}
+static inline struct chain_config *__get_curr_is_config(struct fimc_is *is)
+{
+ return &is->config[is->config_index];
+}
+
static inline void fimc_is_mem_barrier(void)
{
mb();
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
new file mode 100644
index 000000000000..e92b4e115adb
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -0,0 +1,660 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * FIMC-IS ISP video input and video output DMA interface driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * The hardware handling code derived from a driver written by
+ * Younghwan Joo <yhwan.joo@samsung.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/s5p_fimc.h>
+
+#include "common.h"
+#include "media-dev.h"
+#include "fimc-is.h"
+#include "fimc-isp-video.h"
+#include "fimc-is-param.h"
+
+static int isp_video_capture_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt;
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ const struct fimc_fmt *fmt;
+ unsigned int wh, i;
+
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ fmt = isp->video_capture.format;
+ wh = vid_fmt->width * vid_fmt->height;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN,
+ FIMC_ISP_REQ_BUFS_MAX);
+ *num_planes = fmt->memplanes;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = size;
+ allocators[i] = isp->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is)
+{
+ return &__get_curr_is_config(is)->isp.dma2_output;
+}
+
+static int isp_video_capture_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(q);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct param_dma_output *dma = __get_isp_dma2(is);
+ struct fimc_is_video *video = &isp->video_capture;
+ int ret;
+
+ if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) ||
+ test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+ return 0;
+
+
+ dma->cmd = DMA_OUTPUT_COMMAND_ENABLE;
+ dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE;
+ dma->buffer_address = is->is_dma_p_region +
+ DMA2_OUTPUT_ADDR_ARRAY_OFFS;
+ dma->buffer_number = video->reqbufs_count;
+ dma->dma_out_mask = video->buf_mask;
+
+ isp_dbg(2, &video->ve.vdev,
+ "buf_count: %d, planes: %d, dma addr table: %#x\n",
+ video->buf_count, video->format->memplanes,
+ dma->buffer_address);
+
+ fimc_is_mem_barrier();
+
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ return ret;
+
+ ret = fimc_pipeline_call(&video->ve, set_stream, 1);
+ if (ret < 0)
+ return ret;
+
+ set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+ return ret;
+}
+
+static int isp_video_capture_stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(q);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct param_dma_output *dma = __get_isp_dma2(is);
+ int ret;
+
+ ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0);
+ if (ret < 0)
+ return ret;
+
+ dma->cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE;
+ dma->buffer_number = 0;
+ dma->buffer_address = 0;
+ dma->dma_out_mask = 0;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__);
+
+ fimc_is_hw_set_isp_buf_mask(is, 0);
+
+ clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+ clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+
+ isp->video_capture.buf_count = 0;
+ return 0;
+}
+
+static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_is_video *video = &isp->video_capture;
+ int i;
+
+ if (video->format == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < video->format->memplanes; i++) {
+ unsigned long size = video->pixfmt.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&video->ve.vdev,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ /* Check if we get one of the already known buffers. */
+ if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+ dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ int i;
+
+ for (i = 0; i < video->buf_count; i++)
+ if (video->buffers[i]->dma_addr[0] == dma_addr)
+ return 0;
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_is_video *video = &isp->video_capture;
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct isp_video_buf *ivb = to_isp_video_buf(vb);
+ unsigned long flags;
+ unsigned int i;
+
+ if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+ spin_lock_irqsave(&is->slock, flags);
+ video->buf_mask |= BIT(ivb->index);
+ spin_unlock_irqrestore(&is->slock, flags);
+ } else {
+ unsigned int num_planes = video->format->memplanes;
+
+ ivb->index = video->buf_count;
+ video->buffers[ivb->index] = ivb;
+
+ for (i = 0; i < num_planes; i++) {
+ int buf_index = ivb->index * num_planes + i;
+
+ ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ is->is_p_region->shared[32 + buf_index] =
+ ivb->dma_addr[i];
+
+ isp_dbg(2, &video->ve.vdev,
+ "dma_buf %d (%d/%d/%d) addr: %#x\n",
+ buf_index, ivb->index, i, vb->v4l2_buf.index,
+ ivb->dma_addr[i]);
+ }
+
+ if (++video->buf_count < video->reqbufs_count)
+ return;
+
+ video->buf_mask = (1UL << video->buf_count) - 1;
+ set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+ }
+
+ if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+ isp_video_capture_start_streaming(vb->vb2_queue, 0);
+}
+
+/*
+ * FIMC-IS ISP input and output DMA interface interrupt handler.
+ * Locking: called with is->slock spinlock held.
+ */
+void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+ struct fimc_is_video *video = &is->isp.video_capture;
+ struct vb2_buffer *vb;
+ int buf_index;
+
+ /* TODO: Ensure the DMA is really stopped in stop_streaming callback */
+ if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state))
+ return;
+
+ buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count;
+ vb = &video->buffers[buf_index]->vb;
+
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+ video->buf_mask &= ~BIT(buf_index);
+ fimc_is_hw_set_isp_buf_mask(is, video->buf_mask);
+}
+
+static const struct vb2_ops isp_video_capture_qops = {
+ .queue_setup = isp_video_capture_queue_setup,
+ .buf_prepare = isp_video_capture_buffer_prepare,
+ .buf_queue = isp_video_capture_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = isp_video_capture_start_streaming,
+ .stop_streaming = isp_video_capture_stop_streaming,
+};
+
+static int isp_video_open(struct file *file)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct exynos_video_entity *ve = &isp->video_capture.ve;
+ struct media_entity *me = &ve->vdev.entity;
+ int ret;
+
+ if (mutex_lock_interruptible(&isp->video_lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ ret = pm_runtime_get_sync(&isp->pdev->dev);
+ if (ret < 0)
+ goto rel_fh;
+
+ if (v4l2_fh_is_singular_file(file)) {
+ mutex_lock(&me->parent->graph_mutex);
+
+ ret = fimc_pipeline_call(ve, open, me, true);
+
+ /* Mark the video pipeline as in use. */
+ if (ret == 0)
+ me->use_count++;
+
+ mutex_unlock(&me->parent->graph_mutex);
+ }
+ if (!ret)
+ goto unlock;
+rel_fh:
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&isp->video_lock);
+ return ret;
+}
+
+static int isp_video_release(struct file *file)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is_video *ivc = &isp->video_capture;
+ struct media_entity *entity = &ivc->ve.vdev.entity;
+ struct media_device *mdev = entity->parent;
+ int ret = 0;
+
+ mutex_lock(&isp->video_lock);
+
+ if (v4l2_fh_is_singular_file(file) && ivc->streaming) {
+ media_entity_pipeline_stop(entity);
+ ivc->streaming = 0;
+ }
+
+ vb2_fop_release(file);
+
+ if (v4l2_fh_is_singular_file(file)) {
+ fimc_pipeline_call(&ivc->ve, close);
+
+ mutex_lock(&mdev->graph_mutex);
+ entity->use_count--;
+ mutex_unlock(&mdev->graph_mutex);
+ }
+
+ pm_runtime_put(&isp->pdev->dev);
+ mutex_unlock(&isp->video_lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations isp_video_fops = {
+ .owner = THIS_MODULE,
+ .open = isp_video_open,
+ .release = isp_video_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int isp_video_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING);
+ return 0;
+}
+
+static int isp_video_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_fmt *fmt;
+
+ if (f->index >= FIMC_ISP_NUM_FORMATS)
+ return -EINVAL;
+
+ fmt = fimc_isp_find_format(NULL, NULL, f->index);
+ if (WARN_ON(fmt == NULL))
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int isp_video_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ f->fmt.pix_mp = isp->video_capture.pixfmt;
+ return 0;
+}
+
+static void __isp_video_try_fmt(struct fimc_isp *isp,
+ struct v4l2_pix_format_mplane *pixm,
+ const struct fimc_fmt **fmt)
+{
+ *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2);
+
+ pixm->colorspace = V4L2_COLORSPACE_SRGB;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->num_planes = (*fmt)->memplanes;
+ pixm->pixelformat = (*fmt)->fourcc;
+ /*
+ * TODO: double check with the docmentation these width/height
+ * constraints are correct.
+ */
+ v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN,
+ FIMC_ISP_SOURCE_WIDTH_MAX, 3,
+ &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN,
+ FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0);
+}
+
+static int isp_video_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ __isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL);
+ return 0;
+}
+
+static int isp_video_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ const struct fimc_fmt *ifmt = NULL;
+ struct param_dma_output *dma = __get_isp_dma2(is);
+
+ __isp_video_try_fmt(isp, pixm, &ifmt);
+
+ if (WARN_ON(ifmt == NULL))
+ return -EINVAL;
+
+ dma->format = DMA_OUTPUT_FORMAT_BAYER;
+ dma->order = DMA_OUTPUT_ORDER_GB_BG;
+ dma->plane = ifmt->memplanes;
+ dma->bitwidth = ifmt->depth[0];
+ dma->width = pixm->width;
+ dma->height = pixm->height;
+
+ fimc_is_mem_barrier();
+
+ isp->video_capture.format = ifmt;
+ isp->video_capture.pixfmt = *pixm;
+
+ return 0;
+}
+
+/*
+ * Check for source/sink format differences at each link.
+ * Return 0 if the formats match or -EPIPE otherwise.
+ */
+static int isp_video_pipeline_validate(struct fimc_isp *isp)
+{
+ struct v4l2_subdev *sd = &isp->subdev;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct media_pad *pad;
+ int ret;
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static int isp_video_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct exynos_video_entity *ve = &isp->video_capture.ve;
+ struct media_entity *me = &ve->vdev.entity;
+ int ret;
+
+ ret = media_entity_pipeline_start(me, &ve->pipe->mp);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_pipeline_validate(isp);
+ if (ret < 0)
+ goto p_stop;
+
+ ret = vb2_ioctl_streamon(file, priv, type);
+ if (ret < 0)
+ goto p_stop;
+
+ isp->video_capture.streaming = 1;
+ return 0;
+p_stop:
+ media_entity_pipeline_stop(me);
+ return ret;
+}
+
+static int isp_video_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is_video *video = &isp->video_capture;
+ int ret;
+
+ ret = vb2_ioctl_streamoff(file, priv, type);
+ if (ret < 0)
+ return ret;
+
+ media_entity_pipeline_stop(&video->ve.vdev.entity);
+ video->streaming = 0;
+ return 0;
+}
+
+static int isp_video_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ int ret;
+
+ ret = vb2_ioctl_reqbufs(file, priv, rb);
+ if (ret < 0)
+ return ret;
+
+ if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) {
+ rb->count = 0;
+ vb2_ioctl_reqbufs(file, priv, rb);
+ ret = -ENOMEM;
+ }
+
+ isp->video_capture.reqbufs_count = rb->count;
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
+ .vidioc_querycap = isp_video_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane,
+ .vidioc_reqbufs = isp_video_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = isp_video_streamon,
+ .vidioc_streamoff = isp_video_streamoff,
+};
+
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *q = &isp->video_capture.vb_queue;
+ struct fimc_is_video *iv;
+ struct video_device *vdev;
+ int ret;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ iv = &isp->video_capture;
+ else
+ return -ENOSYS;
+
+ mutex_init(&isp->video_lock);
+ INIT_LIST_HEAD(&iv->pending_buf_q);
+ INIT_LIST_HEAD(&iv->active_buf_q);
+ iv->format = fimc_isp_find_format(NULL, NULL, 0);
+ iv->pixfmt.width = IS_DEFAULT_WIDTH;
+ iv->pixfmt.height = IS_DEFAULT_HEIGHT;
+ iv->pixfmt.pixelformat = iv->format->fourcc;
+ iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB;
+ iv->reqbufs_count = 0;
+
+ memset(q, 0, sizeof(*q));
+ q->type = type;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = &isp_video_capture_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct isp_video_buf);
+ q->drv_priv = isp;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &isp->video_lock;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0)
+ return ret;
+
+ vdev = &iv->ve.vdev;
+ memset(vdev, 0, sizeof(*vdev));
+ snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s",
+ type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
+ "capture" : "output");
+ vdev->queue = q;
+ vdev->fops = &isp_video_fops;
+ vdev->ioctl_ops = &isp_video_ioctl_ops;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->minor = -1;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &isp->video_lock;
+
+ iv->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(vdev, isp);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ media_entity_cleanup(&vdev->entity);
+ return ret;
+ }
+
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vdev->name, video_device_node_name(vdev));
+
+ return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type)
+{
+ struct exynos_video_entity *ve;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ve = &isp->video_capture.ve;
+ else
+ return;
+
+ mutex_lock(&isp->video_lock);
+
+ if (video_is_registered(&ve->vdev)) {
+ video_unregister_device(&ve->vdev);
+ media_entity_cleanup(&ve->vdev.entity);
+ ve->pipe = NULL;
+ }
+
+ mutex_unlock(&isp->video_lock);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/exynos4-is/fimc-isp-video.h
new file mode 100644
index 000000000000..98c662654bb6
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.h
@@ -0,0 +1,44 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_ISP_VIDEO__
+#define FIMC_ISP_VIDEO__
+
+#include <media/videobuf2-core.h>
+#include "fimc-isp.h"
+
+#ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type);
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type);
+
+void fimc_isp_video_irq_handler(struct fimc_is *is);
+#else
+static inline void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+}
+
+static inline int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type)
+{
+ return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type)
+{
+}
+#endif /* !CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE */
+
+#endif /* FIMC_ISP_VIDEO__ */
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index f3c6136aa5b4..be62d6b9ac48 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -25,6 +25,7 @@
#include <media/v4l2-device.h>
#include "media-dev.h"
+#include "fimc-isp-video.h"
#include "fimc-is-command.h"
#include "fimc-is-param.h"
#include "fimc-is-regs.h"
@@ -93,8 +94,8 @@ void fimc_isp_irq_handler(struct fimc_is *is)
is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21));
fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP);
+ fimc_isp_video_irq_handler(is);
- /* TODO: Complete ISP DMA interrupt handler */
wake_up(&is->irq_queue);
}
@@ -388,7 +389,33 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
return 0;
}
+static int fimc_isp_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ int ret;
+
+ /* Use pipeline object allocated by the media device. */
+ isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd);
+
+ ret = fimc_isp_video_device_register(isp, sd->v4l2_dev,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret < 0)
+ isp->video_capture.ve.pipe = NULL;
+
+ return ret;
+}
+
+static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+
+ fimc_isp_video_device_unregister(isp,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = {
+ .registered = fimc_isp_subdev_registered,
+ .unregistered = fimc_isp_subdev_unregistered,
.open = fimc_isp_subdev_open,
};
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h
index 03bf95ab017b..4dc55a18d978 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.h
+++ b/drivers/media/platform/exynos4-is/fimc-isp.h
@@ -35,17 +35,18 @@ extern int fimc_isp_debug;
#define FIMC_ISP_SINK_WIDTH_MIN (16 + 8)
#define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8)
#define FIMC_ISP_SOURCE_WIDTH_MIN 8
-#define FIMC_ISP_SOURC_HEIGHT_MIN 8
+#define FIMC_ISP_SOURCE_HEIGHT_MIN 8
#define FIMC_ISP_CAC_MARGIN_WIDTH 16
#define FIMC_ISP_CAC_MARGIN_HEIGHT 12
#define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16)
#define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12)
#define FIMC_ISP_SOURCE_WIDTH_MAX 4000
-#define FIMC_ISP_SOURC_HEIGHT_MAX 4000
+#define FIMC_ISP_SOURCE_HEIGHT_MAX 4000
#define FIMC_ISP_NUM_FORMATS 3
#define FIMC_ISP_REQ_BUFS_MIN 2
+#define FIMC_ISP_REQ_BUFS_MAX 32
#define FIMC_ISP_SD_PAD_SINK 0
#define FIMC_ISP_SD_PAD_SRC_FIFO 1
@@ -100,6 +101,16 @@ struct fimc_isp_ctrls {
struct v4l2_ctrl *colorfx;
};
+struct isp_video_buf {
+ struct vb2_buffer vb;
+ dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES];
+ unsigned int index;
+};
+
+#define to_isp_video_buf(_b) container_of(_b, struct isp_video_buf, vb)
+
+#define FIMC_ISP_MAX_BUFS 4
+
/**
* struct fimc_is_video - fimc-is video device structure
* @vdev: video_device structure
@@ -114,18 +125,26 @@ struct fimc_isp_ctrls {
* @format: current pixel format
*/
struct fimc_is_video {
- struct video_device vdev;
+ struct exynos_video_entity ve;
enum v4l2_buf_type type;
struct media_pad pad;
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vb_queue;
- unsigned int frame_count;
unsigned int reqbufs_count;
+ unsigned int buf_count;
+ unsigned int buf_mask;
+ unsigned int frame_count;
int streaming;
+ struct isp_video_buf *buffers[FIMC_ISP_MAX_BUFS];
const struct fimc_fmt *format;
+ struct v4l2_pix_format_mplane pixfmt;
};
+/* struct fimc_isp:state bit definitions */
+#define ST_ISP_VID_CAP_BUF_PREP 0
+#define ST_ISP_VID_CAP_STREAMING 1
+
/**
* struct fimc_isp - FIMC-IS ISP data structure
* @pdev: pointer to FIMC-IS platform device
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 04d6ecdd314c..e62211a80f0e 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -11,6 +11,8 @@
*/
#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
@@ -25,6 +27,7 @@
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include <media/media-device.h>
@@ -219,6 +222,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
if (ret < 0)
return ret;
}
+
ret = fimc_md_set_camclk(sd, true);
if (ret < 0)
goto err_wbclk;
@@ -379,77 +383,18 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adapter;
- if (!client)
+ if (!client || client->dev.of_node)
return;
v4l2_device_unregister_subdev(sd);
- if (!client->dev.of_node) {
- adapter = client->adapter;
- i2c_unregister_device(client);
- if (adapter)
- i2c_put_adapter(adapter);
- }
+ adapter = client->adapter;
+ i2c_unregister_device(client);
+ if (adapter)
+ i2c_put_adapter(adapter);
}
#ifdef CONFIG_OF
-/* Register I2C client subdev associated with @node. */
-static int fimc_md_of_add_sensor(struct fimc_md *fmd,
- struct device_node *node, int index)
-{
- struct fimc_sensor_info *si;
- struct i2c_client *client;
- struct v4l2_subdev *sd;
- int ret;
-
- if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
- return -EINVAL;
- si = &fmd->sensor[index];
-
- client = of_find_i2c_device_by_node(node);
- if (!client)
- return -EPROBE_DEFER;
-
- device_lock(&client->dev);
-
- if (!client->dev.driver ||
- !try_module_get(client->dev.driver->owner)) {
- ret = -EPROBE_DEFER;
- v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
- node->full_name);
- goto dev_put;
- }
-
- /* Enable sensor's master clock */
- ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
- if (ret < 0)
- goto mod_put;
- sd = i2c_get_clientdata(client);
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- __fimc_md_set_camclk(fmd, &si->pdata, false);
- if (ret < 0)
- goto mod_put;
-
- v4l2_set_subdev_hostdata(sd, &si->pdata);
- if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
- sd->grp_id = GRP_ID_FIMC_IS_SENSOR;
- else
- sd->grp_id = GRP_ID_SENSOR;
-
- si->subdev = sd;
- v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
- sd->name, fmd->num_sensors);
- fmd->num_sensors++;
-
-mod_put:
- module_put(client->dev.driver->owner);
-dev_put:
- device_unlock(&client->dev);
- put_device(&client->dev);
- return ret;
-}
-
/* Parse port node and register as a sub-device any sensor specified there. */
static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *port,
@@ -458,7 +403,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *rem, *ep, *np;
struct fimc_source_info *pd;
struct v4l2_of_endpoint endpoint;
- int ret;
u32 val;
pd = &fmd->sensor[index].pdata;
@@ -486,6 +430,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
if (!of_property_read_u32(rem, "clock-frequency", &val))
pd->clk_frequency = val;
+ else
+ pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ;
if (pd->clk_frequency == 0) {
v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
@@ -525,10 +471,17 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
else
pd->fimc_bus_type = pd->sensor_bus_type;
- ret = fimc_md_of_add_sensor(fmd, rem, index);
- of_node_put(rem);
+ if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
+ return -EINVAL;
- return ret;
+ fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
+ fmd->sensor[index].asd.match.of.node = rem;
+ fmd->async_subdevs[index] = &fmd->sensor[index].asd;
+
+ fmd->num_sensors++;
+
+ of_node_put(rem);
+ return 0;
}
/* Register all SoC external sub-devices */
@@ -732,8 +685,16 @@ static int register_csis_entity(struct fimc_md *fmd,
static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)
{
struct v4l2_subdev *sd = &is->isp.subdev;
+ struct exynos_media_pipeline *ep;
int ret;
+ /* Allocate pipeline object for the ISP capture video node. */
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
+
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
if (ret) {
v4l2_err(&fmd->v4l2_dev,
@@ -884,11 +845,13 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
v4l2_device_unregister_subdev(fmd->csis[i].sd);
fmd->csis[i].sd = NULL;
}
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
- fimc_md_unregister_sensor(fmd->sensor[i].subdev);
- fmd->sensor[i].subdev = NULL;
+ if (fmd->pdev->dev.of_node == NULL) {
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
}
if (fmd->fimc_is)
@@ -1005,16 +968,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
/* Create FIMC-IS links */
static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
{
+ struct fimc_isp *isp = &fmd->fimc_is->isp;
struct media_entity *source, *sink;
int i, ret;
- source = &fmd->fimc_is->isp.subdev.entity;
+ source = &isp->subdev.entity;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (fmd->fimc[i] == NULL)
continue;
- /* Link from IS-ISP subdev to FIMC */
+ /* Link from FIMC-IS-ISP subdev to FIMC */
sink = &fmd->fimc[i]->vid_cap.subdev.entity;
ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
sink, FIMC_SD_PAD_SINK_FIFO, 0);
@@ -1022,7 +986,15 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
return ret;
}
- return ret;
+ /* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */
+ sink = &isp->video_capture.ve.vdev.entity;
+
+ /* Skip this link if the fimc-is-isp video node driver isn't built-in */
+ if (sink->num_pads == 0)
+ return 0;
+
+ return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA,
+ sink, 0, 0);
}
/**
@@ -1223,6 +1195,14 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
struct fimc_camclk_info *camclk;
int ret = 0;
+ /*
+ * When device tree is used the sensor drivers are supposed to
+ * control the clock themselves. This whole function will be
+ * removed once S5PV210 platform is converted to the device tree.
+ */
+ if (fmd->pdev->dev.of_node)
+ return 0;
+
if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
return -EINVAL;
@@ -1277,6 +1257,14 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd);
struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
+ /*
+ * If there is a clock provider registered the sensors will
+ * handle their clock themselves, no need to control it on
+ * the host interface side.
+ */
+ if (fmd->clk_provider.num_clocks > 0)
+ return 0;
+
return __fimc_md_set_camclk(fmd, si, on);
}
@@ -1438,6 +1426,153 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)
return 0;
}
+#ifdef CONFIG_OF
+static int cam_clk_prepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+ int ret;
+
+ if (camclk->fmd->pmf == NULL)
+ return -ENODEV;
+
+ ret = pm_runtime_get_sync(camclk->fmd->pmf);
+ return ret < 0 ? ret : 0;
+}
+
+static void cam_clk_unprepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+
+ if (camclk->fmd->pmf == NULL)
+ return;
+
+ pm_runtime_put_sync(camclk->fmd->pmf);
+}
+
+static const struct clk_ops cam_clk_ops = {
+ .prepare = cam_clk_prepare,
+ .unprepare = cam_clk_unprepare,
+};
+
+static void fimc_md_unregister_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ unsigned int i;
+
+ if (cp->of_node)
+ of_clk_del_provider(cp->of_node);
+
+ for (i = 0; i < cp->num_clocks; i++)
+ clk_unregister(cp->clks[i]);
+}
+
+static int fimc_md_register_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ struct device *dev = &fmd->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ struct cam_clk *camclk = &cp->camclk[i];
+ struct clk_init_data init;
+ const char *p_name;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "clock-output-names", i, &init.name);
+ if (ret < 0)
+ break;
+
+ p_name = __clk_get_name(fmd->camclk[i].clock);
+
+ /* It's safe since clk_register() will duplicate the string. */
+ init.parent_names = &p_name;
+ init.num_parents = 1;
+ init.ops = &cam_clk_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ camclk->hw.init = &init;
+ camclk->fmd = fmd;
+
+ cp->clks[i] = clk_register(NULL, &camclk->hw);
+ if (IS_ERR(cp->clks[i])) {
+ dev_err(dev, "failed to register clock: %s (%ld)\n",
+ init.name, PTR_ERR(cp->clks[i]));
+ ret = PTR_ERR(cp->clks[i]);
+ goto err;
+ }
+ cp->num_clocks++;
+ }
+
+ if (cp->num_clocks == 0) {
+ dev_warn(dev, "clk provider not registered\n");
+ return 0;
+ }
+
+ cp->clk_data.clks = cp->clks;
+ cp->clk_data.clk_num = cp->num_clocks;
+ cp->of_node = dev->of_node;
+ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+ &cp->clk_data);
+ if (ret == 0)
+ return 0;
+err:
+ fimc_md_unregister_clk_provider(fmd);
+ return ret;
+}
+#else
+#define fimc_md_register_clk_provider(fmd) (0)
+#define fimc_md_unregister_clk_provider(fmd) (0)
+#endif
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ struct fimc_sensor_info *si = NULL;
+ int i;
+
+ /* Find platform data for this sensor subdev */
+ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
+ if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+ si = &fmd->sensor[i];
+
+ if (si == NULL)
+ return -EINVAL;
+
+ v4l2_set_subdev_hostdata(subdev, &si->pdata);
+
+ if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
+ subdev->grp_id = GRP_ID_FIMC_IS_SENSOR;
+ else
+ subdev->grp_id = GRP_ID_SENSOR;
+
+ si->subdev = subdev;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
+ subdev->name, fmd->num_sensors);
+
+ fmd->num_sensors++;
+
+ return 0;
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ int ret;
+
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
+ ret = fimc_md_create_links(fmd);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+unlock:
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return ret;
+}
+
static int fimc_md_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1470,63 +1605,91 @@ static int fimc_md_probe(struct platform_device *pdev)
v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
return ret;
}
+
ret = media_device_register(&fmd->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
- goto err_md;
+ goto err_v4l2_dev;
}
+
ret = fimc_md_get_clocks(fmd);
if (ret)
- goto err_clk;
+ goto err_md;
fmd->user_subdev_api = (dev->of_node != NULL);
- /* Protect the media graph while we're registering entities */
- mutex_lock(&fmd->media_dev.graph_mutex);
-
ret = fimc_md_get_pinctrl(fmd);
if (ret < 0) {
if (ret != EPROBE_DEFER)
dev_err(dev, "Failed to get pinctrl: %d\n", ret);
- goto err_unlock;
+ goto err_clk;
}
+ platform_set_drvdata(pdev, fmd);
+
+ /* Protect the media graph while we're registering entities */
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
if (dev->of_node)
ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
else
ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
fimc_md_pdev_match);
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_clk;
+ }
if (dev->platform_data || dev->of_node) {
ret = fimc_md_register_sensor_entities(fmd);
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_m_ent;
+ }
}
- ret = fimc_md_create_links(fmd);
- if (ret)
- goto err_unlock;
- ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
- if (ret)
- goto err_unlock;
+ mutex_unlock(&fmd->media_dev.graph_mutex);
ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
if (ret)
- goto err_unlock;
+ goto err_m_ent;
+ /*
+ * FIMC platform devices need to be registered before the sclk_cam
+ * clocks provider, as one of these devices needs to be activated
+ * to enable the clock.
+ */
+ ret = fimc_md_register_clk_provider(fmd);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "clock provider registration failed\n");
+ goto err_attr;
+ }
+
+ if (fmd->num_sensors > 0) {
+ fmd->subdev_notifier.subdevs = fmd->async_subdevs;
+ fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+ fmd->subdev_notifier.bound = subdev_notifier_bound;
+ fmd->subdev_notifier.complete = subdev_notifier_complete;
+ fmd->num_sensors = 0;
+
+ ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
+ &fmd->subdev_notifier);
+ if (ret)
+ goto err_clk_p;
+ }
- platform_set_drvdata(pdev, fmd);
- mutex_unlock(&fmd->media_dev.graph_mutex);
return 0;
-err_unlock:
- mutex_unlock(&fmd->media_dev.graph_mutex);
+err_clk_p:
+ fimc_md_unregister_clk_provider(fmd);
+err_attr:
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
err_clk:
fimc_md_put_clocks(fmd);
+err_m_ent:
fimc_md_unregister_entities(fmd);
- media_device_unregister(&fmd->media_dev);
err_md:
+ media_device_unregister(&fmd->media_dev);
+err_v4l2_dev:
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
}
@@ -1538,12 +1701,16 @@ static int fimc_md_remove(struct platform_device *pdev)
if (!fmd)
return 0;
+ fimc_md_unregister_clk_provider(fmd);
+ v4l2_async_notifier_unregister(&fmd->subdev_notifier);
+
v4l2_device_unregister(&fmd->v4l2_dev);
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
fimc_md_pipelines_free(fmd);
media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
+
return 0;
}
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 62599fd7756f..ee1e2519f728 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -10,6 +10,7 @@
#define FIMC_MDEVICE_H_
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -31,8 +32,9 @@
#define PINCTRL_STATE_IDLE "idle"
-#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_SENSORS 4
#define FIMC_MAX_CAMCLKS 2
+#define DEFAULT_SENSOR_CLK_FREQ 24000000U
/* LCD/ISP Writeback clocks (PIXELASYNCMx) */
enum {
@@ -78,6 +80,7 @@ struct fimc_camclk_info {
/**
* struct fimc_sensor_info - image data source subdev information
* @pdata: sensor's atrributes passed as media device's platform data
+ * @asd: asynchronous subdev registration data structure
* @subdev: image sensor v4l2 subdev
* @host: fimc device the sensor is currently linked to
*
@@ -85,10 +88,17 @@ struct fimc_camclk_info {
*/
struct fimc_sensor_info {
struct fimc_source_info pdata;
+ struct v4l2_async_subdev asd;
struct v4l2_subdev *subdev;
struct fimc_dev *host;
};
+struct cam_clk {
+ struct clk_hw hw;
+ struct fimc_md *fmd;
+};
+#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw)
+
/**
* struct fimc_md - fimc media device information
* @csis: MIPI CSIS subdevs data
@@ -105,6 +115,7 @@ struct fimc_sensor_info {
* @pinctrl: camera port pinctrl handle
* @state_default: pinctrl default state handle
* @state_idle: pinctrl idle state handle
+ * @cam_clk_provider: CAMCLK clock provider structure
* @user_subdev_api: true if subdevs are not configured by the host driver
* @slock: spinlock protecting @sensor array
*/
@@ -122,13 +133,25 @@ struct fimc_md {
struct media_device media_dev;
struct v4l2_device v4l2_dev;
struct platform_device *pdev;
+
struct fimc_pinctrl {
struct pinctrl *pinctrl;
struct pinctrl_state *state_default;
struct pinctrl_state *state_idle;
} pinctl;
- bool user_subdev_api;
+ struct cam_clk_provider {
+ struct clk *clks[FIMC_MAX_CAMCLKS];
+ struct clk_onecell_data clk_data;
+ struct device_node *of_node;
+ struct cam_clk camclk[FIMC_MAX_CAMCLKS];
+ int num_clocks;
+ } clk_provider;
+
+ struct v4l2_async_notifier subdev_notifier;
+ struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+
+ bool user_subdev_api;
spinlock_t slock;
struct list_head pipelines;
};
@@ -145,6 +168,11 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
container_of(me->parent, struct fimc_md, media_dev);
}
+static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct fimc_md, subdev_notifier);
+}
+
static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
{
mutex_lock(&ve->vdev.entity.parent->graph_mutex);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
index 33f2c7374cfd..57fb05bb8c77 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
@@ -210,19 +210,19 @@
/* JPEG CNTL Register bit */
#define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0)
-#define EXYNOS4_DEC_MODE (1 << 0)
-#define EXYNOS4_ENC_MODE (1 << 1)
+#define EXYNOS4_DEC_MODE (1 << 0)
+#define EXYNOS4_ENC_MODE (1 << 1)
#define EXYNOS4_AUTO_RST_MARKER (1 << 2)
#define EXYNOS4_RST_INTERVAL_SHIFT 3
#define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \
<< EXYNOS4_RST_INTERVAL_SHIFT)
#define EXYNOS4_HUF_TBL_EN (1 << 19)
#define EXYNOS4_HOR_SCALING_SHIFT 20
-#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT)
+#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT)
#define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \
<< EXYNOS4_HOR_SCALING_SHIFT)
#define EXYNOS4_VER_SCALING_SHIFT 22
-#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT)
+#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT)
#define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \
<< EXYNOS4_VER_SCALING_SHIFT)
#define EXYNOS4_PADDING (1 << 27)
@@ -238,8 +238,8 @@
#define EXYNOS4_FRAME_ERR_EN (1 << 4)
#define EXYNOS4_INT_EN_ALL (0x1f << 0)
-#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3)
-#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3)
+#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3)
+#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3)
#define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0)
#define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0)
@@ -270,7 +270,7 @@
#define EXYNOS4_DEC_YUV_420_IMG (4 << 0)
#define EXYNOS4_GRAY_IMG_IP_SHIFT 3
-#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
+#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
#define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT)
#define EXYNOS4_RGB_IP_SHIFT 6
@@ -278,18 +278,18 @@
#define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT)
#define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT)
-#define EXYNOS4_YUV_444_IP_SHIFT 9
+#define EXYNOS4_YUV_444_IP_SHIFT 9
#define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT)
#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT)
#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT)
-#define EXYNOS4_YUV_422_IP_SHIFT 12
+#define EXYNOS4_YUV_422_IP_SHIFT 12
#define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT)
#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT)
#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT)
#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT)
-#define EXYNOS4_YUV_420_IP_SHIFT 15
+#define EXYNOS4_YUV_420_IP_SHIFT 15
#define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT)
#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT)
#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT)
@@ -303,8 +303,8 @@
#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03
-#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26)
-#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26)
+#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26)
+#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26)
/* JPEG HUFF count Register bit */
#define EXYNOS4_HUFF_COUNT_MASK 0xffff
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index 7dca1e640970..841717a2842c 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -571,7 +571,7 @@ static int pm800_probe(struct i2c_client *client,
ret = pm800_pages_init(chip);
if (ret) {
dev_err(&client->dev, "pm800_pages_init failed!\n");
- goto err_page_init;
+ goto err_device_init;
}
ret = device_800_init(chip, pdata);
@@ -587,7 +587,6 @@ static int pm800_probe(struct i2c_client *client,
err_device_init:
pm800_pages_exit(chip);
-err_page_init:
err_subchip_alloc:
pm80x_deinit();
out_init:
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index c9b1f6422941..bcfc9e85b4a0 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -1179,12 +1179,18 @@ static int pm860x_probe(struct i2c_client *client,
chip->companion_addr = pdata->companion_addr;
chip->companion = i2c_new_dummy(chip->client->adapter,
chip->companion_addr);
+ if (!chip->companion) {
+ dev_err(&client->dev,
+ "Failed to allocate I2C companion device\n");
+ return -ENODEV;
+ }
chip->regmap_companion = regmap_init_i2c(chip->companion,
&pm860x_regmap_config);
if (IS_ERR(chip->regmap_companion)) {
ret = PTR_ERR(chip->regmap_companion);
dev_err(&chip->companion->dev,
"Failed to allocate register map: %d\n", ret);
+ i2c_unregister_device(chip->companion);
return ret;
}
i2c_set_clientdata(chip->companion, chip);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 49bb445d846a..33834120d057 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,14 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_BCM590XX
+ tristate "Broadcom BCM590xx PMUs"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Support for the BCM590xx PMUs from Broadcom
+
config MFD_CROS_EC
tristate "ChromeOS Embedded Controller"
select MFD_CORE
@@ -100,7 +108,7 @@ config PMIC_DA903X
bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
depends on I2C=y
help
- Say yes here to support for Dialog Semiconductor DA9030 (a.k.a
+ Say yes here to add support for Dialog Semiconductor DA9030 (a.k.a
ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC
usually found on PXA processors-based platforms. This includes
the I2C driver and the core APIs _only_, you have to select
@@ -270,13 +278,18 @@ config MFD_KEMPLD
device may provide functions like watchdog, GPIO, UART and I2C bus.
The following modules are supported:
+ * COMe-bHL6
* COMe-bIP#
* COMe-bPC2 (ETXexpress-PC)
* COMe-bSC# (ETXexpress-SC T#)
+ * COMe-cBT6
* COMe-cCT6
* COMe-cDC2 (microETXexpress-DC)
+ * COMe-cHL6
* COMe-cPC2 (microETXexpress-PC)
+ * COMe-mBT10
* COMe-mCT10
+ * COMe-mTT10 (nanoETXexpress-TT)
* ETX-OH
This driver can also be built as a module. If so, the module
@@ -322,9 +335,10 @@ config MFD_MAX14577
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
+ select REGMAP_IRQ
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX14577.
+ Say yes here to add support for Maxim Semiconductor MAX14577.
This is a Micro-USB IC with Charger controls on chip.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
@@ -337,7 +351,7 @@ config MFD_MAX77686
select REGMAP_I2C
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX77686.
+ Say yes here to add support for Maxim Semiconductor MAX77686.
This is a Power Management IC with RTC on chip.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
@@ -349,7 +363,7 @@ config MFD_MAX77693
select MFD_CORE
select REGMAP_I2C
help
- Say yes here to support for Maxim Semiconductor MAX77693.
+ Say yes here to add support for Maxim Semiconductor MAX77693.
This is a companion Power Management IC with Flash, Haptic, Charger,
and MUIC(Micro USB Interface Controller) controls on chip.
This driver provides common support for accessing the device;
@@ -363,7 +377,7 @@ config MFD_MAX8907
select REGMAP_I2C
select REGMAP_IRQ
help
- Say yes here to support for Maxim Semiconductor MAX8907. This is
+ Say yes here to add support for Maxim Semiconductor MAX8907. This is
a Power Management IC. This driver provides common support for
accessing the device; additional drivers must be enabled in order
to use the functionality of the device.
@@ -373,7 +387,7 @@ config MFD_MAX8925
depends on I2C=y
select MFD_CORE
help
- Say yes here to support for Maxim Semiconductor MAX8925. This is
+ Say yes here to add support for Maxim Semiconductor MAX8925. This is
a Power Management IC. This driver provides common support for
accessing the device, additional drivers must be enabled in order
to use the functionality of the device.
@@ -384,7 +398,7 @@ config MFD_MAX8997
select MFD_CORE
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX8997/8966.
+ Say yes here to add support for Maxim Semiconductor MAX8997/8966.
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
MUIC controls on chip.
This driver provides common support for accessing the device;
@@ -397,7 +411,7 @@ config MFD_MAX8998
select MFD_CORE
select IRQ_DOMAIN
help
- Say yes here to support for Maxim Semiconductor MAX8998 and
+ Say yes here to add support for Maxim Semiconductor MAX8998 and
National Semiconductor LP3974. This is a Power Management IC.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
@@ -473,10 +487,11 @@ config MFD_PM8XXX
config MFD_PM8921_CORE
tristate "Qualcomm PM8921 PMIC chip"
- depends on (ARCH_MSM || HEXAGON)
- depends on BROKEN
+ depends on (ARM || HEXAGON)
+ select IRQ_DOMAIN
select MFD_CORE
select MFD_PM8XXX
+ select REGMAP
help
If you say yes to this option, support will be included for the
built-in PM8921 PMIC chip.
@@ -487,16 +502,6 @@ config MFD_PM8921_CORE
Say M here if you want to include support for PM8921 chip as a module.
This will build a module called "pm8921-core".
-config MFD_PM8XXX_IRQ
- bool "Qualcomm PM8xxx IRQ features"
- depends on MFD_PM8XXX
- default y if MFD_PM8XXX
- help
- This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
-
- This is required to use certain other PM 8xxx features, such as GPIO
- and MPP.
-
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
@@ -516,6 +521,16 @@ config MFD_RTSX_PCI
types of memory cards, such as Memory Stick, Memory Stick Pro,
Secure Digital and MultiMediaCard.
+config MFD_RTSX_USB
+ tristate "Realtek USB card reader"
+ depends on USB
+ select MFD_CORE
+ help
+ Select this option to get support for Realtek USB 2.0 card readers
+ including RTS5129, RTS5139, RTS5179 and RTS5170.
+ Realtek card reader supports access to many types of memory cards,
+ such as Memory Stick Pro, Secure Digital and MultiMediaCard.
+
config MFD_RC5T583
bool "Ricoh RC5T583 Power Management system device"
depends on I2C=y
@@ -774,17 +789,6 @@ config MFD_PALMAS
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
-config MFD_TI_SSP
- tristate "TI Sequencer Serial Port support"
- depends on ARCH_DAVINCI_TNETV107X
- select MFD_CORE
- ---help---
- Say Y here if you want support for the Sequencer Serial Port
- in a Texas Instruments TNETV107X SoC.
-
- To compile this driver as a module, choose M here: the
- module will be called ti-ssp.
-
config TPS6105X
tristate "TI TPS61050/61052 Boost Converters"
depends on I2C
@@ -853,6 +857,22 @@ config MFD_TPS65217
This driver can also be built as a module. If so, the module
will be called tps65217.
+config MFD_TPS65218
+ tristate "TI TPS65218 Power Management chips"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ If you say yes here you get support for the TPS65218 series of
+ Power Management chips.
+ These include voltage regulators, gpio and other features
+ that are often used in portable devices. Only regulator
+ component is currently supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps65218.
+
config MFD_TPS6586X
bool "TI TPS6586x Power Management chips"
depends on I2C=y
@@ -935,16 +955,6 @@ config TWL4030_CORE
high speed USB OTG transceiver, an audio codec (on most
versions) and many other features.
-config TWL4030_MADC
- tristate "TI TWL4030 MADC"
- depends on TWL4030_CORE
- help
- This driver provides support for triton TWL4030-MADC. The
- driver supports both RT and SW conversion methods.
-
- This driver can be built as a module. If so it will be
- named twl4030-madc
-
config TWL4030_POWER
bool "TI TWL4030 power resources"
depends on TWL4030_CORE && ARM
@@ -1193,9 +1203,6 @@ config MFD_STW481X
in various ST Microelectronics and ST-Ericsson embedded
Nomadik series.
-endmenu
-endif
-
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
@@ -1226,3 +1233,6 @@ config VEXPRESS_CONFIG
help
Platform configuration infrastructure for the ARM Ltd.
Versatile Express.
+
+endmenu
+endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5aea5ef0a62f..2851275e2656 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,12 +8,14 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
+obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
+obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
@@ -21,7 +23,6 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
-obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
@@ -62,6 +63,7 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MFD_TPS65217) += tps65217.o
+obj-$(CONFIG_MFD_TPS65218) += tps65218.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o
tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
@@ -71,7 +73,6 @@ obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
-obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6040_CORE) += twl6040.o
@@ -150,7 +151,6 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
-obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index aaff683cd37d..a8ee4a36a1d8 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -592,7 +592,7 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
/* If ->irq_base is zero this will give a linear mapping */
ab8500->domain = irq_domain_add_simple(NULL,
- num_irqs, ab8500->irq_base,
+ num_irqs, 0,
&ab8500_irq_ops, ab8500);
if (!ab8500->domain) {
@@ -1583,14 +1583,13 @@ static int ab8500_probe(struct platform_device *pdev)
if (!ab8500)
return -ENOMEM;
- if (plat)
- ab8500->irq_base = plat->irq_base;
-
ab8500->dev = &pdev->dev;
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!resource)
+ if (!resource) {
+ dev_err(&pdev->dev, "no IRQ resource\n");
return -ENODEV;
+ }
ab8500->irq = resource->start;
@@ -1612,8 +1611,10 @@ static int ab8500_probe(struct platform_device *pdev)
else {
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_IC_NAME_REG, &value);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not probe HW\n");
return ret;
+ }
ab8500->version = value;
}
@@ -1759,30 +1760,30 @@ static int ab8500_probe(struct platform_device *pdev)
if (is_ab9540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
ARRAY_SIZE(ab9540_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
else if (is_ab8540(ab8500)) {
ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,
ARRAY_SIZE(ab8540_devs), NULL,
- ab8500->irq_base, NULL);
+ 0, ab8500->domain);
if (ret)
return ret;
if (is_ab8540_1p2_or_earlier(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut1_devs,
ARRAY_SIZE(ab8540_cut1_devs), NULL,
- ab8500->irq_base, NULL);
+ 0, ab8500->domain);
else /* ab8540 >= cut2 */
ret = mfd_add_devices(ab8500->dev, 0, ab8540_cut2_devs,
ARRAY_SIZE(ab8540_cut2_devs), NULL,
- ab8500->irq_base, NULL);
+ 0, ab8500->domain);
} else if (is_ab8505(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,
ARRAY_SIZE(ab8505_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
else
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
if (ret)
return ret;
@@ -1790,7 +1791,7 @@ static int ab8500_probe(struct platform_device *pdev)
/* Add battery management devices */
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
ARRAY_SIZE(ab8500_bm_devs), NULL,
- ab8500->irq_base, ab8500->domain);
+ 0, ab8500->domain);
if (ret)
dev_err(ab8500->dev, "error adding bm devices\n");
}
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
index 62501553d63c..f495b8b57dd7 100644
--- a/drivers/mfd/adp5520.c
+++ b/drivers/mfd/adp5520.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c
index c71ff0af1547..39fa554f13bb 100644
--- a/drivers/mfd/as3722.c
+++ b/drivers/mfd/as3722.c
@@ -277,6 +277,7 @@ static const struct regmap_range as3722_readable_ranges[] = {
regmap_reg_range(AS3722_ADC0_CONTROL_REG, AS3722_ADC_CONFIGURATION_REG),
regmap_reg_range(AS3722_ASIC_ID1_REG, AS3722_ASIC_ID2_REG),
regmap_reg_range(AS3722_LOCK_REG, AS3722_LOCK_REG),
+ regmap_reg_range(AS3722_FUSE7_REG, AS3722_FUSE7_REG),
};
static const struct regmap_access_table as3722_readable_table = {
diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c
new file mode 100644
index 000000000000..e9a33c79431b
--- /dev/null
+++ b/drivers/mfd/bcm590xx.c
@@ -0,0 +1,93 @@
+/*
+ * Broadcom BCM590xx PMU
+ *
+ * Copyright 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/bcm590xx.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static const struct mfd_cell bcm590xx_devs[] = {
+ {
+ .name = "bcm590xx-vregs",
+ },
+};
+
+static const struct regmap_config bcm590xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = BCM590XX_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int bcm590xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct bcm590xx *bcm590xx;
+ int ret;
+
+ bcm590xx = devm_kzalloc(&i2c->dev, sizeof(*bcm590xx), GFP_KERNEL);
+ if (!bcm590xx)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, bcm590xx);
+ bcm590xx->dev = &i2c->dev;
+ bcm590xx->i2c_client = i2c;
+
+ bcm590xx->regmap = devm_regmap_init_i2c(i2c, &bcm590xx_regmap_config);
+ if (IS_ERR(bcm590xx->regmap)) {
+ ret = PTR_ERR(bcm590xx->regmap);
+ dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = mfd_add_devices(&i2c->dev, -1, bcm590xx_devs,
+ ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL);
+ if (ret < 0)
+ dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id bcm590xx_of_match[] = {
+ { .compatible = "brcm,bcm59056" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm590xx_of_match);
+
+static const struct i2c_device_id bcm590xx_i2c_id[] = {
+ { "bcm59056" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bcm590xx_i2c_id);
+
+static struct i2c_driver bcm590xx_i2c_driver = {
+ .driver = {
+ .name = "bcm590xx",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bcm590xx_of_match),
+ },
+ .probe = bcm590xx_i2c_probe,
+ .id_table = bcm590xx_i2c_id,
+};
+module_i2c_driver(bcm590xx_i2c_driver);
+
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_DESCRIPTION("BCM590xx multi-function driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bcm590xx");
diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c
index 17c13012686a..be91cb5d6e78 100644
--- a/drivers/mfd/cs5535-mfd.c
+++ b/drivers/mfd/cs5535-mfd.c
@@ -23,7 +23,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/pci.h>
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 25838f10b35b..e8af816d73a9 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -279,6 +279,9 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg)
case DA9052_EVENT_B_REG:
case DA9052_EVENT_C_REG:
case DA9052_EVENT_D_REG:
+ case DA9052_CONTROL_B_REG:
+ case DA9052_CONTROL_D_REG:
+ case DA9052_SUPPLY_REG:
case DA9052_FAULTLOG_REG:
case DA9052_CHG_TIME_REG:
case DA9052_ADC_RES_L_REG:
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index c319c4ef5d49..6da8ec8ff800 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -75,6 +75,7 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
DA9052_PARK_REGISTER,
&val);
break;
+ case DA9053_BC:
default:
/*
* For other chips parking of I2C register
@@ -114,6 +115,7 @@ static const struct i2c_device_id da9052_i2c_id[] = {
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
+ {"da9053-bc", DA9053_BC},
{}
};
@@ -121,8 +123,9 @@ static const struct i2c_device_id da9052_i2c_id[] = {
static const struct of_device_id dialog_dt_ids[] = {
{ .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
{ .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
- { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] },
+ { .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] },
{ .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
+ { .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] },
{ /* sentinel */ }
};
#endif
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
index 0680bcbc53de..17666b40b70c 100644
--- a/drivers/mfd/da9052-spi.c
+++ b/drivers/mfd/da9052-spi.c
@@ -71,6 +71,7 @@ static struct spi_device_id da9052_spi_id[] = {
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
+ {"da9053-bc", DA9053_BC},
{}
};
diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c
index 8103e4362132..d4d4c165eb95 100644
--- a/drivers/mfd/da9055-i2c.c
+++ b/drivers/mfd/da9055-i2c.c
@@ -15,6 +15,8 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/da9055/core.h>
@@ -66,6 +68,11 @@ static struct i2c_device_id da9055_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+static const struct of_device_id da9055_of_match[] = {
+ { .compatible = "dlg,da9055-pmic", },
+ { }
+};
+
static struct i2c_driver da9055_i2c_driver = {
.probe = da9055_i2c_probe,
.remove = da9055_i2c_remove,
@@ -73,6 +80,7 @@ static struct i2c_driver da9055_i2c_driver = {
.driver = {
.name = "da9055-pmic",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(da9055_of_match),
},
};
diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c
index 26937cd01071..e70ae315abc7 100644
--- a/drivers/mfd/da9063-core.c
+++ b/drivers/mfd/da9063-core.c
@@ -110,7 +110,7 @@ static const struct mfd_cell da9063_devs[] = {
int da9063_device_init(struct da9063 *da9063, unsigned int irq)
{
struct da9063_pdata *pdata = da9063->dev->platform_data;
- int model, revision;
+ int model, variant_id, variant_code;
int ret;
if (pdata) {
@@ -141,23 +141,26 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
return -ENODEV;
}
- ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision);
+ ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id);
if (ret < 0) {
- dev_err(da9063->dev, "Cannot read chip revision id.\n");
+ dev_err(da9063->dev, "Cannot read chip variant id.\n");
return -EIO;
}
- revision >>= DA9063_CHIP_VARIANT_SHIFT;
- if (revision != 3) {
- dev_err(da9063->dev, "Unknown chip revision: %d\n", revision);
+
+ variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT;
+
+ dev_info(da9063->dev,
+ "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n",
+ model, variant_id);
+
+ if (variant_code != PMIC_DA9063_BB) {
+ dev_err(da9063->dev, "Unknown chip variant code: 0x%02X\n",
+ variant_code);
return -ENODEV;
}
da9063->model = model;
- da9063->revision = revision;
-
- dev_info(da9063->dev,
- "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n",
- model, revision);
+ da9063->variant_code = variant_code;
ret = da9063_irq_init(da9063);
if (ret) {
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index e43e6e821117..7694e0700d34 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -25,6 +25,7 @@
#include <linux/bitops.h>
#include <linux/fs.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
@@ -2678,16 +2679,12 @@ static struct irq_domain_ops db8500_irq_ops = {
.xlate = irq_domain_xlate_twocell,
};
-static int db8500_irq_init(struct device_node *np, int irq_base)
+static int db8500_irq_init(struct device_node *np)
{
int i;
- /* In the device tree case, just take some IRQs */
- if (np)
- irq_base = 0;
-
db8500_irq_domain = irq_domain_add_simple(
- np, NUM_PRCMU_WAKEUPS, irq_base,
+ np, NUM_PRCMU_WAKEUPS, 0,
&db8500_irq_ops, NULL);
if (!db8500_irq_domain) {
@@ -3114,10 +3111,10 @@ static void db8500_prcmu_update_cpufreq(void)
}
static int db8500_prcmu_register_ab8500(struct device *parent,
- struct ab8500_platform_data *pdata,
- int irq)
+ struct ab8500_platform_data *pdata)
{
- struct resource ab8500_resource = DEFINE_RES_IRQ(irq);
+ struct device_node *np;
+ struct resource ab8500_resource;
struct mfd_cell ab8500_cell = {
.name = "ab8500-core",
.of_compatible = "stericsson,ab8500",
@@ -3128,6 +3125,20 @@ static int db8500_prcmu_register_ab8500(struct device *parent,
.num_resources = 1,
};
+ if (!parent->of_node)
+ return -ENODEV;
+
+ /* Look up the device node, sneak the IRQ out of it */
+ for_each_child_of_node(parent->of_node, np) {
+ if (of_device_is_compatible(np, ab8500_cell.of_compatible))
+ break;
+ }
+ if (!np) {
+ dev_info(parent, "could not find AB8500 node in the device tree\n");
+ return -ENODEV;
+ }
+ of_irq_to_resource_table(np, &ab8500_resource, 1);
+
return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL);
}
@@ -3180,7 +3191,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
goto no_irq_return;
}
- db8500_irq_init(np, pdata->irq_base);
+ db8500_irq_init(np);
prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
@@ -3205,8 +3216,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
}
}
- err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata,
- pdata->ab_irq);
+ err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata);
if (err) {
mfd_remove_devices(&pdev->dev);
pr_err("prcmu: Failed to add ab8500 subdevice\n");
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index 81b7d88af313..433f823037dd 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
index d3e23278d299..07692604e119 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -322,9 +322,12 @@ static int kempld_detect_device(struct kempld_device_data *pld)
return -ENODEV;
}
- /* Release hardware mutex if aquired */
- if (!(index_reg & KEMPLD_MUTEX_KEY))
+ /* Release hardware mutex if acquired */
+ if (!(index_reg & KEMPLD_MUTEX_KEY)) {
iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+ /* PXT and COMe-cPC2 boards may require a second release */
+ iowrite8(KEMPLD_MUTEX_KEY, pld->io_index);
+ }
mutex_unlock(&pld->lock);
@@ -438,6 +441,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "CHL6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
@@ -510,6 +521,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "CVV6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "FRI2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
@@ -533,6 +552,14 @@ static struct dmi_system_id __initdata kempld_dmi_table[] = {
.driver_data = (void *)&kempld_platform_data_generic,
.callback = kempld_create_platform_device,
}, {
+ .ident = "MVV1",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"),
+ },
+ .driver_data = (void *)&kempld_platform_data_generic,
+ .callback = kempld_create_platform_device,
+ }, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index be93fa261ded..3f10ea3f45d1 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -58,7 +58,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -72,9 +71,11 @@
#define ACPIBASE_GPE_END 0x2f
#define ACPIBASE_SMI_OFF 0x30
#define ACPIBASE_SMI_END 0x33
+#define ACPIBASE_PMC_OFF 0x08
+#define ACPIBASE_PMC_END 0x0c
#define ACPIBASE_TCO_OFF 0x60
#define ACPIBASE_TCO_END 0x7f
-#define ACPICTRL 0x44
+#define ACPICTRL_PMCBASE 0x44
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
@@ -90,16 +91,17 @@
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
-struct lpc_ich_cfg {
- int base;
- int ctrl;
- int save;
-};
-
struct lpc_ich_priv {
int chipset;
- struct lpc_ich_cfg acpi;
- struct lpc_ich_cfg gpio;
+
+ int abase; /* ACPI base */
+ int actrl_pbase; /* ACPI control or PMC base */
+ int gbase; /* GPIO base */
+ int gctrl; /* GPIO control */
+
+ int abase_save; /* Cached ACPI base value */
+ int actrl_pbase_save; /* Cached ACPI control or PMC base value */
+ int gctrl_save; /* Cached GPIO control value */
};
static struct resource wdt_ich_res[] = {
@@ -111,7 +113,7 @@ static struct resource wdt_ich_res[] = {
{
.flags = IORESOURCE_IO,
},
- /* GCS */
+ /* GCS or PMC */
{
.flags = IORESOURCE_MEM,
},
@@ -211,6 +213,7 @@ enum lpc_chipsets {
LPC_LPT_LP, /* Lynx Point-LP */
LPC_WBG, /* Wellsburg */
LPC_AVN, /* Avoton SoC */
+ LPC_BAYTRAIL, /* Bay Trail SoC */
LPC_COLETO, /* Coleto Creek */
LPC_WPT_LP, /* Wildcat Point-LP */
};
@@ -303,6 +306,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
[LPC_NM10] = {
.name = "NM10",
.iTCO_version = 2,
+ .gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8] = {
.name = "ICH8 or ICH8R",
@@ -499,7 +503,12 @@ static struct lpc_ich_info lpc_chipset_info[] = {
},
[LPC_AVN] = {
.name = "Avoton SoC",
- .iTCO_version = 1,
+ .iTCO_version = 3,
+ .gpio_version = AVOTON_GPIO,
+ },
+ [LPC_BAYTRAIL] = {
+ .name = "Bay Trail SoC",
+ .iTCO_version = 3,
},
[LPC_COLETO] = {
.name = "Coleto Creek",
@@ -726,6 +735,7 @@ static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN},
{ PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN},
{ PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN},
+ { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL},
{ PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO},
{ PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP},
{ PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP},
@@ -742,14 +752,20 @@ static void lpc_ich_restore_config_space(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
- if (priv->acpi.save >= 0) {
- pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save);
- priv->acpi.save = -1;
+ if (priv->abase_save >= 0) {
+ pci_write_config_byte(dev, priv->abase, priv->abase_save);
+ priv->abase_save = -1;
+ }
+
+ if (priv->actrl_pbase_save >= 0) {
+ pci_write_config_byte(dev, priv->actrl_pbase,
+ priv->actrl_pbase_save);
+ priv->actrl_pbase_save = -1;
}
- if (priv->gpio.save >= 0) {
- pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save);
- priv->gpio.save = -1;
+ if (priv->gctrl_save >= 0) {
+ pci_write_config_byte(dev, priv->gctrl, priv->gctrl_save);
+ priv->gctrl_save = -1;
}
}
@@ -758,9 +774,26 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, priv->acpi.ctrl, &reg_save);
- pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10);
- priv->acpi.save = reg_save;
+ switch (lpc_chipset_info[priv->chipset].iTCO_version) {
+ case 3:
+ /*
+ * Some chipsets (eg Avoton) enable the ACPI space in the
+ * ACPI BASE register.
+ */
+ pci_read_config_byte(dev, priv->abase, &reg_save);
+ pci_write_config_byte(dev, priv->abase, reg_save | 0x2);
+ priv->abase_save = reg_save;
+ break;
+ default:
+ /*
+ * Most chipsets enable the ACPI space in the ACPI control
+ * register.
+ */
+ pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+ pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x80);
+ priv->actrl_pbase_save = reg_save;
+ break;
+ }
}
static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
@@ -768,9 +801,20 @@ static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
- pci_read_config_byte(dev, priv->gpio.ctrl, &reg_save);
- pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10);
- priv->gpio.save = reg_save;
+ pci_read_config_byte(dev, priv->gctrl, &reg_save);
+ pci_write_config_byte(dev, priv->gctrl, reg_save | 0x10);
+ priv->gctrl_save = reg_save;
+}
+
+static void lpc_ich_enable_pmc_space(struct pci_dev *dev)
+{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+ u8 reg_save;
+
+ pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+ pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x2);
+
+ priv->actrl_pbase_save = reg_save;
}
static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
@@ -815,7 +859,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -841,7 +885,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
gpe0_done:
/* Setup GPIO base register */
- pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->gbase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
@@ -891,7 +935,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
struct resource *res;
/* Setup power management base register */
- pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
+ pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -910,14 +954,20 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
lpc_ich_enable_acpi_space(dev);
/*
+ * iTCO v2:
* Get the Memory-Mapped GCS register. To get access to it
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
+ *
+ * iTCO v3:
+ * Get the Power Management Configuration register. To get access
+ * to it we have to read the PMC BASE from config space and address
+ * the register at offset 0x8.
*/
if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
/* Don't register iomem for TCO ver 1 */
lpc_ich_cells[LPC_WDT].num_resources--;
- } else {
+ } else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
if (!(base_addr_cfg & 1)) {
@@ -926,9 +976,17 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
ret = -ENODEV;
goto wdt_done;
}
- res = wdt_mem_res(ICH_RES_MEM_GCS);
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
res->start = base_addr + ACPIBASE_GCS_OFF;
res->end = base_addr + ACPIBASE_GCS_END;
+ } else if (lpc_chipset_info[priv->chipset].iTCO_version == 3) {
+ lpc_ich_enable_pmc_space(dev);
+ pci_read_config_dword(dev, ACPICTRL_PMCBASE, &base_addr_cfg);
+ base_addr = base_addr_cfg & 0xfffffe00;
+
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
+ res->start = base_addr + ACPIBASE_PMC_OFF;
+ res->end = base_addr + ACPIBASE_PMC_END;
}
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
@@ -952,28 +1010,35 @@ static int lpc_ich_probe(struct pci_dev *dev,
return -ENOMEM;
priv->chipset = id->driver_data;
- priv->acpi.save = -1;
- priv->acpi.base = ACPIBASE;
- priv->acpi.ctrl = ACPICTRL;
- priv->gpio.save = -1;
+ priv->actrl_pbase_save = -1;
+ priv->abase_save = -1;
+
+ priv->abase = ACPIBASE;
+ priv->actrl_pbase = ACPICTRL_PMCBASE;
+
+ priv->gctrl_save = -1;
if (priv->chipset <= LPC_ICH5) {
- priv->gpio.base = GPIOBASE_ICH0;
- priv->gpio.ctrl = GPIOCTRL_ICH0;
+ priv->gbase = GPIOBASE_ICH0;
+ priv->gctrl = GPIOCTRL_ICH0;
} else {
- priv->gpio.base = GPIOBASE_ICH6;
- priv->gpio.ctrl = GPIOCTRL_ICH6;
+ priv->gbase = GPIOBASE_ICH6;
+ priv->gctrl = GPIOCTRL_ICH6;
}
pci_set_drvdata(dev, priv);
- ret = lpc_ich_init_wdt(dev);
- if (!ret)
- cell_added = true;
+ if (lpc_chipset_info[priv->chipset].iTCO_version) {
+ ret = lpc_ich_init_wdt(dev);
+ if (!ret)
+ cell_added = true;
+ }
- ret = lpc_ich_init_gpio(dev);
- if (!ret)
- cell_added = true;
+ if (lpc_chipset_info[priv->chipset].gpio_version) {
+ ret = lpc_ich_init_gpio(dev);
+ if (!ret)
+ cell_added = true;
+ }
/*
* We only care if at least one or none of the cells registered
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index 3bb05c03c68d..4ee755034f3b 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -23,7 +23,6 @@
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 71aa14a6bfbb..5f13cefe8def 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -18,6 +18,7 @@
* This driver is based on max8997.c
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
@@ -25,7 +26,10 @@
#include <linux/mfd/max14577-private.h>
static struct mfd_cell max14577_devs[] = {
- { .name = "max14577-muic", },
+ {
+ .name = "max14577-muic",
+ .of_compatible = "maxim,max14577-muic",
+ },
{
.name = "max14577-regulator",
.of_compatible = "maxim,max14577-regulator",
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index f53d5823a3f7..e5fce765accb 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -121,6 +121,10 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
dev_info(max77686->dev, "device found\n");
max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ if (!max77686->rtc) {
+ dev_err(max77686->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max77686->rtc, max77686);
max77686_irq_init(max77686);
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index e0859987ab6b..c5535f018466 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -148,9 +148,18 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ if (!max77693->muic) {
+ dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max77693->muic, max77693);
max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ if (!max77693->haptic) {
+ dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n");
+ ret = -ENODEV;
+ goto err_i2c_haptic;
+ }
i2c_set_clientdata(max77693->haptic, max77693);
/*
@@ -184,8 +193,9 @@ err_mfd:
max77693_irq_exit(max77693);
err_irq:
err_regmap_muic:
- i2c_unregister_device(max77693->muic);
i2c_unregister_device(max77693->haptic);
+err_i2c_haptic:
+ i2c_unregister_device(max77693->muic);
return ret;
}
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
index 176aa26fc787..a83eed5c15ca 100644
--- a/drivers/mfd/max8925-i2c.c
+++ b/drivers/mfd/max8925-i2c.c
@@ -181,9 +181,18 @@ static int max8925_probe(struct i2c_client *client,
mutex_init(&chip->io_lock);
chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR);
+ if (!chip->rtc) {
+ dev_err(chip->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(chip->rtc, chip);
chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
+ if (!chip->adc) {
+ dev_err(chip->dev, "Failed to allocate I2C device for ADC\n");
+ i2c_unregister_device(chip->rtc);
+ return -ENODEV;
+ }
i2c_set_clientdata(chip->adc, chip);
device_init_wakeup(&client->dev, 1);
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 5adede0fb04c..8cf7a015cfe5 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -208,10 +208,26 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
mutex_init(&max8997->iolock);
max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ if (!max8997->rtc) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max8997->rtc, max8997);
+
max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ if (!max8997->haptic) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n");
+ ret = -ENODEV;
+ goto err_i2c_haptic;
+ }
i2c_set_clientdata(max8997->haptic, max8997);
+
max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ if (!max8997->muic) {
+ dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n");
+ ret = -ENODEV;
+ goto err_i2c_muic;
+ }
i2c_set_clientdata(max8997->muic, max8997);
pm_runtime_set_active(max8997->dev);
@@ -239,7 +255,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
err_mfd:
mfd_remove_devices(max8997->dev);
i2c_unregister_device(max8997->muic);
+err_i2c_muic:
i2c_unregister_device(max8997->haptic);
+err_i2c_haptic:
i2c_unregister_device(max8997->rtc);
return ret;
}
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index 5d5e186b5d8b..592db06098e6 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -215,6 +215,10 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
mutex_init(&max8998->iolock);
max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ if (!max8998->rtc) {
+ dev_err(&i2c->dev, "Failed to allocate I2C device for RTC\n");
+ return -ENODEV;
+ }
i2c_set_clientdata(max8998->rtc, max8998);
max8998_irq_init(max8998);
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
index 38ab67829791..702925e242c9 100644
--- a/drivers/mfd/mc13xxx-spi.c
+++ b/drivers/mfd/mc13xxx-spi.c
@@ -140,6 +140,11 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
mc13xxx->irq = spi->irq;
+ spi->max_speed_hz = spi->max_speed_hz ? : 26000000;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
mc13xxx->regmap = devm_regmap_init(&spi->dev, &regmap_mc13xxx_bus,
&spi->dev,
&mc13xxx_regmap_spi_config);
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 41c31b3ac940..29d76986b40b 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -12,7 +12,6 @@
* MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/kernel.h>
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 90b630ccc8bc..651e249287dc 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -665,55 +665,78 @@ static int usbhs_omap_probe(struct platform_device *pdev)
goto err_mem;
}
- need_logic_fck = false;
+ /* Set all clocks as invalid to begin with */
+ omap->ehci_logic_fck = ERR_PTR(-ENODEV);
+ omap->init_60m_fclk = ERR_PTR(-ENODEV);
+ omap->utmi_p1_gfclk = ERR_PTR(-ENODEV);
+ omap->utmi_p2_gfclk = ERR_PTR(-ENODEV);
+ omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV);
+ omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV);
+
for (i = 0; i < omap->nports; i++) {
- if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
- is_ehci_hsic_mode(i))
- need_logic_fck |= true;
+ omap->utmi_clk[i] = ERR_PTR(-ENODEV);
+ omap->hsic480m_clk[i] = ERR_PTR(-ENODEV);
+ omap->hsic60m_clk[i] = ERR_PTR(-ENODEV);
}
- omap->ehci_logic_fck = ERR_PTR(-EINVAL);
- if (need_logic_fck) {
- omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
- if (IS_ERR(omap->ehci_logic_fck)) {
- ret = PTR_ERR(omap->ehci_logic_fck);
- dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret);
+ /* for OMAP3 i.e. USBHS REV1 */
+ if (omap->usbhs_rev == OMAP_USBHS_REV1) {
+ need_logic_fck = false;
+ for (i = 0; i < omap->nports; i++) {
+ if (is_ehci_phy_mode(pdata->port_mode[i]) ||
+ is_ehci_tll_mode(pdata->port_mode[i]) ||
+ is_ehci_hsic_mode(pdata->port_mode[i]))
+
+ need_logic_fck |= true;
}
+
+ if (need_logic_fck) {
+ omap->ehci_logic_fck = devm_clk_get(dev,
+ "usbhost_120m_fck");
+ if (IS_ERR(omap->ehci_logic_fck)) {
+ ret = PTR_ERR(omap->ehci_logic_fck);
+ dev_err(dev, "usbhost_120m_fck failed:%d\n",
+ ret);
+ goto err_mem;
+ }
+ }
+ goto initialize;
}
- omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk");
+ /* for OMAP4+ i.e. USBHS REV2+ */
+ omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk");
if (IS_ERR(omap->utmi_p1_gfclk)) {
ret = PTR_ERR(omap->utmi_p1_gfclk);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_p1_gfclk;
+ goto err_mem;
}
- omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk");
+ omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk");
if (IS_ERR(omap->utmi_p2_gfclk)) {
ret = PTR_ERR(omap->utmi_p2_gfclk);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_p2_gfclk;
+ goto err_mem;
}
- omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
+ omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1");
if (IS_ERR(omap->xclk60mhsp1_ck)) {
ret = PTR_ERR(omap->xclk60mhsp1_ck);
- dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_xclk60mhsp1;
+ dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret);
+ goto err_mem;
}
- omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
+ omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
- dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_xclk60mhsp2;
+ dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret);
+ goto err_mem;
}
- omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
+ omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int");
if (IS_ERR(omap->init_60m_fclk)) {
ret = PTR_ERR(omap->init_60m_fclk);
- dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_init60m;
+ dev_err(dev, "refclk_60m_int failed error:%d\n", ret);
+ goto err_mem;
}
for (i = 0; i < omap->nports; i++) {
@@ -727,55 +750,72 @@ static int usbhs_omap_probe(struct platform_device *pdev)
* platforms have all clocks and we can function without
* them
*/
- omap->utmi_clk[i] = clk_get(dev, clkname);
- if (IS_ERR(omap->utmi_clk[i]))
- dev_dbg(dev, "Failed to get clock : %s : %ld\n",
- clkname, PTR_ERR(omap->utmi_clk[i]));
+ omap->utmi_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->utmi_clk[i])) {
+ ret = PTR_ERR(omap->utmi_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
snprintf(clkname, sizeof(clkname),
"usb_host_hs_hsic480m_p%d_clk", i + 1);
- omap->hsic480m_clk[i] = clk_get(dev, clkname);
- if (IS_ERR(omap->hsic480m_clk[i]))
- dev_dbg(dev, "Failed to get clock : %s : %ld\n",
- clkname, PTR_ERR(omap->hsic480m_clk[i]));
+ omap->hsic480m_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic480m_clk[i])) {
+ ret = PTR_ERR(omap->hsic480m_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
snprintf(clkname, sizeof(clkname),
"usb_host_hs_hsic60m_p%d_clk", i + 1);
- omap->hsic60m_clk[i] = clk_get(dev, clkname);
- if (IS_ERR(omap->hsic60m_clk[i]))
- dev_dbg(dev, "Failed to get clock : %s : %ld\n",
- clkname, PTR_ERR(omap->hsic60m_clk[i]));
+ omap->hsic60m_clk[i] = devm_clk_get(dev, clkname);
+ if (IS_ERR(omap->hsic60m_clk[i])) {
+ ret = PTR_ERR(omap->hsic60m_clk[i]);
+ dev_err(dev, "Failed to get clock : %s : %d\n",
+ clkname, ret);
+ goto err_mem;
+ }
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
- /* for OMAP3, clk_set_parent fails */
ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->xclk60mhsp1_ck);
- if (ret != 0)
- dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->init_60m_fclk);
- if (ret != 0)
- dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->xclk60mhsp2_ck);
- if (ret != 0)
- dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->init_60m_fclk);
- if (ret != 0)
- dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n",
- ret);
+ if (ret != 0) {
+ dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n",
+ ret);
+ goto err_mem;
+ }
}
+initialize:
omap_usbhs_init(dev);
if (dev->of_node) {
@@ -784,7 +824,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "Failed to create DT children: %d\n", ret);
- goto err_alloc;
+ goto err_mem;
}
} else {
@@ -792,40 +832,12 @@ static int usbhs_omap_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "omap_usbhs_alloc_children failed: %d\n",
ret);
- goto err_alloc;
+ goto err_mem;
}
}
return 0;
-err_alloc:
- for (i = 0; i < omap->nports; i++) {
- if (!IS_ERR(omap->utmi_clk[i]))
- clk_put(omap->utmi_clk[i]);
- if (!IS_ERR(omap->hsic60m_clk[i]))
- clk_put(omap->hsic60m_clk[i]);
- if (!IS_ERR(omap->hsic480m_clk[i]))
- clk_put(omap->hsic480m_clk[i]);
- }
-
- clk_put(omap->init_60m_fclk);
-
-err_init60m:
- clk_put(omap->xclk60mhsp2_ck);
-
-err_xclk60mhsp2:
- clk_put(omap->xclk60mhsp1_ck);
-
-err_xclk60mhsp1:
- clk_put(omap->utmi_p2_gfclk);
-
-err_p2_gfclk:
- clk_put(omap->utmi_p1_gfclk);
-
-err_p1_gfclk:
- if (!IS_ERR(omap->ehci_logic_fck))
- clk_put(omap->ehci_logic_fck);
-
err_mem:
pm_runtime_disable(dev);
@@ -847,27 +859,6 @@ static int usbhs_omap_remove_child(struct device *dev, void *data)
*/
static int usbhs_omap_remove(struct platform_device *pdev)
{
- struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < omap->nports; i++) {
- if (!IS_ERR(omap->utmi_clk[i]))
- clk_put(omap->utmi_clk[i]);
- if (!IS_ERR(omap->hsic60m_clk[i]))
- clk_put(omap->hsic60m_clk[i]);
- if (!IS_ERR(omap->hsic480m_clk[i]))
- clk_put(omap->hsic480m_clk[i]);
- }
-
- clk_put(omap->init_60m_fclk);
- clk_put(omap->utmi_p1_gfclk);
- clk_put(omap->utmi_p2_gfclk);
- clk_put(omap->xclk60mhsp2_ck);
- clk_put(omap->xclk60mhsp1_ck);
-
- if (!IS_ERR(omap->ehci_logic_fck))
- clk_put(omap->ehci_logic_fck);
-
pm_runtime_disable(&pdev->dev);
/* remove children */
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 5ee50f779ef6..532eacab6b46 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -252,7 +252,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
break;
}
- tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]),
+ tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch,
GFP_KERNEL);
if (!tll->ch_clk) {
ret = -ENOMEM;
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
index b8941a556d71..c1984b0d1b65 100644
--- a/drivers/mfd/pcf50633-adc.c
+++ b/drivers/mfd/pcf50633-adc.c
@@ -19,7 +19,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 484fe66e6c88..b97a97187ae9 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -14,23 +14,316 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ssbi.h>
+#include <linux/regmap.h>
+#include <linux/of_platform.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
+#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define PM_IRQF_LVL_SEL 0x01 /* level select */
+#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
+#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
+#define PM_IRQF_CLR 0x08 /* clear interrupt */
+#define PM_IRQF_BITS_MASK 0x70
+#define PM_IRQF_BITS_SHIFT 4
+#define PM_IRQF_WRITE 0x80
+
+#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
+ PM_IRQF_MASK_RE)
+
#define REG_HWREV 0x002 /* PMIC4 revision */
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+#define PM8921_NR_IRQS 256
+
+struct pm_irq_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ spinlock_t pm_irq_lock;
+ struct irq_domain *irqdomain;
+ unsigned int num_irqs;
+ unsigned int num_blocks;
+ unsigned int num_masters;
+ u8 config[0];
+};
+
struct pm8921 {
struct device *dev;
struct pm_irq_chip *irq_chip;
};
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
+ unsigned int *ip)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int
+pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ cp |= PM_IRQF_WRITE;
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ if (rc)
+ pr_err("Failed Configuring IRQ rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
+{
+ int pmirq, irq, i, ret = 0;
+ unsigned int bits;
+
+ ret = pm8xxx_read_block_irq(chip, block, &bits);
+ if (ret) {
+ pr_err("Failed reading %d block ret=%d", block, ret);
+ return ret;
+ }
+ if (!bits) {
+ pr_err("block bit set in master but no irqs: %d", block);
+ return 0;
+ }
+
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & (1 << i)) {
+ pmirq = block * 8 + i;
+ irq = irq_find_mapping(chip->irqdomain, pmirq);
+ generic_handle_irq(irq);
+ }
+ }
+ return 0;
+}
+
+static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
+{
+ unsigned int blockbits;
+ int block_number, i, ret = 0;
+
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master,
+ &blockbits);
+ if (ret) {
+ pr_err("Failed to read master %d ret=%d\n", master, ret);
+ return ret;
+ }
+ if (!blockbits) {
+ pr_err("master bit set in root but no blocks: %d", master);
+ return 0;
+ }
+
+ for (i = 0; i < 8; i++)
+ if (blockbits & (1 << i)) {
+ block_number = master * 8 + i; /* block # */
+ ret |= pm8xxx_irq_block_handler(chip, block_number);
+ }
+ return ret;
+}
+
+static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int root;
+ int i, ret, masters = 0;
+
+ chained_irq_enter(irq_chip, desc);
+
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root);
+ if (ret) {
+ pr_err("Can't read root status ret=%d\n", ret);
+ return;
+ }
+
+ /* on pm8xxx series masters start from bit 1 of the root */
+ masters = root >> 1;
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->num_masters; i++)
+ if (masters & (1 << i))
+ pm8xxx_irq_master_handler(chip, i);
+
+ chained_irq_exit(irq_chip, desc);
+}
+
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq];
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
+ | PM_IRQF_MASK_ALL;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ } else {
+ chip->config[pmirq] |= PM_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ else
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ }
+
+ config = chip->config[pmirq] | PM_IRQF_CLR;
+ return pm8xxx_config_irq(chip, block, config);
+}
+
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "pm8xxx",
+ .irq_mask_ack = pm8xxx_irq_mask_ack,
+ .irq_unmask = pm8xxx_irq_unmask,
+ .irq_set_type = pm8xxx_irq_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+/**
+ * pm8xxx_get_irq_stat - get the status of the irq line
+ * @chip: pointer to identify a pmic irq controller
+ * @irq: the irq number
+ *
+ * The pm8xxx gpio and mpp rely on the interrupt block to read
+ * the values on their pins. This function is to facilitate reading
+ * the status of a gpio or an mpp line. The caller has to convert the
+ * gpio number to irq number.
+ *
+ * RETURNS:
+ * an int indicating the value read on that line
+ */
+static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
+{
+ int pmirq, rc;
+ unsigned int block, bits, bit;
+ unsigned long flags;
+ struct irq_data *irq_data = irq_get_irq_data(irq);
+
+ pmirq = irq_data->hwirq;
+
+ block = pmirq / 8;
+ bit = pmirq % 8;
+
+ spin_lock_irqsave(&chip->pm_irq_lock, flags);
+
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ if (rc) {
+ pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ if (rc) {
+ pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+ spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
+
+ return rc;
+}
+
+static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct pm_irq_chip *chip = d->host_data;
+
+ irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ return 0;
+}
+
+static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8xxx_irq_domain_map,
+};
+
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
@@ -81,42 +374,35 @@ static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_read_irq_stat = pm8921_read_irq_stat,
};
-static int pm8921_add_subdevices(const struct pm8921_platform_data
- *pdata,
- struct pm8921 *pmic,
- u32 rev)
-{
- int ret = 0, irq_base = 0;
- struct pm_irq_chip *irq_chip;
-
- if (pdata->irq_pdata) {
- pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
- irq_base = pdata->irq_pdata->irq_base;
- irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+static const struct regmap_config ssbi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x3ff,
+ .fast_io = true,
+ .reg_read = ssbi_reg_read,
+ .reg_write = ssbi_reg_write
+};
- if (IS_ERR(irq_chip)) {
- pr_err("Failed to init interrupts ret=%ld\n",
- PTR_ERR(irq_chip));
- return PTR_ERR(irq_chip);
- }
- pmic->irq_chip = irq_chip;
- }
- return ret;
-}
+static const struct of_device_id pm8921_id_table[] = {
+ { .compatible = "qcom,pm8058", },
+ { .compatible = "qcom,pm8921", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8921_id_table);
static int pm8921_probe(struct platform_device *pdev)
{
- const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct pm8921 *pmic;
- int rc;
- u8 val;
+ struct regmap *regmap;
+ int irq, rc;
+ unsigned int val;
u32 rev;
+ struct pm_irq_chip *chip;
+ unsigned int nirqs = PM8921_NR_IRQS;
- if (!pdata) {
- pr_err("missing platform data\n");
- return -EINVAL;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL);
if (!pmic) {
@@ -124,8 +410,13 @@ static int pm8921_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
+ &ssbi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
/* Read PMIC chip revision */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV, &val);
if (rc) {
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
return rc;
@@ -134,7 +425,7 @@ static int pm8921_probe(struct platform_device *pdev)
rev = val;
/* Read PMIC chip revision 2 */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV_2, &val);
if (rc) {
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
REG_HWREV_2, rc);
@@ -147,37 +438,56 @@ static int pm8921_probe(struct platform_device *pdev)
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);
- rc = pm8921_add_subdevices(pdata, pmic, rev);
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
+ sizeof(chip->config[0]) * nirqs,
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ pmic->irq_chip = chip;
+ chip->dev = &pdev->dev;
+ chip->regmap = regmap;
+ chip->num_irqs = nirqs;
+ chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+ chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+ spin_lock_init(&chip->pm_irq_lock);
+
+ chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
+ &pm8xxx_irq_domain_ops,
+ chip);
+ if (!chip->irqdomain)
+ return -ENODEV;
+
+ irq_set_handler_data(irq, chip);
+ irq_set_chained_handler(irq, pm8xxx_irq_handler);
+ irq_set_irq_wake(irq, 1);
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
- pr_err("Cannot add subdevices rc=%d\n", rc);
- goto err;
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
}
- /* gpio might not work if no irq device is found */
- WARN_ON(pmic->irq_chip == NULL);
+ return rc;
+}
+static int pm8921_remove_child(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
return 0;
-
-err:
- mfd_remove_devices(pmic->dev);
- return rc;
}
static int pm8921_remove(struct platform_device *pdev)
{
- struct pm8xxx_drvdata *drvdata;
- struct pm8921 *pmic = NULL;
-
- drvdata = platform_get_drvdata(pdev);
- if (drvdata)
- pmic = drvdata->pm_chip_data;
- if (pmic) {
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8xxx_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
- }
- }
+ int irq = platform_get_irq(pdev, 0);
+ struct pm8921 *pmic = pm8921_drvdata.pm_chip_data;
+ struct pm_irq_chip *chip = pmic->irq_chip;
+
+ device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
return 0;
}
@@ -188,6 +498,7 @@ static struct platform_driver pm8921_driver = {
.driver = {
.name = "pm8921-core",
.owner = THIS_MODULE,
+ .of_match_table = pm8921_id_table,
},
};
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
deleted file mode 100644
index 1360e20adf11..000000000000
--- a/drivers/mfd/pm8xxx-irq.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/irq.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-/* PMIC8xxx IRQ */
-
-#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
-
-#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
-#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
-#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
-#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
-#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
-#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
-#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
-#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
-#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
-
-#define PM_IRQF_LVL_SEL 0x01 /* level select */
-#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
-#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
-#define PM_IRQF_CLR 0x08 /* clear interrupt */
-#define PM_IRQF_BITS_MASK 0x70
-#define PM_IRQF_BITS_SHIFT 4
-#define PM_IRQF_WRITE 0x80
-
-#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
- PM_IRQF_MASK_RE)
-
-struct pm_irq_chip {
- struct device *dev;
- spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
- unsigned int num_irqs;
- unsigned int num_blocks;
- unsigned int num_masters;
- u8 config[0];
-};
-
-static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
-{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
-}
-
-static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
-{
- return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
-}
-
-static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
- if (rc)
- pr_err("Failed Reading Status rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
- if (rc)
- pr_err("Failed Configuring IRQ rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
-{
- int pmirq, irq, i, ret = 0;
- u8 bits;
-
- ret = pm8xxx_read_block_irq(chip, block, &bits);
- if (ret) {
- pr_err("Failed reading %d block ret=%d", block, ret);
- return ret;
- }
- if (!bits) {
- pr_err("block bit set in master but no irqs: %d", block);
- return 0;
- }
-
- /* Check IRQ bits */
- for (i = 0; i < 8; i++) {
- if (bits & (1 << i)) {
- pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
- generic_handle_irq(irq);
- }
- }
- return 0;
-}
-
-static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
-{
- u8 blockbits;
- int block_number, i, ret = 0;
-
- ret = pm8xxx_read_master_irq(chip, master, &blockbits);
- if (ret) {
- pr_err("Failed to read master %d ret=%d\n", master, ret);
- return ret;
- }
- if (!blockbits) {
- pr_err("master bit set in root but no blocks: %d", master);
- return 0;
- }
-
- for (i = 0; i < 8; i++)
- if (blockbits & (1 << i)) {
- block_number = master * 8 + i; /* block # */
- ret |= pm8xxx_irq_block_handler(chip, block_number);
- }
- return ret;
-}
-
-static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
- struct irq_chip *irq_chip = irq_desc_get_chip(desc);
- u8 root;
- int i, ret, masters = 0;
-
- ret = pm8xxx_read_root_irq(chip, &root);
- if (ret) {
- pr_err("Can't read root status ret=%d\n", ret);
- return;
- }
-
- /* on pm8xxx series masters start from bit 1 of the root */
- masters = root >> 1;
-
- /* Read allowed masters for blocks. */
- for (i = 0; i < chip->num_masters; i++)
- if (masters & (1 << i))
- pm8xxx_irq_master_handler(chip, i);
-
- irq_chip->irq_ack(&desc->irq_data);
-}
-
-static void pm8xxx_irq_mask_ack(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
- pm8xxx_config_irq(chip, block, config);
-}
-
-static void pm8xxx_irq_unmask(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq];
- pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
- | PM_IRQF_MASK_ALL;
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- if (flow_type & IRQF_TRIGGER_RISING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- if (flow_type & IRQF_TRIGGER_FALLING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- } else {
- chip->config[pmirq] |= PM_IRQF_LVL_SEL;
-
- if (flow_type & IRQF_TRIGGER_HIGH)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- else
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- }
-
- config = chip->config[pmirq] | PM_IRQF_CLR;
- return pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- return 0;
-}
-
-static struct irq_chip pm8xxx_irq_chip = {
- .name = "pm8xxx",
- .irq_mask_ack = pm8xxx_irq_mask_ack,
- .irq_unmask = pm8xxx_irq_unmask,
- .irq_set_type = pm8xxx_irq_set_type,
- .irq_set_wake = pm8xxx_irq_set_wake,
- .flags = IRQCHIP_MASK_ON_SUSPEND,
-};
-
-/**
- * pm8xxx_get_irq_stat - get the status of the irq line
- * @chip: pointer to identify a pmic irq controller
- * @irq: the irq number
- *
- * The pm8xxx gpio and mpp rely on the interrupt block to read
- * the values on their pins. This function is to facilitate reading
- * the status of a gpio or an mpp line. The caller has to convert the
- * gpio number to irq number.
- *
- * RETURNS:
- * an int indicating the value read on that line
- */
-int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
-{
- int pmirq, rc;
- u8 block, bits, bit;
- unsigned long flags;
-
- if (chip == NULL || irq < chip->irq_base ||
- irq >= chip->irq_base + chip->num_irqs)
- return -EINVAL;
-
- pmirq = irq - chip->irq_base;
-
- block = pmirq / 8;
- bit = pmirq % 8;
-
- spin_lock_irqsave(&chip->pm_irq_lock, flags);
-
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
- if (rc) {
- pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
- if (rc) {
- pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = (bits & (1 << bit)) ? 1 : 0;
-
-bail_out:
- spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
-
-struct pm_irq_chip * pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- struct pm_irq_chip *chip;
- int devirq, rc;
- unsigned int pmirq;
-
- if (!pdata) {
- pr_err("No platform data\n");
- return ERR_PTR(-EINVAL);
- }
-
- devirq = pdata->devirq;
- if (devirq < 0) {
- pr_err("missing devirq\n");
- rc = devirq;
- return ERR_PTR(-EINVAL);
- }
-
- chip = kzalloc(sizeof(struct pm_irq_chip)
- + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
- if (!chip) {
- pr_err("Cannot alloc pm_irq_chip struct\n");
- return ERR_PTR(-EINVAL);
- }
-
- chip->dev = dev;
- chip->devirq = devirq;
- chip->irq_base = pdata->irq_base;
- chip->num_irqs = pdata->irq_cdata.nirqs;
- chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
- chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
- spin_lock_init(&chip->pm_irq_lock);
-
- for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
- irq_set_chip_and_handler(chip->irq_base + pmirq,
- &pm8xxx_irq_chip,
- handle_level_irq);
- irq_set_chip_data(chip->irq_base + pmirq, chip);
-#ifdef CONFIG_ARM
- set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
-#else
- irq_set_noprobe(chip->irq_base + pmirq);
-#endif
- }
-
- irq_set_irq_type(devirq, pdata->irq_trigger_flag);
- irq_set_handler_data(devirq, chip);
- irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
-
- return chip;
-}
-
-int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- irq_set_chained_handler(chip->devirq, NULL);
- kfree(chip);
- return 0;
-}
diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c
index b41db5968706..bb8502020274 100644
--- a/drivers/mfd/rc5t583-irq.c
+++ b/drivers/mfd/rc5t583-irq.c
@@ -22,7 +22,6 @@
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/mfd/rc5t583.h>
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index d346146249a2..c79569750be9 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -19,7 +19,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c
index c8f345f7e9a2..663f8a37aa6b 100644
--- a/drivers/mfd/retu-mfd.c
+++ b/drivers/mfd/retu-mfd.c
@@ -19,7 +19,6 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/irq.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/module.h>
diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c
new file mode 100644
index 000000000000..b53b9d46cc45
--- /dev/null
+++ b/drivers/mfd/rtsx_usb.c
@@ -0,0 +1,760 @@
+/* Driver for Realtek USB card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Roger Tseng <rogerable@realtek.com>
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rtsx_usb.h>
+
+static int polling_pipe = 1;
+module_param(polling_pipe, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)");
+
+static struct mfd_cell rtsx_usb_cells[] = {
+ [RTSX_USB_SD_CARD] = {
+ .name = "rtsx_usb_sdmmc",
+ .pdata_size = 0,
+ },
+ [RTSX_USB_MS_CARD] = {
+ .name = "rtsx_usb_ms",
+ .pdata_size = 0,
+ },
+};
+
+static void rtsx_usb_sg_timed_out(unsigned long data)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)data;
+
+ dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__);
+ usb_sg_cancel(&ucr->current_sg);
+
+ /* we know the cancellation is caused by time-out */
+ ucr->current_sg.status = -ETIMEDOUT;
+}
+
+static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
+ unsigned int pipe, struct scatterlist *sg, int num_sg,
+ unsigned int length, unsigned int *act_len, int timeout)
+{
+ int ret;
+
+ dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n",
+ __func__, length, num_sg);
+ ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0,
+ sg, num_sg, length, GFP_NOIO);
+ if (ret)
+ return ret;
+
+ ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
+ add_timer(&ucr->sg_timer);
+ usb_sg_wait(&ucr->current_sg);
+ del_timer(&ucr->sg_timer);
+
+ if (act_len)
+ *act_len = ucr->current_sg.bytes;
+
+ return ucr->current_sg.status;
+}
+
+int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe,
+ void *buf, unsigned int len, int num_sg,
+ unsigned int *act_len, int timeout)
+{
+ if (timeout < 600)
+ timeout = 600;
+
+ if (num_sg)
+ return rtsx_usb_bulk_transfer_sglist(ucr, pipe,
+ (struct scatterlist *)buf, num_sg, len, act_len,
+ timeout);
+ else
+ return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len,
+ timeout);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data);
+
+static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 seq_type)
+{
+ rtsx_usb_cmd_hdr_tag(ucr);
+
+ ucr->cmd_buf[PACKET_TYPE] = seq_type;
+ ucr->cmd_buf[5] = (u8)(len >> 8);
+ ucr->cmd_buf[6] = (u8)len;
+ ucr->cmd_buf[8] = (u8)(addr >> 8);
+ ucr->cmd_buf[9] = (u8)addr;
+
+ if (seq_type == SEQ_WRITE)
+ ucr->cmd_buf[STAGE_FLAG] = 0;
+ else
+ ucr->cmd_buf[STAGE_FLAG] = STAGE_R;
+}
+
+static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 *data)
+{
+ u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4);
+
+ if (!data)
+ return -EINVAL;
+
+ if (cmd_len > IOBUF_SIZE)
+ return -EINVAL;
+
+ rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE);
+ memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len);
+
+ return rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, cmd_len, 0, NULL, 100);
+}
+
+static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr,
+ u16 addr, u16 len, u8 *data)
+{
+ int i, ret;
+ u16 rsp_len = round_down(len, 4);
+ u16 res_len = len - rsp_len;
+
+ if (!data)
+ return -EINVAL;
+
+ /* 4-byte aligned part */
+ if (rsp_len) {
+ rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ);
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, 12, 0, NULL, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN),
+ data, rsp_len, 0, NULL, 100);
+ if (ret)
+ return ret;
+ }
+
+ /* unaligned part */
+ for (i = 0; i < res_len; i++) {
+ ret = rtsx_usb_read_register(ucr, addr + rsp_len + i,
+ data + rsp_len + i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len)
+{
+ return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf);
+
+int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len)
+{
+ return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf);
+
+int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr,
+ u8 mask, u8 data)
+{
+ u16 value, index;
+
+ addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT;
+ value = swab16(addr);
+ index = mask | data << 8;
+
+ return usb_control_msg(ucr->pusb_dev,
+ usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register);
+
+int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
+{
+ u16 value;
+
+ if (!data)
+ return -EINVAL;
+ *data = 0;
+
+ addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT;
+ value = swab16(addr);
+
+ return usb_control_msg(ucr->pusb_dev,
+ usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, data, 1, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register);
+
+void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr,
+ u8 mask, u8 data)
+{
+ int i;
+
+ if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) {
+ i = CMD_OFFSET + ucr->cmd_idx * 4;
+
+ ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) |
+ (u8)((reg_addr >> 8) & 0x3F);
+ ucr->cmd_buf[i++] = (u8)reg_addr;
+ ucr->cmd_buf[i++] = mask;
+ ucr->cmd_buf[i++] = data;
+
+ ucr->cmd_idx++;
+ }
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd);
+
+int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout)
+{
+ int ret;
+
+ ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8);
+ ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx);
+ ucr->cmd_buf[STAGE_FLAG] = flag;
+
+ ret = rtsx_usb_transfer_data(ucr,
+ usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT),
+ ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET,
+ 0, NULL, timeout);
+ if (ret) {
+ rtsx_usb_clear_fsm_err(ucr);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd);
+
+int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout)
+{
+ if (rsp_len <= 0)
+ return -EINVAL;
+
+ rsp_len = ALIGN(rsp_len, 4);
+
+ return rtsx_usb_transfer_data(ucr,
+ usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN),
+ ucr->rsp_buf, rsp_len, 0, NULL, timeout);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp);
+
+static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status)
+{
+ int ret;
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00);
+ ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_get_rsp(ucr, 2, 100);
+ if (ret)
+ return ret;
+
+ *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) |
+ ((ucr->rsp_buf[1] & 0x03) << 4);
+
+ return 0;
+}
+
+int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status)
+{
+ int ret;
+
+ if (!status)
+ return -EINVAL;
+
+ if (polling_pipe == 0)
+ ret = usb_control_msg(ucr->pusb_dev,
+ usb_rcvctrlpipe(ucr->pusb_dev, 0),
+ RTSX_USB_REQ_POLL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, status, 2, 100);
+ else
+ ret = rtsx_usb_get_status_with_bulk(ucr, status);
+
+ /* usb_control_msg may return positive when success */
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status);
+
+static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val)
+{
+ dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n",
+ val, addr);
+
+ rtsx_usb_init_cmd(ucr);
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL,
+ 0xFF, (addr >> 4) & 0x0F);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01);
+
+ return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data)
+{
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data);
+ return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_write_register);
+
+int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
+{
+ int ret;
+
+ if (data != NULL)
+ *data = 0;
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0);
+ ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+ if (ret)
+ return ret;
+
+ ret = rtsx_usb_get_rsp(ucr, 1, 100);
+ if (ret)
+ return ret;
+
+ if (data != NULL)
+ *data = ucr->rsp_buf[0];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_read_register);
+
+static inline u8 double_ssc_depth(u8 depth)
+{
+ return (depth > 1) ? (depth - 1) : depth;
+}
+
+static u8 revise_ssc_depth(u8 ssc_depth, u8 div)
+{
+ if (div > CLK_DIV_1) {
+ if (ssc_depth > div - 1)
+ ssc_depth -= (div - 1);
+ else
+ ssc_depth = SSC_DEPTH_2M;
+ }
+
+ return ssc_depth;
+}
+
+int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock,
+ u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
+{
+ int ret;
+ u8 n, clk_divider, mcu_cnt, div;
+
+ if (!card_clock) {
+ ucr->cur_clk = 0;
+ return 0;
+ }
+
+ if (initial_mode) {
+ /* We use 250k(around) here, in initial stage */
+ clk_divider = SD_CLK_DIVIDE_128;
+ card_clock = 30000000;
+ } else {
+ clk_divider = SD_CLK_DIVIDE_0;
+ }
+
+ ret = rtsx_usb_write_register(ucr, SD_CFG1,
+ SD_CLK_DIVIDE_MASK, clk_divider);
+ if (ret < 0)
+ return ret;
+
+ card_clock /= 1000000;
+ dev_dbg(&ucr->pusb_intf->dev,
+ "Switch card clock to %dMHz\n", card_clock);
+
+ if (!initial_mode && double_clk)
+ card_clock *= 2;
+ dev_dbg(&ucr->pusb_intf->dev,
+ "Internal SSC clock: %dMHz (cur_clk = %d)\n",
+ card_clock, ucr->cur_clk);
+
+ if (card_clock == ucr->cur_clk)
+ return 0;
+
+ /* Converting clock value into internal settings: n and div */
+ n = card_clock - 2;
+ if ((card_clock <= 2) || (n > MAX_DIV_N))
+ return -EINVAL;
+
+ mcu_cnt = 60/card_clock + 3;
+ if (mcu_cnt > 15)
+ mcu_cnt = 15;
+
+ /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */
+
+ div = CLK_DIV_1;
+ while (n < MIN_DIV_N && div < CLK_DIV_4) {
+ n = (n + 2) * 2 - 2;
+ div++;
+ }
+ dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div);
+
+ if (double_clk)
+ ssc_depth = double_ssc_depth(ssc_depth);
+
+ ssc_depth = revise_ssc_depth(ssc_depth, div);
+ dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth);
+
+ rtsx_usb_init_cmd(ucr);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV,
+ 0x3F, (div << 4) | mcu_cnt);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2,
+ SSC_DEPTH_MASK, ssc_depth);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+ if (vpclk) {
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ }
+
+ ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000);
+ if (ret < 0)
+ return ret;
+
+ ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff,
+ SSC_RSTB | SSC_8X_EN | SSC_SEL_4M);
+ if (ret < 0)
+ return ret;
+
+ /* Wait SSC clock stable */
+ usleep_range(100, 1000);
+
+ ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0);
+ if (ret < 0)
+ return ret;
+
+ ucr->cur_clk = card_clock;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock);
+
+int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card)
+{
+ int ret;
+ u16 val;
+ u16 cd_mask[] = {
+ [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD),
+ [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD)
+ };
+
+ ret = rtsx_usb_get_card_status(ucr, &val);
+ /*
+ * If get status fails, return 0 (ok) for the exclusive check
+ * and let the flow fail at somewhere else.
+ */
+ if (ret)
+ return 0;
+
+ if (val & cd_mask[card])
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check);
+
+static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr)
+{
+ int ret;
+ u8 val;
+
+ rtsx_usb_init_cmd(ucr);
+
+ if (CHECK_PKG(ucr, LQFP48)) {
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+ LDO3318_PWR_MASK, LDO_SUSPEND);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+ FORCE_LDO_POWERB, FORCE_LDO_POWERB);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1,
+ 0x30, 0x10);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5,
+ 0x03, 0x01);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6,
+ 0x0C, 0x04);
+ }
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+ SD30_DRIVE_MASK, DRIVER_TYPE_D);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0);
+
+ if (ucr->is_rts5179)
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+ CARD_PULL_CTL5, 0x03, 0x01);
+
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL,
+ EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND,
+ XD_INT | MS_INT | SD_INT,
+ XD_INT | MS_INT | SD_INT);
+
+ ret = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+ if (ret)
+ return ret;
+
+ /* config non-crystal mode */
+ rtsx_usb_read_register(ucr, CFG_MODE, &val);
+ if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) {
+ ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtsx_usb_init_chip(struct rtsx_ucr *ucr)
+{
+ int ret;
+ u8 val;
+
+ rtsx_usb_clear_fsm_err(ucr);
+
+ /* power on SSC */
+ ret = rtsx_usb_write_register(ucr,
+ FPDCTL, SSC_POWER_MASK, SSC_POWER_ON);
+ if (ret)
+ return ret;
+
+ usleep_range(100, 1000);
+ ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00);
+ if (ret)
+ return ret;
+
+ /* determine IC version */
+ ret = rtsx_usb_read_register(ucr, HW_VERSION, &val);
+ if (ret)
+ return ret;
+
+ ucr->ic_version = val & HW_VER_MASK;
+
+ /* determine package */
+ ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val);
+ if (ret)
+ return ret;
+
+ if (val & CARD_SHARE_LQFP_SEL) {
+ ucr->package = LQFP48;
+ dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n");
+ } else {
+ ucr->package = QFN24;
+ dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n");
+ }
+
+ /* determine IC variations */
+ rtsx_usb_read_register(ucr, CFG_MODE_1, &val);
+ if (val & RTS5179) {
+ ucr->is_rts5179 = true;
+ dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n");
+ } else {
+ ucr->is_rts5179 = false;
+ }
+
+ return rtsx_usb_reset_chip(ucr);
+}
+
+static int rtsx_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+ struct rtsx_ucr *ucr;
+ int ret;
+
+ dev_dbg(&intf->dev,
+ ": Realtek USB Card Reader found at bus %03d address %03d\n",
+ usb_dev->bus->busnum, usb_dev->devnum);
+
+ ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL);
+ if (!ucr)
+ return -ENOMEM;
+
+ ucr->pusb_dev = usb_dev;
+
+ ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE,
+ GFP_KERNEL, &ucr->iobuf_dma);
+ if (!ucr->iobuf)
+ return -ENOMEM;
+
+ usb_set_intfdata(intf, ucr);
+
+ ucr->vendor_id = id->idVendor;
+ ucr->product_id = id->idProduct;
+ ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf;
+
+ mutex_init(&ucr->dev_mutex);
+
+ ucr->pusb_intf = intf;
+
+ /* initialize */
+ ret = rtsx_usb_init_chip(ucr);
+ if (ret)
+ goto out_init_fail;
+
+ ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells,
+ ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
+ if (ret)
+ goto out_init_fail;
+
+ /* initialize USB SG transfer timer */
+ init_timer(&ucr->sg_timer);
+ setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
+#ifdef CONFIG_PM
+ intf->needs_remote_wakeup = 1;
+ usb_enable_autosuspend(usb_dev);
+#endif
+
+ return 0;
+
+out_init_fail:
+ usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf,
+ ucr->iobuf_dma);
+ return ret;
+}
+
+static void rtsx_usb_disconnect(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "%s called\n", __func__);
+
+ mfd_remove_devices(&intf->dev);
+
+ usb_set_intfdata(ucr->pusb_intf, NULL);
+ usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf,
+ ucr->iobuf_dma);
+}
+
+#ifdef CONFIG_PM
+static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct rtsx_ucr *ucr =
+ (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n",
+ __func__, message.event);
+
+ mutex_lock(&ucr->dev_mutex);
+ rtsx_usb_turn_off_led(ucr);
+ mutex_unlock(&ucr->dev_mutex);
+ return 0;
+}
+
+static int rtsx_usb_resume(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static int rtsx_usb_reset_resume(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr =
+ (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ rtsx_usb_reset_chip(ucr);
+ return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define rtsx_usb_suspend NULL
+#define rtsx_usb_resume NULL
+#define rtsx_usb_reset_resume NULL
+
+#endif /* CONFIG_PM */
+
+
+static int rtsx_usb_pre_reset(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ mutex_lock(&ucr->dev_mutex);
+ return 0;
+}
+
+static int rtsx_usb_post_reset(struct usb_interface *intf)
+{
+ struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf);
+
+ mutex_unlock(&ucr->dev_mutex);
+ return 0;
+}
+
+static struct usb_device_id rtsx_usb_usb_ids[] = {
+ { USB_DEVICE(0x0BDA, 0x0129) },
+ { USB_DEVICE(0x0BDA, 0x0139) },
+ { USB_DEVICE(0x0BDA, 0x0140) },
+ { }
+};
+
+static struct usb_driver rtsx_usb_driver = {
+ .name = "rtsx_usb",
+ .probe = rtsx_usb_probe,
+ .disconnect = rtsx_usb_disconnect,
+ .suspend = rtsx_usb_suspend,
+ .resume = rtsx_usb_resume,
+ .reset_resume = rtsx_usb_reset_resume,
+ .pre_reset = rtsx_usb_pre_reset,
+ .post_reset = rtsx_usb_post_reset,
+ .id_table = rtsx_usb_usb_ids,
+ .supports_autosuspend = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(rtsx_usb_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
+MODULE_DESCRIPTION("Realtek USB Card Reader Driver");
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index 281a82747275..1cf27521fff4 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -60,6 +60,7 @@ static const struct mfd_cell s5m8767_devs[] = {
.name = "s5m-rtc",
}, {
.name = "s5m8767-clk",
+ .of_compatible = "samsung,s5m8767-clk",
}
};
@@ -68,6 +69,7 @@ static const struct mfd_cell s2mps11_devs[] = {
.name = "s2mps11-pmic",
}, {
.name = "s2mps11-clk",
+ .of_compatible = "samsung,s2mps11-clk",
}
};
@@ -78,6 +80,7 @@ static const struct mfd_cell s2mps14_devs[] = {
.name = "s2mps14-rtc",
}, {
.name = "s2mps14-clk",
+ .of_compatible = "samsung,s2mps14-clk",
}
};
@@ -295,6 +298,13 @@ static int sec_pmic_probe(struct i2c_client *i2c,
switch (sec_pmic->device_type) {
case S2MPA01:
regmap = &s2mpa01_regmap_config;
+ /*
+ * The rtc-s5m driver does not support S2MPA01 and there
+ * is no mfd_cell for S2MPA01 RTC device.
+ * However we must pass something to devm_regmap_init_i2c()
+ * so use S5M-like regmap config even though it wouldn't work.
+ */
+ regmap_rtc = &s5m_rtc_regmap_config;
break;
case S2MPS11X:
regmap = &s2mps11_regmap_config;
@@ -344,7 +354,7 @@ static int sec_pmic_probe(struct i2c_client *i2c,
ret = PTR_ERR(sec_pmic->regmap_rtc);
dev_err(&i2c->dev, "Failed to allocate RTC register map: %d\n",
ret);
- return ret;
+ goto err_regmap_rtc;
}
if (pdata && pdata->cfg_pmic_irq)
@@ -385,14 +395,15 @@ static int sec_pmic_probe(struct i2c_client *i2c,
}
if (ret)
- goto err;
+ goto err_mfd;
device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);
return ret;
-err:
+err_mfd:
sec_irq_exit(sec_pmic);
+err_regmap_rtc:
i2c_unregister_device(sec_pmic->rtc);
return ret;
}
diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c
index 24ae3d8421c5..90112d4cc905 100644
--- a/drivers/mfd/smsc-ece1099.c
+++ b/drivers/mfd/smsc-ece1099.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 42ccd0544513..4a91f6771fb8 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -706,7 +706,7 @@ static int stmpe1801_reset(struct stmpe *stmpe)
if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET))
return 0;
usleep_range(100, 200);
- };
+ }
return -EIO;
}
diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c
index 1243d5c6a448..7ceb3df09e25 100644
--- a/drivers/mfd/stw481x.c
+++ b/drivers/mfd/stw481x.c
@@ -167,7 +167,7 @@ static struct mfd_cell stw481x_cells[] = {
},
};
-const struct regmap_config stw481x_regmap_config = {
+static const struct regmap_config stw481x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
@@ -186,6 +186,12 @@ static int stw481x_probe(struct i2c_client *client,
i2c_set_clientdata(client, stw481x);
stw481x->client = client;
stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config);
+ if (IS_ERR(stw481x->map)) {
+ ret = PTR_ERR(stw481x->map);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
ret = stw481x_startup(stw481x);
if (ret) {
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 71841f9181bd..dbea55de4397 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -69,13 +69,6 @@ EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
static int syscon_match_pdevname(struct device *dev, void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
- const struct platform_device_id *id = platform_get_device_id(pdev);
-
- if (id)
- if (!strcmp(id->name, (const char *)data))
- return 1;
-
return !strcmp(dev_name(dev), (const char *)data);
}
@@ -152,7 +145,7 @@ static int syscon_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, syscon);
- dev_info(dev, "regmap %pR registered\n", res);
+ dev_dbg(dev, "regmap %pR registered\n", res);
return 0;
}
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 2cf636c267d9..bd83accc0f6d 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -13,8 +13,10 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tc3589x.h>
+#include <linux/err.h>
/**
* enum tc3589x_version - indicates the TC3589x version
@@ -160,7 +162,7 @@ static const struct mfd_cell tc3589x_dev_gpio[] = {
.name = "tc3589x-gpio",
.num_resources = ARRAY_SIZE(gpio_resources),
.resources = &gpio_resources[0],
- .of_compatible = "tc3589x-gpio",
+ .of_compatible = "toshiba,tc3589x-gpio",
},
};
@@ -169,7 +171,7 @@ static const struct mfd_cell tc3589x_dev_keypad[] = {
.name = "tc3589x-keypad",
.num_resources = ARRAY_SIZE(keypad_resources),
.resources = &keypad_resources[0],
- .of_compatible = "tc3589x-keypad",
+ .of_compatible = "toshiba,tc3589x-keypad",
},
};
@@ -318,45 +320,74 @@ static int tc3589x_device_init(struct tc3589x *tc3589x)
return ret;
}
-static int tc3589x_of_probe(struct device_node *np,
- struct tc3589x_platform_data *pdata)
+#ifdef CONFIG_OF
+static const struct of_device_id tc3589x_match[] = {
+ /* Legacy compatible string */
+ { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN },
+ { .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 },
+ { .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 },
+ { .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 },
+ { .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 },
+ { .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 },
+ { .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, tc3589x_match);
+
+static struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
{
+ struct device_node *np = dev->of_node;
+ struct tc3589x_platform_data *pdata;
struct device_node *child;
+ const struct of_device_id *of_id;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_id = of_match_device(tc3589x_match, dev);
+ if (!of_id)
+ return ERR_PTR(-ENODEV);
+ *version = (enum tc3589x_version) of_id->data;
for_each_child_of_node(np, child) {
- if (!strcmp(child->name, "tc3589x_gpio")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))
pdata->block |= TC3589x_BLOCK_GPIO;
- }
- if (!strcmp(child->name, "tc3589x_keypad")) {
+ if (of_device_is_compatible(child, "toshiba,tc3589x-keypad"))
pdata->block |= TC3589x_BLOCK_KEYPAD;
- }
}
- return 0;
+ return pdata;
}
+#else
+static inline struct tc3589x_platform_data *
+tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
+{
+ dev_err(dev, "no device tree support\n");
+ return ERR_PTR(-ENODEV);
+}
+#endif
static int tc3589x_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node;
+ struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct tc3589x *tc3589x;
+ enum tc3589x_version version;
int ret;
if (!pdata) {
- if (np) {
- pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = tc3589x_of_probe(np, pdata);
- if (ret)
- return ret;
- }
- else {
+ pdata = tc3589x_of_probe(&i2c->dev, &version);
+ if (IS_ERR(pdata)) {
dev_err(&i2c->dev, "No platform data or DT found\n");
- return -EINVAL;
+ return PTR_ERR(pdata);
}
+ } else {
+ /* When not probing from device tree we have this ID */
+ version = id->driver_data;
}
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
@@ -375,7 +406,7 @@ static int tc3589x_probe(struct i2c_client *i2c,
tc3589x->pdata = pdata;
tc3589x->irq_base = pdata->irq_base;
- switch (id->driver_data) {
+ switch (version) {
case TC3589X_TC35893:
case TC3589X_TC35895:
case TC3589X_TC35896:
@@ -471,9 +502,12 @@ static const struct i2c_device_id tc3589x_id[] = {
MODULE_DEVICE_TABLE(i2c, tc3589x_id);
static struct i2c_driver tc3589x_driver = {
- .driver.name = "tc3589x",
- .driver.owner = THIS_MODULE,
- .driver.pm = &tc3589x_dev_pm_ops,
+ .driver = {
+ .name = "tc3589x",
+ .owner = THIS_MODULE,
+ .pm = &tc3589x_dev_pm_ops,
+ .of_match_table = of_match_ptr(tc3589x_match),
+ },
.probe = tc3589x_probe,
.remove = tc3589x_remove,
.id_table = tc3589x_id,
diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c
deleted file mode 100644
index a5424579679c..000000000000
--- a/drivers/mfd/ti-ssp.c
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs
- *
- * Copyright (C) 2010 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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 <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/sched.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/ti_ssp.h>
-
-/* Register Offsets */
-#define REG_REV 0x00
-#define REG_IOSEL_1 0x04
-#define REG_IOSEL_2 0x08
-#define REG_PREDIV 0x0c
-#define REG_INTR_ST 0x10
-#define REG_INTR_EN 0x14
-#define REG_TEST_CTRL 0x18
-
-/* Per port registers */
-#define PORT_CFG_2 0x00
-#define PORT_ADDR 0x04
-#define PORT_DATA 0x08
-#define PORT_CFG_1 0x0c
-#define PORT_STATE 0x10
-
-#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT)
-#define SSP_PORT_CLKRATE_MASK 0x0f
-
-#define SSP_SEQRAM_WR_EN BIT(4)
-#define SSP_SEQRAM_RD_EN BIT(5)
-#define SSP_START BIT(15)
-#define SSP_BUSY BIT(10)
-#define SSP_PORT_ASL BIT(7)
-#define SSP_PORT_CFO1 BIT(6)
-
-#define SSP_PORT_SEQRAM_SIZE 32
-
-static const int ssp_port_base[] = {0x040, 0x080};
-static const int ssp_port_seqram[] = {0x100, 0x180};
-
-struct ti_ssp {
- struct resource *res;
- struct device *dev;
- void __iomem *regs;
- spinlock_t lock;
- struct clk *clk;
- int irq;
- wait_queue_head_t wqh;
-
- /*
- * Some of the iosel2 register bits always read-back as 0, we need to
- * remember these values so that we don't clobber previously set
- * values.
- */
- u32 iosel2;
-};
-
-static inline struct ti_ssp *dev_to_ssp(struct device *dev)
-{
- return dev_get_drvdata(dev->parent);
-}
-
-static inline int dev_to_port(struct device *dev)
-{
- return to_platform_device(dev)->id;
-}
-
-/* Register Access Helpers, rmw() functions need to run locked */
-static inline u32 ssp_read(struct ti_ssp *ssp, int reg)
-{
- return __raw_readl(ssp->regs + reg);
-}
-
-static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val)
-{
- __raw_writel(val, ssp->regs + reg);
-}
-
-static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits)
-{
- ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits);
-}
-
-static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg)
-{
- return ssp_read(ssp, ssp_port_base[port] + reg);
-}
-
-static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg,
- u32 val)
-{
- ssp_write(ssp, ssp_port_base[port] + reg, val);
-}
-
-static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg,
- u32 mask, u32 bits)
-{
- ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits);
-}
-
-static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg,
- u32 bits)
-{
- ssp_port_rmw(ssp, port, reg, bits, 0);
-}
-
-static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg,
- u32 bits)
-{
- ssp_port_rmw(ssp, port, reg, 0, bits);
-}
-
-/* Called to setup port clock mode, caller must hold ssp->lock */
-static int __set_mode(struct ti_ssp *ssp, int port, int mode)
-{
- mode &= SSP_PORT_CONFIG_MASK;
- ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode);
-
- return 0;
-}
-
-int ti_ssp_set_mode(struct device *dev, int mode)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int ret;
-
- spin_lock(&ssp->lock);
- ret = __set_mode(ssp, port, mode);
- spin_unlock(&ssp->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(ti_ssp_set_mode);
-
-/* Called to setup iosel2, caller must hold ssp->lock */
-static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val)
-{
- ssp->iosel2 = (ssp->iosel2 & ~mask) | val;
- ssp_write(ssp, REG_IOSEL_2, ssp->iosel2);
-}
-
-/* Called to setup port iosel, caller must hold ssp->lock */
-static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel)
-{
- unsigned val, shift = port ? 16 : 0;
-
- /* IOSEL1 gets the least significant 16 bits */
- val = ssp_read(ssp, REG_IOSEL_1);
- val &= 0xffff << (port ? 0 : 16);
- val |= (iosel & 0xffff) << (port ? 16 : 0);
- ssp_write(ssp, REG_IOSEL_1, val);
-
- /* IOSEL2 gets the most significant 16 bits */
- val = (iosel >> 16) & 0x7;
- __set_iosel2(ssp, 0x7 << shift, val << shift);
-}
-
-int ti_ssp_set_iosel(struct device *dev, u32 iosel)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
-
- spin_lock(&ssp->lock);
- __set_iosel(ssp, port, iosel);
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_set_iosel);
-
-int ti_ssp_load(struct device *dev, int offs, u32* prog, int len)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int i;
-
- if (len > SSP_PORT_SEQRAM_SIZE)
- return -ENOSPC;
-
- spin_lock(&ssp->lock);
-
- /* Enable SeqRAM access */
- ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
-
- /* Copy code */
- for (i = 0; i < len; i++) {
- __raw_writel(prog[i], ssp->regs + offs + 4*i +
- ssp_port_seqram[port]);
- }
-
- /* Disable SeqRAM access */
- ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
-
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_load);
-
-int ti_ssp_raw_read(struct device *dev)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int shift = port ? 27 : 11;
-
- return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf;
-}
-EXPORT_SYMBOL(ti_ssp_raw_read);
-
-int ti_ssp_raw_write(struct device *dev, u32 val)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev), shift;
-
- spin_lock(&ssp->lock);
-
- shift = port ? 22 : 6;
- val &= 0xf;
- __set_iosel2(ssp, 0xf << shift, val << shift);
-
- spin_unlock(&ssp->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(ti_ssp_raw_write);
-
-static inline int __xfer_done(struct ti_ssp *ssp, int port)
-{
- return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY);
-}
-
-int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output)
-{
- struct ti_ssp *ssp = dev_to_ssp(dev);
- int port = dev_to_port(dev);
- int ret;
-
- if (pc & ~(0x3f))
- return -EINVAL;
-
- /* Grab ssp->lock to serialize rmw on ssp registers */
- spin_lock(&ssp->lock);
-
- ssp_port_write(ssp, port, PORT_ADDR, input >> 16);
- ssp_port_write(ssp, port, PORT_DATA, input & 0xffff);
- ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc);
-
- /* grab wait queue head lock to avoid race with the isr */
- spin_lock_irq(&ssp->wqh.lock);
-
- /* kick off sequence execution in hardware */
- ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START);
-
- /* drop ssp lock; no register writes beyond this */
- spin_unlock(&ssp->lock);
-
- ret = wait_event_interruptible_locked_irq(ssp->wqh,
- __xfer_done(ssp, port));
- spin_unlock_irq(&ssp->wqh.lock);
-
- if (ret < 0)
- return ret;
-
- if (output) {
- *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) |
- (ssp_port_read(ssp, port, PORT_DATA) & 0xffff);
- }
-
- ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */
-
- return ret;
-}
-EXPORT_SYMBOL(ti_ssp_run);
-
-static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data)
-{
- struct ti_ssp *ssp = dev_data;
-
- spin_lock(&ssp->wqh.lock);
-
- ssp_write(ssp, REG_INTR_ST, 0x3);
- wake_up_locked(&ssp->wqh);
-
- spin_unlock(&ssp->wqh.lock);
-
- return IRQ_HANDLED;
-}
-
-static int ti_ssp_probe(struct platform_device *pdev)
-{
- static struct ti_ssp *ssp;
- const struct ti_ssp_data *pdata = dev_get_platdata(&pdev->dev);
- int error = 0, prediv = 0xff, id;
- unsigned long sysclk;
- struct device *dev = &pdev->dev;
- struct mfd_cell cells[2];
-
- ssp = kzalloc(sizeof(*ssp), GFP_KERNEL);
- if (!ssp) {
- dev_err(dev, "cannot allocate device info\n");
- return -ENOMEM;
- }
-
- ssp->dev = dev;
- dev_set_drvdata(dev, ssp);
-
- ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!ssp->res) {
- error = -ENODEV;
- dev_err(dev, "cannot determine register area\n");
- goto error_res;
- }
-
- if (!request_mem_region(ssp->res->start, resource_size(ssp->res),
- pdev->name)) {
- error = -ENOMEM;
- dev_err(dev, "cannot claim register memory\n");
- goto error_res;
- }
-
- ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res));
- if (!ssp->regs) {
- error = -ENOMEM;
- dev_err(dev, "cannot map register memory\n");
- goto error_map;
- }
-
- ssp->clk = clk_get(dev, NULL);
- if (IS_ERR(ssp->clk)) {
- error = PTR_ERR(ssp->clk);
- dev_err(dev, "cannot claim device clock\n");
- goto error_clk;
- }
-
- ssp->irq = platform_get_irq(pdev, 0);
- if (ssp->irq < 0) {
- error = -ENODEV;
- dev_err(dev, "unknown irq\n");
- goto error_irq;
- }
-
- error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0,
- dev_name(dev), ssp);
- if (error < 0) {
- dev_err(dev, "cannot acquire irq\n");
- goto error_irq;
- }
-
- spin_lock_init(&ssp->lock);
- init_waitqueue_head(&ssp->wqh);
-
- /* Power on and initialize SSP */
- error = clk_enable(ssp->clk);
- if (error) {
- dev_err(dev, "cannot enable device clock\n");
- goto error_enable;
- }
-
- /* Reset registers to a sensible known state */
- ssp_write(ssp, REG_IOSEL_1, 0);
- ssp_write(ssp, REG_IOSEL_2, 0);
- ssp_write(ssp, REG_INTR_EN, 0x3);
- ssp_write(ssp, REG_INTR_ST, 0x3);
- ssp_write(ssp, REG_TEST_CTRL, 0);
- ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL);
- ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL);
- ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1);
- ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1);
-
- sysclk = clk_get_rate(ssp->clk);
- if (pdata && pdata->out_clock)
- prediv = (sysclk / pdata->out_clock) - 1;
- prediv = clamp(prediv, 0, 0xff);
- ssp_rmw(ssp, REG_PREDIV, 0xff, prediv);
-
- memset(cells, 0, sizeof(cells));
- for (id = 0; id < 2; id++) {
- const struct ti_ssp_dev_data *data = &pdata->dev_data[id];
-
- cells[id].id = id;
- cells[id].name = data->dev_name;
- cells[id].platform_data = data->pdata;
- }
-
- error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL);
- if (error < 0) {
- dev_err(dev, "cannot add mfd cells\n");
- goto error_enable;
- }
-
- return 0;
-
-error_enable:
- free_irq(ssp->irq, ssp);
-error_irq:
- clk_put(ssp->clk);
-error_clk:
- iounmap(ssp->regs);
-error_map:
- release_mem_region(ssp->res->start, resource_size(ssp->res));
-error_res:
- kfree(ssp);
- return error;
-}
-
-static int ti_ssp_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct ti_ssp *ssp = dev_get_drvdata(dev);
-
- mfd_remove_devices(dev);
- clk_disable(ssp->clk);
- free_irq(ssp->irq, ssp);
- clk_put(ssp->clk);
- iounmap(ssp->regs);
- release_mem_region(ssp->res->start, resource_size(ssp->res));
- kfree(ssp);
- return 0;
-}
-
-static struct platform_driver ti_ssp_driver = {
- .probe = ti_ssp_probe,
- .remove = ti_ssp_remove,
- .driver = {
- .name = "ti-ssp",
- .owner = THIS_MODULE,
- }
-};
-
-module_platform_driver(ti_ssp_driver);
-
-MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver");
-MODULE_AUTHOR("Cyril Chemparathy");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ti-ssp");
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index d4e860413bb5..dd4bf5816221 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -14,7 +14,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -184,12 +183,6 @@ static int ti_tscadc_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no memory resource defined.\n");
- return -EINVAL;
- }
-
/* Allocate memory for device */
tscadc = devm_kzalloc(&pdev->dev,
sizeof(struct ti_tscadc_dev), GFP_KERNEL);
@@ -206,19 +199,10 @@ static int ti_tscadc_probe(struct platform_device *pdev)
} else
tscadc->irq = err;
- res = devm_request_mem_region(&pdev->dev,
- res->start, resource_size(res), pdev->name);
- if (!res) {
- dev_err(&pdev->dev, "failed to reserve registers.\n");
- return -EBUSY;
- }
-
- tscadc->tscadc_base = devm_ioremap(&pdev->dev,
- res->start, resource_size(res));
- if (!tscadc->tscadc_base) {
- dev_err(&pdev->dev, "failed to map registers.\n");
- return -ENOMEM;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tscadc->tscadc_base))
+ return PTR_ERR(tscadc->tscadc_base);
tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev,
tscadc->tscadc_base, &tscadc_regmap_config);
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 2bc5cfb85204..6ce36d6970a4 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -715,7 +715,7 @@ static int timb_probe(struct pci_dev *dev,
for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
msix_entries[i].entry = i;
- err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
+ err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);
if (err) {
dev_err(&dev->dev,
"MSI-X init failed: %d, expected entries: %d\n",
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
new file mode 100644
index 000000000000..a74bfb59f18f
--- /dev/null
+++ b/drivers/mfd/tps65218.c
@@ -0,0 +1,282 @@
+/*
+ * Driver for TPS65218 Integrated power management chipsets
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65218.h>
+
+#define TPS65218_PASSWORD_REGS_UNLOCK 0x7D
+
+/**
+ * tps65218_reg_read: Read a single tps65218 register.
+ *
+ * @tps: Device to read from.
+ * @reg: Register to read.
+ * @val: Contians the value
+ */
+int tps65218_reg_read(struct tps65218 *tps, unsigned int reg,
+ unsigned int *val)
+{
+ return regmap_read(tps->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65218_reg_read);
+
+/**
+ * tps65218_reg_write: Write a single tps65218 register.
+ *
+ * @tps65218: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65218_reg_write(struct tps65218 *tps, unsigned int reg,
+ unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int xor_reg_val;
+
+ switch (level) {
+ case TPS65218_PROTECT_NONE:
+ return regmap_write(tps->regmap, reg, val);
+ case TPS65218_PROTECT_L1:
+ xor_reg_val = reg ^ TPS65218_PASSWORD_REGS_UNLOCK;
+ ret = regmap_write(tps->regmap, TPS65218_REG_PASSWORD,
+ xor_reg_val);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(tps->regmap, reg, val);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(tps65218_reg_write);
+
+/**
+ * tps65218_update_bits: Modify bits w.r.t mask, val and level.
+ *
+ * @tps65218: Device to write to.
+ * @reg: Register to read-write to.
+ * @mask: Mask.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+static int tps65218_update_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int data;
+
+ ret = tps65218_reg_read(tps, reg, &data);
+ if (ret) {
+ dev_err(tps->dev, "Read from reg 0x%x failed\n", reg);
+ return ret;
+ }
+
+ data &= ~mask;
+ data |= val & mask;
+
+ mutex_lock(&tps->tps_lock);
+ ret = tps65218_reg_write(tps, reg, data, level);
+ if (ret)
+ dev_err(tps->dev, "Write for reg 0x%x failed\n", reg);
+ mutex_unlock(&tps->tps_lock);
+
+ return ret;
+}
+
+int tps65218_set_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ return tps65218_update_bits(tps, reg, mask, val, level);
+}
+EXPORT_SYMBOL_GPL(tps65218_set_bits);
+
+int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg,
+ unsigned int mask, unsigned int level)
+{
+ return tps65218_update_bits(tps, reg, mask, 0, level);
+}
+EXPORT_SYMBOL_GPL(tps65218_clear_bits);
+
+static struct regmap_config tps65218_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_irq tps65218_irqs[] = {
+ /* INT1 IRQs */
+ [TPS65218_PRGC_IRQ] = {
+ .mask = TPS65218_INT1_PRGC,
+ },
+ [TPS65218_CC_AQC_IRQ] = {
+ .mask = TPS65218_INT1_CC_AQC,
+ },
+ [TPS65218_HOT_IRQ] = {
+ .mask = TPS65218_INT1_HOT,
+ },
+ [TPS65218_PB_IRQ] = {
+ .mask = TPS65218_INT1_PB,
+ },
+ [TPS65218_AC_IRQ] = {
+ .mask = TPS65218_INT1_AC,
+ },
+ [TPS65218_VPRG_IRQ] = {
+ .mask = TPS65218_INT1_VPRG,
+ },
+ [TPS65218_INVALID1_IRQ] = {
+ },
+ [TPS65218_INVALID2_IRQ] = {
+ },
+ /* INT2 IRQs*/
+ [TPS65218_LS1_I_IRQ] = {
+ .mask = TPS65218_INT2_LS1_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS2_I_IRQ] = {
+ .mask = TPS65218_INT2_LS2_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS3_I_IRQ] = {
+ .mask = TPS65218_INT2_LS3_I,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS1_F_IRQ] = {
+ .mask = TPS65218_INT2_LS1_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS2_F_IRQ] = {
+ .mask = TPS65218_INT2_LS2_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_LS3_F_IRQ] = {
+ .mask = TPS65218_INT2_LS3_F,
+ .reg_offset = 1,
+ },
+ [TPS65218_INVALID3_IRQ] = {
+ },
+ [TPS65218_INVALID4_IRQ] = {
+ },
+};
+
+static struct regmap_irq_chip tps65218_irq_chip = {
+ .name = "tps65218",
+ .irqs = tps65218_irqs,
+ .num_irqs = ARRAY_SIZE(tps65218_irqs),
+
+ .num_regs = 2,
+ .mask_base = TPS65218_REG_INT_MASK1,
+};
+
+static const struct of_device_id of_tps65218_match_table[] = {
+ { .compatible = "ti,tps65218", },
+};
+
+static int tps65218_probe(struct i2c_client *client,
+ const struct i2c_device_id *ids)
+{
+ struct tps65218 *tps;
+ const struct of_device_id *match;
+ int ret;
+
+ match = of_match_device(of_tps65218_match_table, &client->dev);
+ if (!match) {
+ dev_err(&client->dev,
+ "Failed to find matching dt id\n");
+ return -EINVAL;
+ }
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = &client->dev;
+ tps->irq = client->irq;
+ tps->regmap = devm_regmap_init_i2c(client, &tps65218_regmap_config);
+ if (IS_ERR(tps->regmap)) {
+ ret = PTR_ERR(tps->regmap);
+ dev_err(tps->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ mutex_init(&tps->tps_lock);
+
+ ret = regmap_add_irq_chip(tps->regmap, tps->irq,
+ IRQF_ONESHOT, 0, &tps65218_irq_chip,
+ &tps->irq_data);
+ if (ret < 0)
+ return ret;
+
+ ret = of_platform_populate(client->dev.of_node, NULL, NULL,
+ &client->dev);
+ if (ret < 0)
+ goto err_irq;
+
+ return 0;
+
+err_irq:
+ regmap_del_irq_chip(tps->irq, tps->irq_data);
+
+ return ret;
+}
+
+static int tps65218_remove(struct i2c_client *client)
+{
+ struct tps65218 *tps = i2c_get_clientdata(client);
+
+ regmap_del_irq_chip(tps->irq, tps->irq_data);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65218_id_table[] = {
+ { "tps65218", TPS65218 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps65218_id_table);
+
+static struct i2c_driver tps65218_driver = {
+ .driver = {
+ .name = "tps65218",
+ .owner = THIS_MODULE,
+ .of_match_table = of_tps65218_match_table,
+ },
+ .probe = tps65218_probe,
+ .remove = tps65218_remove,
+ .id_table = tps65218_id_table,
+};
+
+module_i2c_driver(tps65218_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("TPS65218 chip family multi-function driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 1f142d76cbbc..460a014ca629 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -255,8 +255,10 @@ static int tps65910_irq_init(struct tps65910 *tps65910, int irq,
ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq,
IRQF_ONESHOT, pdata->irq_base,
tps6591x_irqs_chip, &tps65910->irq_data);
- if (ret < 0)
+ if (ret < 0) {
dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret);
+ tps65910->chip_irq = 0;
+ }
return ret;
}
@@ -509,6 +511,7 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
regmap_irq_get_domain(tps65910->irq_data));
if (ret < 0) {
dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
+ tps65910_irq_exit(tps65910);
return ret;
}
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 27a518e0eec6..1f82d60b1d0f 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c
index d360a83a2738..fbecec7f1e3d 100644
--- a/drivers/mfd/tps65912-irq.c
+++ b/drivers/mfd/tps65912-irq.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index ed718328eff1..e87140bef667 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -282,11 +282,11 @@ static struct reg_default twl4030_49_defaults[] = {
static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case 0:
- case 3:
- case 40:
- case 41:
- case 42:
+ case 0x00:
+ case 0x03:
+ case 0x40:
+ case 0x41:
+ case 0x42:
return false;
default:
return true;
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 9aa6d1efa241..596b1f657e21 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -27,7 +27,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/init.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 18a607e2ca06..a6bb17d908b8 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -31,7 +31,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/init.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c
index 75316fb33448..6e88f25832fb 100644
--- a/drivers/mfd/twl6040.c
+++ b/drivers/mfd/twl6040.c
@@ -661,6 +661,11 @@ static int twl6040_probe(struct i2c_client *client,
init_completion(&twl6040->ready);
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+ if (twl6040->rev < 0) {
+ dev_err(&client->dev, "Failed to read revision register: %d\n",
+ twl6040->rev);
+ goto gpio_err;
+ }
/* ERRATA: Automatic power-up is not possible in ES1.0 */
if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
@@ -703,7 +708,6 @@ static int twl6040_probe(struct i2c_client *client,
}
/* dual-access registers controlled by I2C only */
- twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
regmap_register_patch(twl6040->regmap, twl6040_patch,
ARRAY_SIZE(twl6040_patch));
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index 0313f839e8fa..153d595afaac 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -742,9 +742,7 @@ static int ucb1x00_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops ucb1x00_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
-};
+static SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, ucb1x00_suspend, ucb1x00_resume);
static struct mcp_driver ucb1x00_driver = {
.drv = {
diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c
index 84ce6b9daa3d..d0db89d13e01 100644
--- a/drivers/mfd/vexpress-config.c
+++ b/drivers/mfd/vexpress-config.c
@@ -16,7 +16,6 @@
#include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/export.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -27,7 +26,7 @@
#define VEXPRESS_CONFIG_MAX_BRIDGES 2
-struct vexpress_config_bridge {
+static struct vexpress_config_bridge {
struct device_node *node;
struct vexpress_config_bridge_info *info;
struct list_head transactions;
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 981bef4b7ebc..35281e804e7e 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -168,7 +168,7 @@ static void *vexpress_sysreg_config_func_get(struct device *dev,
struct device_node *node)
{
struct vexpress_sysreg_config_func *config_func;
- u32 site;
+ u32 site = 0;
u32 position = 0;
u32 dcc = 0;
u32 func_device[2];
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
index bffc584e4a43..070f8cfbbd7a 100644
--- a/drivers/mfd/wm5102-tables.c
+++ b/drivers/mfd/wm5102-tables.c
@@ -73,6 +73,7 @@ static const struct reg_default wm5102_revb_patch[] = {
{ 0x171, 0x0000 },
{ 0x35E, 0x000C },
{ 0x2D4, 0x0000 },
+ { 0x4DC, 0x0900 },
{ 0x80, 0x0000 },
};
@@ -1839,6 +1840,23 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
@@ -1894,9 +1912,27 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_AOD_IRQ1:
case ARIZONA_AOD_IRQ2:
case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
index 11632f135e8c..1942b6f231da 100644
--- a/drivers/mfd/wm5110-tables.c
+++ b/drivers/mfd/wm5110-tables.c
@@ -538,7 +538,7 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
- { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x0000029B, 0x0028 }, /* R667 - Headphone Detect 1 */
{ 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
{ 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
@@ -2461,6 +2461,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_STATUS_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_WDMA_OFFSET_1:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_RDMA_OFFSET_1:
+ case ARIZONA_DSP1_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
@@ -2470,6 +2491,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP2_STATUS_1:
case ARIZONA_DSP2_STATUS_2:
case ARIZONA_DSP2_STATUS_3:
+ case ARIZONA_DSP2_STATUS_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_1:
+ case ARIZONA_DSP2_WDMA_BUFFER_2:
+ case ARIZONA_DSP2_WDMA_BUFFER_3:
+ case ARIZONA_DSP2_WDMA_BUFFER_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_5:
+ case ARIZONA_DSP2_WDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_BUFFER_7:
+ case ARIZONA_DSP2_WDMA_BUFFER_8:
+ case ARIZONA_DSP2_RDMA_BUFFER_1:
+ case ARIZONA_DSP2_RDMA_BUFFER_2:
+ case ARIZONA_DSP2_RDMA_BUFFER_3:
+ case ARIZONA_DSP2_RDMA_BUFFER_4:
+ case ARIZONA_DSP2_RDMA_BUFFER_5:
+ case ARIZONA_DSP2_RDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_CONFIG_1:
+ case ARIZONA_DSP2_WDMA_CONFIG_2:
+ case ARIZONA_DSP2_WDMA_OFFSET_1:
+ case ARIZONA_DSP2_RDMA_CONFIG_1:
+ case ARIZONA_DSP2_RDMA_OFFSET_1:
+ case ARIZONA_DSP2_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP2_SCRATCH_0:
case ARIZONA_DSP2_SCRATCH_1:
case ARIZONA_DSP2_SCRATCH_2:
@@ -2479,6 +2521,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP3_STATUS_1:
case ARIZONA_DSP3_STATUS_2:
case ARIZONA_DSP3_STATUS_3:
+ case ARIZONA_DSP3_STATUS_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_1:
+ case ARIZONA_DSP3_WDMA_BUFFER_2:
+ case ARIZONA_DSP3_WDMA_BUFFER_3:
+ case ARIZONA_DSP3_WDMA_BUFFER_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_5:
+ case ARIZONA_DSP3_WDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_BUFFER_7:
+ case ARIZONA_DSP3_WDMA_BUFFER_8:
+ case ARIZONA_DSP3_RDMA_BUFFER_1:
+ case ARIZONA_DSP3_RDMA_BUFFER_2:
+ case ARIZONA_DSP3_RDMA_BUFFER_3:
+ case ARIZONA_DSP3_RDMA_BUFFER_4:
+ case ARIZONA_DSP3_RDMA_BUFFER_5:
+ case ARIZONA_DSP3_RDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_CONFIG_1:
+ case ARIZONA_DSP3_WDMA_CONFIG_2:
+ case ARIZONA_DSP3_WDMA_OFFSET_1:
+ case ARIZONA_DSP3_RDMA_CONFIG_1:
+ case ARIZONA_DSP3_RDMA_OFFSET_1:
+ case ARIZONA_DSP3_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP3_SCRATCH_0:
case ARIZONA_DSP3_SCRATCH_1:
case ARIZONA_DSP3_SCRATCH_2:
@@ -2488,6 +2551,27 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
case ARIZONA_DSP4_STATUS_3:
+ case ARIZONA_DSP4_STATUS_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_1:
+ case ARIZONA_DSP4_WDMA_BUFFER_2:
+ case ARIZONA_DSP4_WDMA_BUFFER_3:
+ case ARIZONA_DSP4_WDMA_BUFFER_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_5:
+ case ARIZONA_DSP4_WDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_BUFFER_7:
+ case ARIZONA_DSP4_WDMA_BUFFER_8:
+ case ARIZONA_DSP4_RDMA_BUFFER_1:
+ case ARIZONA_DSP4_RDMA_BUFFER_2:
+ case ARIZONA_DSP4_RDMA_BUFFER_3:
+ case ARIZONA_DSP4_RDMA_BUFFER_4:
+ case ARIZONA_DSP4_RDMA_BUFFER_5:
+ case ARIZONA_DSP4_RDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_CONFIG_1:
+ case ARIZONA_DSP4_WDMA_CONFIG_2:
+ case ARIZONA_DSP4_WDMA_OFFSET_1:
+ case ARIZONA_DSP4_RDMA_CONFIG_1:
+ case ARIZONA_DSP4_RDMA_OFFSET_1:
+ case ARIZONA_DSP4_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP4_SCRATCH_0:
case ARIZONA_DSP4_SCRATCH_1:
case ARIZONA_DSP4_SCRATCH_2:
@@ -2543,31 +2627,119 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
+ case ARIZONA_DSP1_STATUS_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_1:
+ case ARIZONA_DSP1_WDMA_BUFFER_2:
+ case ARIZONA_DSP1_WDMA_BUFFER_3:
+ case ARIZONA_DSP1_WDMA_BUFFER_4:
+ case ARIZONA_DSP1_WDMA_BUFFER_5:
+ case ARIZONA_DSP1_WDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_BUFFER_7:
+ case ARIZONA_DSP1_WDMA_BUFFER_8:
+ case ARIZONA_DSP1_RDMA_BUFFER_1:
+ case ARIZONA_DSP1_RDMA_BUFFER_2:
+ case ARIZONA_DSP1_RDMA_BUFFER_3:
+ case ARIZONA_DSP1_RDMA_BUFFER_4:
+ case ARIZONA_DSP1_RDMA_BUFFER_5:
+ case ARIZONA_DSP1_RDMA_BUFFER_6:
+ case ARIZONA_DSP1_WDMA_CONFIG_1:
+ case ARIZONA_DSP1_WDMA_CONFIG_2:
+ case ARIZONA_DSP1_WDMA_OFFSET_1:
+ case ARIZONA_DSP1_RDMA_CONFIG_1:
+ case ARIZONA_DSP1_RDMA_OFFSET_1:
+ case ARIZONA_DSP1_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
case ARIZONA_DSP1_SCRATCH_3:
+ case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP2_STATUS_1:
case ARIZONA_DSP2_STATUS_2:
case ARIZONA_DSP2_STATUS_3:
+ case ARIZONA_DSP2_STATUS_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_1:
+ case ARIZONA_DSP2_WDMA_BUFFER_2:
+ case ARIZONA_DSP2_WDMA_BUFFER_3:
+ case ARIZONA_DSP2_WDMA_BUFFER_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_5:
+ case ARIZONA_DSP2_WDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_BUFFER_7:
+ case ARIZONA_DSP2_WDMA_BUFFER_8:
+ case ARIZONA_DSP2_RDMA_BUFFER_1:
+ case ARIZONA_DSP2_RDMA_BUFFER_2:
+ case ARIZONA_DSP2_RDMA_BUFFER_3:
+ case ARIZONA_DSP2_RDMA_BUFFER_4:
+ case ARIZONA_DSP2_RDMA_BUFFER_5:
+ case ARIZONA_DSP2_RDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_CONFIG_1:
+ case ARIZONA_DSP2_WDMA_CONFIG_2:
+ case ARIZONA_DSP2_WDMA_OFFSET_1:
+ case ARIZONA_DSP2_RDMA_CONFIG_1:
+ case ARIZONA_DSP2_RDMA_OFFSET_1:
+ case ARIZONA_DSP2_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP2_SCRATCH_0:
case ARIZONA_DSP2_SCRATCH_1:
case ARIZONA_DSP2_SCRATCH_2:
case ARIZONA_DSP2_SCRATCH_3:
+ case ARIZONA_DSP2_CLOCKING_1:
case ARIZONA_DSP3_STATUS_1:
case ARIZONA_DSP3_STATUS_2:
case ARIZONA_DSP3_STATUS_3:
+ case ARIZONA_DSP3_STATUS_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_1:
+ case ARIZONA_DSP3_WDMA_BUFFER_2:
+ case ARIZONA_DSP3_WDMA_BUFFER_3:
+ case ARIZONA_DSP3_WDMA_BUFFER_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_5:
+ case ARIZONA_DSP3_WDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_BUFFER_7:
+ case ARIZONA_DSP3_WDMA_BUFFER_8:
+ case ARIZONA_DSP3_RDMA_BUFFER_1:
+ case ARIZONA_DSP3_RDMA_BUFFER_2:
+ case ARIZONA_DSP3_RDMA_BUFFER_3:
+ case ARIZONA_DSP3_RDMA_BUFFER_4:
+ case ARIZONA_DSP3_RDMA_BUFFER_5:
+ case ARIZONA_DSP3_RDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_CONFIG_1:
+ case ARIZONA_DSP3_WDMA_CONFIG_2:
+ case ARIZONA_DSP3_WDMA_OFFSET_1:
+ case ARIZONA_DSP3_RDMA_CONFIG_1:
+ case ARIZONA_DSP3_RDMA_OFFSET_1:
+ case ARIZONA_DSP3_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP3_SCRATCH_0:
case ARIZONA_DSP3_SCRATCH_1:
case ARIZONA_DSP3_SCRATCH_2:
case ARIZONA_DSP3_SCRATCH_3:
+ case ARIZONA_DSP3_CLOCKING_1:
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
case ARIZONA_DSP4_STATUS_3:
+ case ARIZONA_DSP4_STATUS_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_1:
+ case ARIZONA_DSP4_WDMA_BUFFER_2:
+ case ARIZONA_DSP4_WDMA_BUFFER_3:
+ case ARIZONA_DSP4_WDMA_BUFFER_4:
+ case ARIZONA_DSP4_WDMA_BUFFER_5:
+ case ARIZONA_DSP4_WDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_BUFFER_7:
+ case ARIZONA_DSP4_WDMA_BUFFER_8:
+ case ARIZONA_DSP4_RDMA_BUFFER_1:
+ case ARIZONA_DSP4_RDMA_BUFFER_2:
+ case ARIZONA_DSP4_RDMA_BUFFER_3:
+ case ARIZONA_DSP4_RDMA_BUFFER_4:
+ case ARIZONA_DSP4_RDMA_BUFFER_5:
+ case ARIZONA_DSP4_RDMA_BUFFER_6:
+ case ARIZONA_DSP4_WDMA_CONFIG_1:
+ case ARIZONA_DSP4_WDMA_CONFIG_2:
+ case ARIZONA_DSP4_WDMA_OFFSET_1:
+ case ARIZONA_DSP4_RDMA_CONFIG_1:
+ case ARIZONA_DSP4_RDMA_OFFSET_1:
+ case ARIZONA_DSP4_EXTERNAL_START_SELECT_1:
case ARIZONA_DSP4_SCRATCH_0:
case ARIZONA_DSP4_SCRATCH_1:
case ARIZONA_DSP4_SCRATCH_2:
case ARIZONA_DSP4_SCRATCH_3:
+ case ARIZONA_DSP4_CLOCKING_1:
return true;
default:
return wm5110_is_adsp_memory(dev, reg);
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 7c1ae24605d9..4ab527f5c53b 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/bug.h>
#include <linux/device.h>
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
index 624ff90501cd..cd01f7962dfd 100644
--- a/drivers/mfd/wm8350-irq.c
+++ b/drivers/mfd/wm8350-irq.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index d66d256551fb..e5eae751aa1b 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -161,31 +161,19 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8400 *wm8400;
- int ret;
wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL);
- if (wm8400 == NULL) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!wm8400)
+ return -ENOMEM;
wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config);
- if (IS_ERR(wm8400->regmap)) {
- ret = PTR_ERR(wm8400->regmap);
- goto err;
- }
+ if (IS_ERR(wm8400->regmap))
+ return PTR_ERR(wm8400->regmap);
wm8400->dev = &i2c->dev;
i2c_set_clientdata(i2c, wm8400);
- ret = wm8400_init(wm8400, dev_get_platdata(&i2c->dev));
- if (ret != 0)
- goto err;
-
- return 0;
-
-err:
- return ret;
+ return wm8400_init(wm8400, dev_get_platdata(&i2c->dev));
}
static int wm8400_i2c_remove(struct i2c_client *i2c)
diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c
index 2bef3f76032a..a3700a56b8ff 100644
--- a/drivers/misc/sgi-gru/grukdump.c
+++ b/drivers/misc/sgi-gru/grukdump.c
@@ -178,10 +178,10 @@ static int gru_dump_context(struct gru_state *gru, int ctxnum,
hdr.cbrcnt = cbrcnt;
hdr.dsrcnt = dsrcnt;
hdr.cch_locked = cch_locked;
- if (!ret && copy_to_user((void __user *)uhdr, &hdr, sizeof(hdr)))
- ret = -EFAULT;
+ if (copy_to_user(uhdr, &hdr, sizeof(hdr)))
+ return -EFAULT;
- return ret ? ret : bytes;
+ return bytes;
}
int gru_dump_chiplet_request(unsigned long arg)
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 168bc72f7a94..84c0e59b792a 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -38,10 +38,11 @@
#define MCI_CPSM_INTERRUPT (1 << 8)
#define MCI_CPSM_PENDING (1 << 9)
#define MCI_CPSM_ENABLE (1 << 10)
-#define MCI_SDIO_SUSP (1 << 11)
-#define MCI_ENCMD_COMPL (1 << 12)
-#define MCI_NIEN (1 << 13)
-#define MCI_CE_ATACMD (1 << 14)
+/* Argument flag extenstions in the ST Micro versions */
+#define MCI_ST_SDIO_SUSP (1 << 11)
+#define MCI_ST_ENCMD_COMPL (1 << 12)
+#define MCI_ST_NIEN (1 << 13)
+#define MCI_ST_CE_ATACMD (1 << 14)
#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 5ebcda39f554..5d49a2129618 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -150,7 +150,7 @@ config MTD_BCM63XX_PARTS
config MTD_BCM47XX_PARTS
tristate "BCM47XX partitioning support"
- depends on BCM47XX
+ depends on BCM47XX || ARCH_BCM_5301X
help
This provides partitions parser for devices based on BCM47xx
boards.
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index de1eb92e42f5..adfa74c1bc45 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -14,7 +14,6 @@
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
-#include <bcm47xx_nvram.h>
/* 10 parts were found on sflash on Netgear WNDR4500 */
#define BCM47XXPART_MAX_PARTS 12
@@ -30,6 +29,7 @@
#define BOARD_DATA_MAGIC2 0xBD0D0BBD
#define CFE_MAGIC 0x43464531 /* 1EFC */
#define FACTORY_MAGIC 0x59544346 /* FCTY */
+#define NVRAM_HEADER 0x48534C46 /* FLSH */
#define POT_MAGIC1 0x54544f50 /* POTT */
#define POT_MAGIC2 0x504f /* OP */
#define ML_MAGIC1 0x39685a42
@@ -91,7 +91,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
if (offset >= 0x2000000)
break;
- if (curr_part > BCM47XXPART_MAX_PARTS) {
+ if (curr_part >= BCM47XXPART_MAX_PARTS) {
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
break;
}
@@ -147,6 +147,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* TRX */
if (buf[0x000 / 4] == TRX_MAGIC) {
+ if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
+ pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
+ break;
+ }
+
trx = (struct trx_header *)buf;
trx_part = curr_part;
@@ -212,7 +217,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* Look for NVRAM at the end of the last block. */
for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
- if (curr_part > BCM47XXPART_MAX_PARTS) {
+ if (curr_part >= BCM47XXPART_MAX_PARTS) {
pr_warn("Reached maximum number of partitions, scanning stopped!\n");
break;
}
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 77514430f1fe..e4ec355704a6 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -21,7 +21,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -69,10 +68,10 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, s
static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
-static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
- struct otp_info *, size_t);
-static int cfi_intelext_get_user_prot_info (struct mtd_info *,
- struct otp_info *, size_t);
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t,
+ size_t *, struct otp_info *);
+static int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t,
+ size_t *, struct otp_info *);
#endif
static int cfi_intelext_suspend (struct mtd_info *);
static void cfi_intelext_resume (struct mtd_info *);
@@ -435,10 +434,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
int i;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+ if (!mtd)
return NULL;
- }
mtd->priv = map;
mtd->type = MTD_NORFLASH;
@@ -564,10 +561,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
- if (!mtd->eraseregions) {
- printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
+ if (!mtd->eraseregions)
goto setup_err;
- }
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
unsigned long ernum, ersize;
@@ -2399,24 +2394,19 @@ static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
NULL, do_otp_lock, 1);
}
-static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
-{
- size_t retlen;
- int ret;
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
- ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
- return ret ? : retlen;
+{
+ return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
+ NULL, 0);
}
-static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
- size_t retlen;
- int ret;
-
- ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
- return ret ? : retlen;
+ return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
+ NULL, 1);
}
#endif
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 89b9d6891532..e21fde9d4d7e 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -24,7 +24,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -507,10 +506,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
int i;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+ if (!mtd)
return NULL;
- }
mtd->priv = map;
mtd->type = MTD_NORFLASH;
@@ -661,10 +658,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
- if (!mtd->eraseregions) {
- printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
+ if (!mtd->eraseregions)
goto setup_err;
- }
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
unsigned long ernum, ersize;
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 096993f9711e..6293855fb5ee 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -22,7 +22,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
@@ -176,7 +175,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
if (!mtd) {
- printk(KERN_ERR "Failed to allocate memory for MTD device\n");
kfree(cfi->cmdset_priv);
return NULL;
}
@@ -189,7 +187,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
if (!mtd->eraseregions) {
- printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
kfree(cfi->cmdset_priv);
kfree(mtd);
return NULL;
diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c
index d25535279404..e8d0164498b0 100644
--- a/drivers/mtd/chips/cfi_probe.c
+++ b/drivers/mtd/chips/cfi_probe.c
@@ -168,10 +168,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
return 0;
cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
- if (!cfi->cfiq) {
- printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
+ if (!cfi->cfiq)
return 0;
- }
memset(cfi->cfiq,0,sizeof(struct cfi_ident));
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index f992418f40a8..08049f6eea60 100644
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -116,10 +116,8 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n
printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr);
extp = kmalloc(size, GFP_KERNEL);
- if (!extp) {
- printk(KERN_ERR "Failed to allocate memory\n");
+ if (!extp)
goto out;
- }
#ifdef CONFIG_MTD_XIP
local_irq_disable();
diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c
index ffb36ba8a6e0..b57ceea21513 100644
--- a/drivers/mtd/chips/gen_probe.c
+++ b/drivers/mtd/chips/gen_probe.c
@@ -114,7 +114,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG);
chip_map = kzalloc(mapsize, GFP_KERNEL);
if (!chip_map) {
- printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
kfree(cfi.cfiq);
return NULL;
}
@@ -139,7 +138,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
if (!retcfi) {
- printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
kfree(cfi.cfiq);
kfree(chip_map);
return NULL;
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 01281382180b..1210bc2923b7 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -210,6 +210,14 @@ config MTD_DOCG3
M-Systems and now Sandisk. The support is very experimental,
and doesn't give access to any write operations.
+config MTD_ST_SPI_FSM
+ tristate "ST Microelectronics SPI FSM Serial Flash Controller"
+ depends on ARM || SH
+ help
+ This provides an MTD device driver for the ST Microelectronics
+ SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
+ for a subset of connected Serial Flash devices.
+
if MTD_DOCG3
config BCH_CONST_M
default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index d83bd73096f6..c68868f60588 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
+obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index d9fd87a4c8dc..66f0405f7e53 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -209,7 +209,6 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)
}
-/* FIXME: ensure that mtd->size % erase_size == 0 */
static struct block2mtd_dev *add_device(char *devname, int erase_size)
{
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
@@ -240,13 +239,18 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
if (IS_ERR(bdev)) {
pr_err("error: cannot open device %s\n", devname);
- goto devinit_err;
+ goto err_free_block2mtd;
}
dev->blkdev = bdev;
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
pr_err("attempting to use an MTD device as a block device\n");
- goto devinit_err;
+ goto err_free_block2mtd;
+ }
+
+ if ((long)dev->blkdev->bd_inode->i_size % erase_size) {
+ pr_err("erasesize must be a divisor of device size\n");
+ goto err_free_block2mtd;
}
mutex_init(&dev->write_mutex);
@@ -255,7 +259,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
/* make the name contain the block device in */
name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
if (!name)
- goto devinit_err;
+ goto err_destroy_mutex;
dev->mtd.name = name;
@@ -274,7 +278,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
if (mtd_device_register(&dev->mtd, NULL, 0)) {
/* Device didn't get added, so free the entry */
- goto devinit_err;
+ goto err_destroy_mutex;
}
list_add(&dev->list, &blkmtd_device_list);
pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
@@ -283,7 +287,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
return dev;
-devinit_err:
+err_destroy_mutex:
+ mutex_destroy(&dev->write_mutex);
+err_free_block2mtd:
block2mtd_free_device(dev);
return NULL;
}
@@ -448,6 +454,7 @@ static void block2mtd_exit(void)
struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
block2mtd_sync(&dev->mtd);
mtd_device_unregister(&dev->mtd);
+ mutex_destroy(&dev->write_mutex);
pr_info("mtd%d: [%s] removed\n",
dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "));
diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c
index d1dd6a33a050..1fd4a0f77967 100644
--- a/drivers/mtd/devices/elm.c
+++ b/drivers/mtd/devices/elm.c
@@ -15,6 +15,8 @@
*
*/
+#define DRIVER_NAME "omap-elm"
+
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -84,6 +86,8 @@ struct elm_info {
struct list_head list;
enum bch_ecc bch_type;
struct elm_registers elm_regs;
+ int ecc_steps;
+ int ecc_syndrome_size;
};
static LIST_HEAD(elm_devices);
@@ -103,7 +107,8 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
* @dev: ELM device
* @bch_type: Type of BCH ecc
*/
-int elm_config(struct device *dev, enum bch_ecc bch_type)
+int elm_config(struct device *dev, enum bch_ecc bch_type,
+ int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
{
u32 reg_val;
struct elm_info *info = dev_get_drvdata(dev);
@@ -112,10 +117,22 @@ int elm_config(struct device *dev, enum bch_ecc bch_type)
dev_err(dev, "Unable to configure elm - device not probed?\n");
return -ENODEV;
}
+ /* ELM cannot detect ECC errors for chunks > 1KB */
+ if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
+ dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);
+ return -EINVAL;
+ }
+ /* ELM support 8 error syndrome process */
+ if (ecc_steps > ERROR_VECTOR_MAX) {
+ dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
+ return -EINVAL;
+ }
reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
- info->bch_type = bch_type;
+ info->bch_type = bch_type;
+ info->ecc_steps = ecc_steps;
+ info->ecc_syndrome_size = ecc_syndrome_size;
return 0;
}
@@ -157,17 +174,15 @@ static void elm_load_syndrome(struct elm_info *info,
int i, offset;
u32 val;
- for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+ for (i = 0; i < info->ecc_steps; i++) {
/* Check error reported */
if (err_vec[i].error_reported) {
elm_configure_page_mode(info, i, true);
offset = ELM_SYNDROME_FRAGMENT_0 +
SYNDROME_FRAGMENT_REG_SIZE * i;
-
- /* BCH8 */
- if (info->bch_type) {
-
+ switch (info->bch_type) {
+ case BCH8_ECC:
/* syndrome fragment 0 = ecc[9-12B] */
val = cpu_to_be32(*(u32 *) &ecc[9]);
elm_write_reg(info, offset, val);
@@ -186,7 +201,8 @@ static void elm_load_syndrome(struct elm_info *info,
offset += 4;
val = ecc[0];
elm_write_reg(info, offset, val);
- } else {
+ break;
+ case BCH4_ECC:
/* syndrome fragment 0 = ecc[20-52b] bits */
val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
((ecc[2] & 0xf) << 28);
@@ -196,11 +212,14 @@ static void elm_load_syndrome(struct elm_info *info,
offset += 4;
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
elm_write_reg(info, offset, val);
+ break;
+ default:
+ pr_err("invalid config bch_type\n");
}
}
/* Update ecc pointer with ecc byte size */
- ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
+ ecc += info->ecc_syndrome_size;
}
}
@@ -223,7 +242,7 @@ static void elm_start_processing(struct elm_info *info,
* Set syndrome vector valid, so that ELM module
* will process it for vectors error is reported
*/
- for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+ for (i = 0; i < info->ecc_steps; i++) {
if (err_vec[i].error_reported) {
offset = ELM_SYNDROME_FRAGMENT_6 +
SYNDROME_FRAGMENT_REG_SIZE * i;
@@ -252,7 +271,7 @@ static void elm_error_correction(struct elm_info *info,
int offset;
u32 reg_val;
- for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+ for (i = 0; i < info->ecc_steps; i++) {
/* Check error reported */
if (err_vec[i].error_reported) {
@@ -354,10 +373,8 @@ static int elm_probe(struct platform_device *pdev)
struct elm_info *info;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ if (!info)
return -ENOMEM;
- }
info->dev = &pdev->dev;
@@ -380,7 +397,7 @@ static int elm_probe(struct platform_device *pdev)
}
pm_runtime_enable(&pdev->dev);
- if (pm_runtime_get_sync(&pdev->dev)) {
+ if (pm_runtime_get_sync(&pdev->dev) < 0) {
ret = -EINVAL;
pm_runtime_disable(&pdev->dev);
dev_err(&pdev->dev, "can't enable clock\n");
@@ -505,7 +522,7 @@ MODULE_DEVICE_TABLE(of, elm_of_match);
static struct platform_driver elm_driver = {
.driver = {
- .name = "elm",
+ .name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(elm_of_match),
.pm = &elm_pm_ops,
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index ad1913909702..524dab3ac938 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -15,7 +15,6 @@
*
*/
-#include <linux/init.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -41,7 +40,8 @@
#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
-#define OPCODE_QUAD_READ 0x6b /* Read data bytes */
+#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */
+#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
@@ -54,7 +54,8 @@
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */
-#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */
+#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */
+#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */
#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */
@@ -95,6 +96,7 @@
enum read_type {
M25P80_NORMAL = 0,
M25P80_FAST,
+ M25P80_DUAL,
M25P80_QUAD,
};
@@ -479,6 +481,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
{
switch (flash->flash_read) {
case M25P80_FAST:
+ case M25P80_DUAL:
case M25P80_QUAD:
return 1;
case M25P80_NORMAL:
@@ -492,6 +495,8 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
{
switch (flash->flash_read) {
+ case M25P80_DUAL:
+ return 2;
case M25P80_QUAD:
return 4;
default:
@@ -855,7 +860,8 @@ struct flash_info {
#define SST_WRITE 0x04 /* use SST byte programming */
#define M25P_NO_FR 0x08 /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */
-#define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read */
+#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */
+#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
@@ -934,6 +940,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) },
/* Micron */
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
@@ -953,8 +960,8 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
- { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) },
- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) },
+ { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) },
+ { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) },
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
@@ -965,6 +972,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
+ { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
@@ -1072,9 +1080,8 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
info = (void *)m25p_ids[tmp].driver_data;
if (info->jedec_id == jedec) {
- if (info->ext_id != 0 && info->ext_id != ext_jedec)
- continue;
- return &m25p_ids[tmp];
+ if (info->ext_id == 0 || info->ext_id == ext_jedec)
+ return &m25p_ids[tmp];
}
}
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
@@ -1226,7 +1233,7 @@ static int m25p_probe(struct spi_device *spi)
if (info->flags & M25P_NO_FR)
flash->flash_read = M25P80_NORMAL;
- /* Quad-read mode takes precedence over fast/normal */
+ /* Quad/Dual-read mode takes precedence over fast/normal */
if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) {
ret = set_quad_mode(flash, info->jedec_id);
if (ret) {
@@ -1234,6 +1241,8 @@ static int m25p_probe(struct spi_device *spi)
return ret;
}
flash->flash_read = M25P80_QUAD;
+ } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) {
+ flash->flash_read = M25P80_DUAL;
}
/* Default commands */
@@ -1241,6 +1250,9 @@ static int m25p_probe(struct spi_device *spi)
case M25P80_QUAD:
flash->read_opcode = OPCODE_QUAD_READ;
break;
+ case M25P80_DUAL:
+ flash->read_opcode = OPCODE_DUAL_READ;
+ break;
case M25P80_FAST:
flash->read_opcode = OPCODE_FAST_READ;
break;
@@ -1265,6 +1277,9 @@ static int m25p_probe(struct spi_device *spi)
case M25P80_QUAD:
flash->read_opcode = OPCODE_QUAD_READ_4B;
break;
+ case M25P80_DUAL:
+ flash->read_opcode = OPCODE_DUAL_READ_4B;
+ break;
case M25P80_FAST:
flash->read_opcode = OPCODE_FAST_READ_4B;
break;
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 624069de4f28..dd22ce2cc9ad 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -10,7 +10,6 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -440,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
#ifdef CONFIG_MTD_DATAFLASH_OTP
-static int dataflash_get_otp_info(struct mtd_info *mtd,
- struct otp_info *info, size_t len)
+static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *info)
{
/* Report both blocks as identical: bytes 0..64, locked.
* Unless the user block changed from all-ones, we can't
@@ -450,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd,
info->start = 0;
info->length = 64;
info->locked = 1;
- return sizeof(*info);
+ *retlen = sizeof(*info);
+ return 0;
}
static ssize_t otp_read(struct spi_device *spi, unsigned base,
@@ -542,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
struct dataflash *priv = mtd->priv;
int status;
- if (len > 64)
- return -EINVAL;
+ if (from >= 64) {
+ /*
+ * Attempting to write beyond the end of OTP memory,
+ * no data can be written.
+ */
+ *retlen = 0;
+ return 0;
+ }
- /* Strictly speaking, we *could* truncate the write ... but
- * let's not do that for the only write that's ever possible.
- */
+ /* Truncate the write to fit into OTP memory. */
if ((from + len) > 64)
- return -EINVAL;
+ len = 64 - from;
/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
* IN: ignore all
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index e1f2aebaa489..2cceebfb251e 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -205,6 +205,8 @@ static inline void kill_final_newline(char *str)
return 1; \
} while (0)
+#ifndef MODULE
+static int phram_init_called;
/*
* This shall contain the module parameter if any. It is of the form:
* - phram=<device>,<address>,<size> for module case
@@ -213,9 +215,10 @@ static inline void kill_final_newline(char *str)
* size.
* Example: phram.phram=rootfs,0xa0000000,512Mi
*/
-static __initdata char phram_paramline[64 + 20 + 20];
+static char phram_paramline[64 + 20 + 20];
+#endif
-static int __init phram_setup(const char *val)
+static int phram_setup(const char *val)
{
char buf[64 + 20 + 20], *str = buf;
char *token[3];
@@ -264,17 +267,36 @@ static int __init phram_setup(const char *val)
return ret;
}
-static int __init phram_param_call(const char *val, struct kernel_param *kp)
+static int phram_param_call(const char *val, struct kernel_param *kp)
{
+#ifdef MODULE
+ return phram_setup(val);
+#else
/*
- * This function is always called before 'init_phram()', whether
- * built-in or module.
+ * If more parameters are later passed in via
+ * /sys/module/phram/parameters/phram
+ * and init_phram() has already been called,
+ * we can parse the argument now.
*/
+
+ if (phram_init_called)
+ return phram_setup(val);
+
+ /*
+ * During early boot stage, we only save the parameters
+ * here. We must parse them later: if the param passed
+ * from kernel boot command line, phram_param_call() is
+ * called so early that it is not possible to resolve
+ * the device (even kmalloc() fails). Defer that work to
+ * phram_setup().
+ */
+
if (strlen(val) >= sizeof(phram_paramline))
return -ENOSPC;
strcpy(phram_paramline, val);
return 0;
+#endif
}
module_param_call(phram, phram_param_call, NULL, NULL, 000);
@@ -283,10 +305,15 @@ MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"
static int __init init_phram(void)
{
+ int ret = 0;
+
+#ifndef MODULE
if (phram_paramline[0])
- return phram_setup(phram_paramline);
+ ret = phram_setup(phram_paramline);
+ phram_init_called = 1;
+#endif
- return 0;
+ return ret;
}
static void __exit cleanup_phram(void)
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index 0c51b988e1f8..f02603e1bfeb 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -725,16 +725,11 @@ static int __init init_pmc551(void)
}
mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
- "device.\n");
+ if (!mtd)
break;
- }
priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);
if (!priv) {
- printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
- "device.\n");
kfree(mtd);
break;
}
diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h
new file mode 100644
index 000000000000..4f0c2c7c898e
--- /dev/null
+++ b/drivers/mtd/devices/serial_flash_cmds.h
@@ -0,0 +1,81 @@
+/*
+ * Generic/SFDP Flash Commands and Device Capabilities
+ *
+ * Copyright (C) 2013 Lee Jones <lee.jones@lianro.org>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MTD_SERIAL_FLASH_CMDS_H
+#define _MTD_SERIAL_FLASH_CMDS_H
+
+/* Generic Flash Commands/OPCODEs */
+#define FLASH_CMD_WREN 0x06
+#define FLASH_CMD_WRDI 0x04
+#define FLASH_CMD_RDID 0x9f
+#define FLASH_CMD_RDSR 0x05
+#define FLASH_CMD_RDSR2 0x35
+#define FLASH_CMD_WRSR 0x01
+#define FLASH_CMD_SE_4K 0x20
+#define FLASH_CMD_SE_32K 0x52
+#define FLASH_CMD_SE 0xd8
+#define FLASH_CMD_CHIPERASE 0xc7
+#define FLASH_CMD_WRVCR 0x81
+#define FLASH_CMD_RDVCR 0x85
+
+/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
+#define FLASH_CMD_READ 0x03 /* READ */
+#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
+#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
+#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
+#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
+#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
+
+#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
+#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
+#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
+
+#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
+#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
+
+/* READ commands with 32-bit addressing */
+#define FLASH_CMD_READ4 0x13
+#define FLASH_CMD_READ4_FAST 0x0c
+#define FLASH_CMD_READ4_1_1_2 0x3c
+#define FLASH_CMD_READ4_1_2_2 0xbc
+#define FLASH_CMD_READ4_1_1_4 0x6c
+#define FLASH_CMD_READ4_1_4_4 0xec
+
+/* Configuration flags */
+#define FLASH_FLAG_SINGLE 0x000000ff
+#define FLASH_FLAG_READ_WRITE 0x00000001
+#define FLASH_FLAG_READ_FAST 0x00000002
+#define FLASH_FLAG_SE_4K 0x00000004
+#define FLASH_FLAG_SE_32K 0x00000008
+#define FLASH_FLAG_CE 0x00000010
+#define FLASH_FLAG_32BIT_ADDR 0x00000020
+#define FLASH_FLAG_RESET 0x00000040
+#define FLASH_FLAG_DYB_LOCKING 0x00000080
+
+#define FLASH_FLAG_DUAL 0x0000ff00
+#define FLASH_FLAG_READ_1_1_2 0x00000100
+#define FLASH_FLAG_READ_1_2_2 0x00000200
+#define FLASH_FLAG_READ_2_2_2 0x00000400
+#define FLASH_FLAG_WRITE_1_1_2 0x00001000
+#define FLASH_FLAG_WRITE_1_2_2 0x00002000
+#define FLASH_FLAG_WRITE_2_2_2 0x00004000
+
+#define FLASH_FLAG_QUAD 0x00ff0000
+#define FLASH_FLAG_READ_1_1_4 0x00010000
+#define FLASH_FLAG_READ_1_4_4 0x00020000
+#define FLASH_FLAG_READ_4_4_4 0x00040000
+#define FLASH_FLAG_WRITE_1_1_4 0x00100000
+#define FLASH_FLAG_WRITE_1_4_4 0x00200000
+#define FLASH_FLAG_WRITE_4_4_4 0x00400000
+
+#endif /* _MTD_SERIAL_FLASH_CMDS_H */
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index 423821412062..363da96e6891 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -913,7 +913,6 @@ static int spear_smi_probe(struct platform_device *pdev)
if (np) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
- pr_err("%s: ERROR: no memory", __func__);
ret = -ENOMEM;
goto err;
}
@@ -943,7 +942,6 @@ static int spear_smi_probe(struct platform_device *pdev)
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
if (!dev) {
ret = -ENOMEM;
- dev_err(&pdev->dev, "mem alloc fail\n");
goto err;
}
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 687bf27ec850..c63ecbcad0b7 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -15,7 +15,6 @@
*
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
new file mode 100644
index 000000000000..1957d7c8e185
--- /dev/null
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -0,0 +1,2108 @@
+/*
+ * st_spi_fsm.c - ST Fast Sequence Mode (FSM) Serial Flash Controller
+ *
+ * Author: Angus Clark <angus.clark@st.com>
+ *
+ * Copyright (C) 2010-2014 STMicroelectronics Limited
+ *
+ * JEDEC probe based on drivers/mtd/devices/m25p80.c
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "serial_flash_cmds.h"
+
+/*
+ * FSM SPI Controller Registers
+ */
+#define SPI_CLOCKDIV 0x0010
+#define SPI_MODESELECT 0x0018
+#define SPI_CONFIGDATA 0x0020
+#define SPI_STA_MODE_CHANGE 0x0028
+#define SPI_FAST_SEQ_TRANSFER_SIZE 0x0100
+#define SPI_FAST_SEQ_ADD1 0x0104
+#define SPI_FAST_SEQ_ADD2 0x0108
+#define SPI_FAST_SEQ_ADD_CFG 0x010c
+#define SPI_FAST_SEQ_OPC1 0x0110
+#define SPI_FAST_SEQ_OPC2 0x0114
+#define SPI_FAST_SEQ_OPC3 0x0118
+#define SPI_FAST_SEQ_OPC4 0x011c
+#define SPI_FAST_SEQ_OPC5 0x0120
+#define SPI_MODE_BITS 0x0124
+#define SPI_DUMMY_BITS 0x0128
+#define SPI_FAST_SEQ_FLASH_STA_DATA 0x012c
+#define SPI_FAST_SEQ_1 0x0130
+#define SPI_FAST_SEQ_2 0x0134
+#define SPI_FAST_SEQ_3 0x0138
+#define SPI_FAST_SEQ_4 0x013c
+#define SPI_FAST_SEQ_CFG 0x0140
+#define SPI_FAST_SEQ_STA 0x0144
+#define SPI_QUAD_BOOT_SEQ_INIT_1 0x0148
+#define SPI_QUAD_BOOT_SEQ_INIT_2 0x014c
+#define SPI_QUAD_BOOT_READ_SEQ_1 0x0150
+#define SPI_QUAD_BOOT_READ_SEQ_2 0x0154
+#define SPI_PROGRAM_ERASE_TIME 0x0158
+#define SPI_MULT_PAGE_REPEAT_SEQ_1 0x015c
+#define SPI_MULT_PAGE_REPEAT_SEQ_2 0x0160
+#define SPI_STATUS_WR_TIME_REG 0x0164
+#define SPI_FAST_SEQ_DATA_REG 0x0300
+
+/*
+ * Register: SPI_MODESELECT
+ */
+#define SPI_MODESELECT_CONTIG 0x01
+#define SPI_MODESELECT_FASTREAD 0x02
+#define SPI_MODESELECT_DUALIO 0x04
+#define SPI_MODESELECT_FSM 0x08
+#define SPI_MODESELECT_QUADBOOT 0x10
+
+/*
+ * Register: SPI_CONFIGDATA
+ */
+#define SPI_CFG_DEVICE_ST 0x1
+#define SPI_CFG_DEVICE_ATMEL 0x4
+#define SPI_CFG_MIN_CS_HIGH(x) (((x) & 0xfff) << 4)
+#define SPI_CFG_CS_SETUPHOLD(x) (((x) & 0xff) << 16)
+#define SPI_CFG_DATA_HOLD(x) (((x) & 0xff) << 24)
+
+#define SPI_CFG_DEFAULT_MIN_CS_HIGH SPI_CFG_MIN_CS_HIGH(0x0AA)
+#define SPI_CFG_DEFAULT_CS_SETUPHOLD SPI_CFG_CS_SETUPHOLD(0xA0)
+#define SPI_CFG_DEFAULT_DATA_HOLD SPI_CFG_DATA_HOLD(0x00)
+
+/*
+ * Register: SPI_FAST_SEQ_TRANSFER_SIZE
+ */
+#define TRANSFER_SIZE(x) ((x) * 8)
+
+/*
+ * Register: SPI_FAST_SEQ_ADD_CFG
+ */
+#define ADR_CFG_CYCLES_ADD1(x) ((x) << 0)
+#define ADR_CFG_PADS_1_ADD1 (0x0 << 6)
+#define ADR_CFG_PADS_2_ADD1 (0x1 << 6)
+#define ADR_CFG_PADS_4_ADD1 (0x3 << 6)
+#define ADR_CFG_CSDEASSERT_ADD1 (1 << 8)
+#define ADR_CFG_CYCLES_ADD2(x) ((x) << (0+16))
+#define ADR_CFG_PADS_1_ADD2 (0x0 << (6+16))
+#define ADR_CFG_PADS_2_ADD2 (0x1 << (6+16))
+#define ADR_CFG_PADS_4_ADD2 (0x3 << (6+16))
+#define ADR_CFG_CSDEASSERT_ADD2 (1 << (8+16))
+
+/*
+ * Register: SPI_FAST_SEQ_n
+ */
+#define SEQ_OPC_OPCODE(x) ((x) << 0)
+#define SEQ_OPC_CYCLES(x) ((x) << 8)
+#define SEQ_OPC_PADS_1 (0x0 << 14)
+#define SEQ_OPC_PADS_2 (0x1 << 14)
+#define SEQ_OPC_PADS_4 (0x3 << 14)
+#define SEQ_OPC_CSDEASSERT (1 << 16)
+
+/*
+ * Register: SPI_FAST_SEQ_CFG
+ */
+#define SEQ_CFG_STARTSEQ (1 << 0)
+#define SEQ_CFG_SWRESET (1 << 5)
+#define SEQ_CFG_CSDEASSERT (1 << 6)
+#define SEQ_CFG_READNOTWRITE (1 << 7)
+#define SEQ_CFG_ERASE (1 << 8)
+#define SEQ_CFG_PADS_1 (0x0 << 16)
+#define SEQ_CFG_PADS_2 (0x1 << 16)
+#define SEQ_CFG_PADS_4 (0x3 << 16)
+
+/*
+ * Register: SPI_MODE_BITS
+ */
+#define MODE_DATA(x) (x & 0xff)
+#define MODE_CYCLES(x) ((x & 0x3f) << 16)
+#define MODE_PADS_1 (0x0 << 22)
+#define MODE_PADS_2 (0x1 << 22)
+#define MODE_PADS_4 (0x3 << 22)
+#define DUMMY_CSDEASSERT (1 << 24)
+
+/*
+ * Register: SPI_DUMMY_BITS
+ */
+#define DUMMY_CYCLES(x) ((x & 0x3f) << 16)
+#define DUMMY_PADS_1 (0x0 << 22)
+#define DUMMY_PADS_2 (0x1 << 22)
+#define DUMMY_PADS_4 (0x3 << 22)
+#define DUMMY_CSDEASSERT (1 << 24)
+
+/*
+ * Register: SPI_FAST_SEQ_FLASH_STA_DATA
+ */
+#define STA_DATA_BYTE1(x) ((x & 0xff) << 0)
+#define STA_DATA_BYTE2(x) ((x & 0xff) << 8)
+#define STA_PADS_1 (0x0 << 16)
+#define STA_PADS_2 (0x1 << 16)
+#define STA_PADS_4 (0x3 << 16)
+#define STA_CSDEASSERT (0x1 << 20)
+#define STA_RDNOTWR (0x1 << 21)
+
+/*
+ * FSM SPI Instruction Opcodes
+ */
+#define STFSM_OPC_CMD 0x1
+#define STFSM_OPC_ADD 0x2
+#define STFSM_OPC_STA 0x3
+#define STFSM_OPC_MODE 0x4
+#define STFSM_OPC_DUMMY 0x5
+#define STFSM_OPC_DATA 0x6
+#define STFSM_OPC_WAIT 0x7
+#define STFSM_OPC_JUMP 0x8
+#define STFSM_OPC_GOTO 0x9
+#define STFSM_OPC_STOP 0xF
+
+/*
+ * FSM SPI Instructions (== opcode + operand).
+ */
+#define STFSM_INSTR(cmd, op) ((cmd) | ((op) << 4))
+
+#define STFSM_INST_CMD1 STFSM_INSTR(STFSM_OPC_CMD, 1)
+#define STFSM_INST_CMD2 STFSM_INSTR(STFSM_OPC_CMD, 2)
+#define STFSM_INST_CMD3 STFSM_INSTR(STFSM_OPC_CMD, 3)
+#define STFSM_INST_CMD4 STFSM_INSTR(STFSM_OPC_CMD, 4)
+#define STFSM_INST_CMD5 STFSM_INSTR(STFSM_OPC_CMD, 5)
+#define STFSM_INST_ADD1 STFSM_INSTR(STFSM_OPC_ADD, 1)
+#define STFSM_INST_ADD2 STFSM_INSTR(STFSM_OPC_ADD, 2)
+
+#define STFSM_INST_DATA_WRITE STFSM_INSTR(STFSM_OPC_DATA, 1)
+#define STFSM_INST_DATA_READ STFSM_INSTR(STFSM_OPC_DATA, 2)
+
+#define STFSM_INST_STA_RD1 STFSM_INSTR(STFSM_OPC_STA, 0x1)
+#define STFSM_INST_STA_WR1 STFSM_INSTR(STFSM_OPC_STA, 0x1)
+#define STFSM_INST_STA_RD2 STFSM_INSTR(STFSM_OPC_STA, 0x2)
+#define STFSM_INST_STA_WR1_2 STFSM_INSTR(STFSM_OPC_STA, 0x3)
+
+#define STFSM_INST_MODE STFSM_INSTR(STFSM_OPC_MODE, 0)
+#define STFSM_INST_DUMMY STFSM_INSTR(STFSM_OPC_DUMMY, 0)
+#define STFSM_INST_WAIT STFSM_INSTR(STFSM_OPC_WAIT, 0)
+#define STFSM_INST_STOP STFSM_INSTR(STFSM_OPC_STOP, 0)
+
+#define STFSM_DEFAULT_EMI_FREQ 100000000UL /* 100 MHz */
+#define STFSM_DEFAULT_WR_TIME (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */
+
+#define STFSM_FLASH_SAFE_FREQ 10000000UL /* 10 MHz */
+
+#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */
+
+/* Flash Commands */
+#define FLASH_CMD_WREN 0x06
+#define FLASH_CMD_WRDI 0x04
+#define FLASH_CMD_RDID 0x9f
+#define FLASH_CMD_RDSR 0x05
+#define FLASH_CMD_RDSR2 0x35
+#define FLASH_CMD_WRSR 0x01
+#define FLASH_CMD_SE_4K 0x20
+#define FLASH_CMD_SE_32K 0x52
+#define FLASH_CMD_SE 0xd8
+#define FLASH_CMD_CHIPERASE 0xc7
+#define FLASH_CMD_WRVCR 0x81
+#define FLASH_CMD_RDVCR 0x85
+
+#define FLASH_CMD_READ 0x03 /* READ */
+#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
+#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
+#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
+#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
+#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
+
+#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
+#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
+#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
+
+#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
+#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
+
+/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
+#define FLASH_CMD_READ4 0x13
+#define FLASH_CMD_READ4_FAST 0x0c
+#define FLASH_CMD_READ4_1_1_2 0x3c
+#define FLASH_CMD_READ4_1_2_2 0xbc
+#define FLASH_CMD_READ4_1_1_4 0x6c
+#define FLASH_CMD_READ4_1_4_4 0xec
+
+/* S25FLxxxS commands */
+#define S25FL_CMD_WRITE4_1_1_4 0x34
+#define S25FL_CMD_SE4 0xdc
+#define S25FL_CMD_CLSR 0x30
+#define S25FL_CMD_DYBWR 0xe1
+#define S25FL_CMD_DYBRD 0xe0
+#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with
+ * 'FLASH_CMD_WRITE_1_4_4'
+ * as found on N25Qxxx devices! */
+
+/* Status register */
+#define FLASH_STATUS_BUSY 0x01
+#define FLASH_STATUS_WEL 0x02
+#define FLASH_STATUS_BP0 0x04
+#define FLASH_STATUS_BP1 0x08
+#define FLASH_STATUS_BP2 0x10
+#define FLASH_STATUS_SRWP0 0x80
+#define FLASH_STATUS_TIMEOUT 0xff
+/* S25FL Error Flags */
+#define S25FL_STATUS_E_ERR 0x20
+#define S25FL_STATUS_P_ERR 0x40
+
+#define FLASH_PAGESIZE 256 /* In Bytes */
+#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
+#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
+
+/*
+ * Flags to tweak operation of default read/write/erase routines
+ */
+#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
+#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
+#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004
+#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
+#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
+
+struct stfsm_seq {
+ uint32_t data_size;
+ uint32_t addr1;
+ uint32_t addr2;
+ uint32_t addr_cfg;
+ uint32_t seq_opc[5];
+ uint32_t mode;
+ uint32_t dummy;
+ uint32_t status;
+ uint8_t seq[16];
+ uint32_t seq_cfg;
+} __packed __aligned(4);
+
+struct stfsm {
+ struct device *dev;
+ void __iomem *base;
+ struct resource *region;
+ struct mtd_info mtd;
+ struct mutex lock;
+ struct flash_info *info;
+
+ uint32_t configuration;
+ uint32_t fifo_dir_delay;
+ bool booted_from_spi;
+ bool reset_signal;
+ bool reset_por;
+
+ struct stfsm_seq stfsm_seq_read;
+ struct stfsm_seq stfsm_seq_write;
+ struct stfsm_seq stfsm_seq_en_32bit_addr;
+};
+
+/* Parameters to configure a READ or WRITE FSM sequence */
+struct seq_rw_config {
+ uint32_t flags; /* flags to support config */
+ uint8_t cmd; /* FLASH command */
+ int write; /* Write Sequence */
+ uint8_t addr_pads; /* No. of addr pads (MODE & DUMMY) */
+ uint8_t data_pads; /* No. of data pads */
+ uint8_t mode_data; /* MODE data */
+ uint8_t mode_cycles; /* No. of MODE cycles */
+ uint8_t dummy_cycles; /* No. of DUMMY cycles */
+};
+
+/* SPI Flash Device Table */
+struct flash_info {
+ char *name;
+ /*
+ * JEDEC id zero means "no ID" (most older chips); otherwise it has
+ * a high byte of zero plus three data bytes: the manufacturer id,
+ * then a two byte device id.
+ */
+ u32 jedec_id;
+ u16 ext_id;
+ /*
+ * The size listed here is what works with FLASH_CMD_SE, which isn't
+ * necessarily called a "sector" by the vendor.
+ */
+ unsigned sector_size;
+ u16 n_sectors;
+ u32 flags;
+ /*
+ * Note, where FAST_READ is supported, freq_max specifies the
+ * FAST_READ frequency, not the READ frequency.
+ */
+ u32 max_freq;
+ int (*config)(struct stfsm *);
+};
+
+static int stfsm_n25q_config(struct stfsm *fsm);
+static int stfsm_mx25_config(struct stfsm *fsm);
+static int stfsm_s25fl_config(struct stfsm *fsm);
+static int stfsm_w25q_config(struct stfsm *fsm);
+
+static struct flash_info flash_types[] = {
+ /*
+ * ST Microelectronics/Numonyx --
+ * (newer production versions may have feature updates
+ * (eg faster operating frequency)
+ */
+#define M25P_FLAG (FLASH_FLAG_READ_WRITE | FLASH_FLAG_READ_FAST)
+ { "m25p40", 0x202013, 0, 64 * 1024, 8, M25P_FLAG, 25, NULL },
+ { "m25p80", 0x202014, 0, 64 * 1024, 16, M25P_FLAG, 25, NULL },
+ { "m25p16", 0x202015, 0, 64 * 1024, 32, M25P_FLAG, 25, NULL },
+ { "m25p32", 0x202016, 0, 64 * 1024, 64, M25P_FLAG, 50, NULL },
+ { "m25p64", 0x202017, 0, 64 * 1024, 128, M25P_FLAG, 50, NULL },
+ { "m25p128", 0x202018, 0, 256 * 1024, 64, M25P_FLAG, 50, NULL },
+
+#define M25PX_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_WRITE_1_1_2)
+ { "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
+ { "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL },
+
+#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_SE_4K | \
+ FLASH_FLAG_SE_32K)
+ { "mx25l25635e", 0xc22019, 0, 64*1024, 512,
+ (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
+ stfsm_mx25_config },
+
+#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_WRITE_1_1_2 | \
+ FLASH_FLAG_WRITE_1_2_2 | \
+ FLASH_FLAG_WRITE_1_1_4 | \
+ FLASH_FLAG_WRITE_1_4_4)
+ { "n25q128", 0x20ba18, 0, 64 * 1024, 256, N25Q_FLAG, 108,
+ stfsm_n25q_config },
+ { "n25q256", 0x20ba19, 0, 64 * 1024, 512,
+ N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config },
+
+ /*
+ * Spansion S25FLxxxP
+ * - 256KiB and 64KiB sector variants (identified by ext. JEDEC)
+ */
+#define S25FLXXXP_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_WRITE_1_1_4 | \
+ FLASH_FLAG_READ_FAST)
+ { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80,
+ stfsm_s25fl_config },
+ { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80,
+ stfsm_s25fl_config },
+
+ /*
+ * Spansion S25FLxxxS
+ * - 256KiB and 64KiB sector variants (identified by ext. JEDEC)
+ * - RESET# signal supported by die but not bristled out on all
+ * package types. The package type is a function of board design,
+ * so this information is captured in the board's flags.
+ * - Supports 'DYB' sector protection. Depending on variant, sectors
+ * may default to locked state on power-on.
+ */
+#define S25FLXXXS_FLAG (S25FLXXXP_FLAG | \
+ FLASH_FLAG_RESET | \
+ FLASH_FLAG_DYB_LOCKING)
+ { "s25fl128s0", 0x012018, 0x0300, 256 * 1024, 64, S25FLXXXS_FLAG, 80,
+ stfsm_s25fl_config },
+ { "s25fl128s1", 0x012018, 0x0301, 64 * 1024, 256, S25FLXXXS_FLAG, 80,
+ stfsm_s25fl_config },
+ { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128,
+ S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config },
+ { "s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512,
+ S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config },
+
+ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+#define W25X_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_WRITE_1_1_2)
+ { "w25x40", 0xef3013, 0, 64 * 1024, 8, W25X_FLAG, 75, NULL },
+ { "w25x80", 0xef3014, 0, 64 * 1024, 16, W25X_FLAG, 75, NULL },
+ { "w25x16", 0xef3015, 0, 64 * 1024, 32, W25X_FLAG, 75, NULL },
+ { "w25x32", 0xef3016, 0, 64 * 1024, 64, W25X_FLAG, 75, NULL },
+ { "w25x64", 0xef3017, 0, 64 * 1024, 128, W25X_FLAG, 75, NULL },
+
+ /* Winbond -- w25q "blocks" are 64K, "sectors" are 4KiB */
+#define W25Q_FLAG (FLASH_FLAG_READ_WRITE | \
+ FLASH_FLAG_READ_FAST | \
+ FLASH_FLAG_READ_1_1_2 | \
+ FLASH_FLAG_READ_1_2_2 | \
+ FLASH_FLAG_READ_1_1_4 | \
+ FLASH_FLAG_READ_1_4_4 | \
+ FLASH_FLAG_WRITE_1_1_4)
+ { "w25q80", 0xef4014, 0, 64 * 1024, 16, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+ { "w25q16", 0xef4015, 0, 64 * 1024, 32, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+ { "w25q32", 0xef4016, 0, 64 * 1024, 64, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+ { "w25q64", 0xef4017, 0, 64 * 1024, 128, W25Q_FLAG, 80,
+ stfsm_w25q_config },
+
+ /* Sentinel */
+ { NULL, 0x000000, 0, 0, 0, 0, 0, NULL },
+};
+
+/*
+ * FSM message sequence configurations:
+ *
+ * All configs are presented in order of preference
+ */
+
+/* Default READ configurations, in order of preference */
+static struct seq_rw_config default_read_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/* Default WRITE configurations */
+static struct seq_rw_config default_write_configs[] = {
+ {FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
+ {FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
+ {FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
+ {FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [N25Qxxx] Configuration
+ */
+#define N25Q_VCR_DUMMY_CYCLES(x) (((x) & 0xf) << 4)
+#define N25Q_VCR_XIP_DISABLED ((uint8_t)0x1 << 3)
+#define N25Q_VCR_WRAP_CONT 0x3
+
+/* N25Q 3-byte Address READ configurations
+ * - 'FAST' variants configured for 8 dummy cycles.
+ *
+ * Note, the number of dummy cycles used for 'FAST' READ operations is
+ * configurable and would normally be tuned according to the READ command and
+ * operating frequency. However, this applies universally to all 'FAST' READ
+ * commands, including those used by the SPIBoot controller, and remains in
+ * force until the device is power-cycled. Since the SPIBoot controller is
+ * hard-wired to use 8 dummy cycles, we must configure the device to also use 8
+ * cycles.
+ */
+static struct seq_rw_config n25q_read3_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/* N25Q 4-byte Address READ configurations
+ * - use special 4-byte address READ commands (reduces overheads, and
+ * reduces risk of hitting watchdog reset issues).
+ * - 'FAST' variants configured for 8 dummy cycles (see note above.)
+ */
+static struct seq_rw_config n25q_read4_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [MX25xxx] Configuration
+ */
+#define MX25_STATUS_QE (0x1 << 6)
+
+static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
+{
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
+ SEQ_OPC_CSDEASSERT);
+
+ seq->seq[0] = STFSM_INST_CMD1;
+ seq->seq[1] = STFSM_INST_WAIT;
+ seq->seq[2] = STFSM_INST_STOP;
+
+ seq->seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ);
+
+ return 0;
+}
+
+/*
+ * [S25FLxxx] Configuration
+ */
+#define STFSM_S25FL_CONFIG_QE (0x1 << 1)
+
+/*
+ * S25FLxxxS devices provide three ways of supporting 32-bit addressing: Bank
+ * Register, Extended Address Modes, and a 32-bit address command set. The
+ * 32-bit address command set is used here, since it avoids any problems with
+ * entering a state that is incompatible with the SPIBoot Controller.
+ */
+static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
+ {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
+ {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
+ {FLASH_FLAG_WRITE_1_1_4, S25FL_CMD_WRITE4_1_1_4, 1, 1, 4, 0x00, 0, 0},
+ {FLASH_FLAG_READ_WRITE, S25FL_CMD_WRITE4, 1, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [W25Qxxx] Configuration
+ */
+#define W25Q_STATUS_QE (0x1 << 9)
+
+static struct stfsm_seq stfsm_seq_read_jedec = {
+ .data_size = TRANSFER_SIZE(8),
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_DATA_READ,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_read_status_fifo = {
+ .data_size = TRANSFER_SIZE(4),
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_DATA_READ,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_erase_sector = {
+ /* 'addr_cfg' configured during initialisation */
+ .seq_opc = {
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_SE)),
+ },
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_ADD1,
+ STFSM_INST_ADD2,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_erase_chip = {
+ .seq_opc = {
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+
+ (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT),
+ },
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_WAIT,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_write_status = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_STA_WR1,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_wrvcr = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_STA_WR1,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+};
+
+static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
+{
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR));
+ seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+ SEQ_OPC_CSDEASSERT);
+
+ seq->seq[0] = STFSM_INST_CMD2;
+ seq->seq[1] = STFSM_INST_CMD1;
+ seq->seq[2] = STFSM_INST_WAIT;
+ seq->seq[3] = STFSM_INST_STOP;
+
+ seq->seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ);
+
+ return 0;
+}
+
+static inline int stfsm_is_idle(struct stfsm *fsm)
+{
+ return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10;
+}
+
+static inline uint32_t stfsm_fifo_available(struct stfsm *fsm)
+{
+ return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f;
+}
+
+static void stfsm_clear_fifo(struct stfsm *fsm)
+{
+ uint32_t avail;
+
+ for (;;) {
+ avail = stfsm_fifo_available(fsm);
+ if (!avail)
+ break;
+
+ while (avail) {
+ readl(fsm->base + SPI_FAST_SEQ_DATA_REG);
+ avail--;
+ }
+ }
+}
+
+static inline void stfsm_load_seq(struct stfsm *fsm,
+ const struct stfsm_seq *seq)
+{
+ void __iomem *dst = fsm->base + SPI_FAST_SEQ_TRANSFER_SIZE;
+ const uint32_t *src = (const uint32_t *)seq;
+ int words = sizeof(*seq) / sizeof(*src);
+
+ BUG_ON(!stfsm_is_idle(fsm));
+
+ while (words--) {
+ writel(*src, dst);
+ src++;
+ dst += 4;
+ }
+}
+
+static void stfsm_wait_seq(struct stfsm *fsm)
+{
+ unsigned long deadline;
+ int timeout = 0;
+
+ deadline = jiffies + msecs_to_jiffies(STFSM_MAX_WAIT_SEQ_MS);
+
+ while (!timeout) {
+ if (time_after_eq(jiffies, deadline))
+ timeout = 1;
+
+ if (stfsm_is_idle(fsm))
+ return;
+
+ cond_resched();
+ }
+
+ dev_err(fsm->dev, "timeout on sequence completion\n");
+}
+
+static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
+{
+ uint32_t remaining = size >> 2;
+ uint32_t avail;
+ uint32_t words;
+
+ dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
+
+ BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
+
+ while (remaining) {
+ for (;;) {
+ avail = stfsm_fifo_available(fsm);
+ if (avail)
+ break;
+ udelay(1);
+ }
+ words = min(avail, remaining);
+ remaining -= words;
+
+ readsl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
+ buf += words;
+ }
+}
+
+static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
+ uint32_t size)
+{
+ uint32_t words = size >> 2;
+
+ dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
+
+ BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
+
+ writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
+
+ return size;
+}
+
+static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
+{
+ struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
+ uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR;
+
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(cmd) |
+ SEQ_OPC_CSDEASSERT);
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+static uint8_t stfsm_wait_busy(struct stfsm *fsm)
+{
+ struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
+ unsigned long deadline;
+ uint32_t status;
+ int timeout = 0;
+
+ /* Use RDRS1 */
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
+
+ /* Load read_status sequence */
+ stfsm_load_seq(fsm, seq);
+
+ /*
+ * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS)
+ */
+ deadline = jiffies + FLASH_MAX_BUSY_WAIT;
+ while (!timeout) {
+ if (time_after_eq(jiffies, deadline))
+ timeout = 1;
+
+ stfsm_wait_seq(fsm);
+
+ stfsm_read_fifo(fsm, &status, 4);
+
+ if ((status & FLASH_STATUS_BUSY) == 0)
+ return 0;
+
+ if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) &&
+ ((status & S25FL_STATUS_P_ERR) ||
+ (status & S25FL_STATUS_E_ERR)))
+ return (uint8_t)(status & 0xff);
+
+ if (!timeout)
+ /* Restart */
+ writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG);
+
+ cond_resched();
+ }
+
+ dev_err(fsm->dev, "timeout on wait_busy\n");
+
+ return FLASH_STATUS_TIMEOUT;
+}
+
+static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
+ uint8_t *status)
+{
+ struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
+ uint32_t tmp;
+
+ dev_dbg(fsm->dev, "reading STA[%s]\n",
+ (cmd == FLASH_CMD_RDSR) ? "1" : "2");
+
+ seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(cmd)),
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_read_fifo(fsm, &tmp, 4);
+
+ *status = (uint8_t)(tmp >> 24);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
+ int sta_bytes)
+{
+ struct stfsm_seq *seq = &stfsm_seq_write_status;
+
+ dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
+ (sta_bytes == 1) ? "1" : "1+2", status);
+
+ seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT;
+ seq->seq[2] = (sta_bytes == 1) ?
+ STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+};
+
+static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
+{
+ struct stfsm_seq *seq = &stfsm_seq_wrvcr;
+
+ dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
+
+ seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT);
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+/*
+ * SoC reset on 'boot-from-spi' systems
+ *
+ * Certain modes of operation cause the Flash device to enter a particular state
+ * for a period of time (e.g. 'Erase Sector', 'Quad Enable', and 'Enter 32-bit
+ * Addr' commands). On boot-from-spi systems, it is important to consider what
+ * happens if a warm reset occurs during this period. The SPIBoot controller
+ * assumes that Flash device is in its default reset state, 24-bit address mode,
+ * and ready to accept commands. This can be achieved using some form of
+ * on-board logic/controller to force a device POR in response to a SoC-level
+ * reset or by making use of the device reset signal if available (limited
+ * number of devices only).
+ *
+ * Failure to take such precautions can cause problems following a warm reset.
+ * For some operations (e.g. ERASE), there is little that can be done. For
+ * other modes of operation (e.g. 32-bit addressing), options are often
+ * available that can help minimise the window in which a reset could cause a
+ * problem.
+ *
+ */
+static bool stfsm_can_handle_soc_reset(struct stfsm *fsm)
+{
+ /* Reset signal is available on the board and supported by the device */
+ if (fsm->reset_signal && fsm->info->flags & FLASH_FLAG_RESET)
+ return true;
+
+ /* Board-level logic forces a power-on-reset */
+ if (fsm->reset_por)
+ return true;
+
+ /* Reset is not properly handled and may result in failure to reboot */
+ return false;
+}
+
+/* Configure 'addr_cfg' according to addressing mode */
+static void stfsm_prepare_erasesec_seq(struct stfsm *fsm,
+ struct stfsm_seq *seq)
+{
+ int addr1_cycles = fsm->info->flags & FLASH_FLAG_32BIT_ADDR ? 16 : 8;
+
+ seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(addr1_cycles) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2 |
+ ADR_CFG_CSDEASSERT_ADD2);
+}
+
+/* Search for preferred configuration based on available flags */
+static struct seq_rw_config *
+stfsm_search_seq_rw_configs(struct stfsm *fsm,
+ struct seq_rw_config cfgs[])
+{
+ struct seq_rw_config *config;
+ int flags = fsm->info->flags;
+
+ for (config = cfgs; config->cmd != 0; config++)
+ if ((config->flags & flags) == config->flags)
+ return config;
+
+ return NULL;
+}
+
+/* Prepare a READ/WRITE sequence according to configuration parameters */
+static void stfsm_prepare_rw_seq(struct stfsm *fsm,
+ struct stfsm_seq *seq,
+ struct seq_rw_config *cfg)
+{
+ int addr1_cycles, addr2_cycles;
+ int i = 0;
+
+ memset(seq, 0, sizeof(*seq));
+
+ /* Add READ/WRITE OPC */
+ seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(cfg->cmd));
+
+ /* Add WREN OPC for a WRITE sequence */
+ if (cfg->write)
+ seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+ SEQ_OPC_CSDEASSERT);
+
+ /* Address configuration (24 or 32-bit addresses) */
+ addr1_cycles = (fsm->info->flags & FLASH_FLAG_32BIT_ADDR) ? 16 : 8;
+ addr1_cycles /= cfg->addr_pads;
+ addr2_cycles = 16 / cfg->addr_pads;
+ seq->addr_cfg = ((addr1_cycles & 0x3f) << 0 | /* ADD1 cycles */
+ (cfg->addr_pads - 1) << 6 | /* ADD1 pads */
+ (addr2_cycles & 0x3f) << 16 | /* ADD2 cycles */
+ ((cfg->addr_pads - 1) << 22)); /* ADD2 pads */
+
+ /* Data/Sequence configuration */
+ seq->seq_cfg = ((cfg->data_pads - 1) << 16 |
+ SEQ_CFG_STARTSEQ |
+ SEQ_CFG_CSDEASSERT);
+ if (!cfg->write)
+ seq->seq_cfg |= SEQ_CFG_READNOTWRITE;
+
+ /* Mode configuration (no. of pads taken from addr cfg) */
+ seq->mode = ((cfg->mode_data & 0xff) << 0 | /* data */
+ (cfg->mode_cycles & 0x3f) << 16 | /* cycles */
+ (cfg->addr_pads - 1) << 22); /* pads */
+
+ /* Dummy configuration (no. of pads taken from addr cfg) */
+ seq->dummy = ((cfg->dummy_cycles & 0x3f) << 16 | /* cycles */
+ (cfg->addr_pads - 1) << 22); /* pads */
+
+
+ /* Instruction sequence */
+ i = 0;
+ if (cfg->write)
+ seq->seq[i++] = STFSM_INST_CMD2;
+
+ seq->seq[i++] = STFSM_INST_CMD1;
+
+ seq->seq[i++] = STFSM_INST_ADD1;
+ seq->seq[i++] = STFSM_INST_ADD2;
+
+ if (cfg->mode_cycles)
+ seq->seq[i++] = STFSM_INST_MODE;
+
+ if (cfg->dummy_cycles)
+ seq->seq[i++] = STFSM_INST_DUMMY;
+
+ seq->seq[i++] =
+ cfg->write ? STFSM_INST_DATA_WRITE : STFSM_INST_DATA_READ;
+ seq->seq[i++] = STFSM_INST_STOP;
+}
+
+static int stfsm_search_prepare_rw_seq(struct stfsm *fsm,
+ struct stfsm_seq *seq,
+ struct seq_rw_config *cfgs)
+{
+ struct seq_rw_config *config;
+
+ config = stfsm_search_seq_rw_configs(fsm, cfgs);
+ if (!config) {
+ dev_err(fsm->dev, "failed to find suitable config\n");
+ return -EINVAL;
+ }
+
+ stfsm_prepare_rw_seq(fsm, seq, config);
+
+ return 0;
+}
+
+/* Prepare a READ/WRITE/ERASE 'default' sequences */
+static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm)
+{
+ uint32_t flags = fsm->info->flags;
+ int ret;
+
+ /* Configure 'READ' sequence */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ default_read_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "failed to prep READ sequence with flags [0x%08x]\n",
+ flags);
+ return ret;
+ }
+
+ /* Configure 'WRITE' sequence */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+ default_write_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "failed to prep WRITE sequence with flags [0x%08x]\n",
+ flags);
+ return ret;
+ }
+
+ /* Configure 'ERASE_SECTOR' sequence */
+ stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector);
+
+ return 0;
+}
+
+static int stfsm_mx25_config(struct stfsm *fsm)
+{
+ uint32_t flags = fsm->info->flags;
+ uint32_t data_pads;
+ uint8_t sta;
+ int ret;
+ bool soc_reset;
+
+ /*
+ * Use default READ/WRITE sequences
+ */
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure 32-bit Address Support
+ */
+ if (flags & FLASH_FLAG_32BIT_ADDR) {
+ /* Configure 'enter_32bitaddr' FSM sequence */
+ stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
+
+ soc_reset = stfsm_can_handle_soc_reset(fsm);
+ if (soc_reset || !fsm->booted_from_spi) {
+ /* If we can handle SoC resets, we enable 32-bit address
+ * mode pervasively */
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ } else {
+ /* Else, enable/disable 32-bit addressing before/after
+ * each operation */
+ fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
+ CFG_WRITE_TOGGLE_32BIT_ADDR |
+ CFG_ERASESEC_TOGGLE_32BIT_ADDR);
+ /* It seems a small delay is required after exiting
+ * 32-bit mode following a write operation. The issue
+ * is under investigation.
+ */
+ fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
+ }
+ }
+
+ /* For QUAD mode, set 'QE' STATUS bit */
+ data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+ if (data_pads == 4) {
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
+ sta |= MX25_STATUS_QE;
+ stfsm_write_status(fsm, sta, 1);
+ }
+
+ return 0;
+}
+
+static int stfsm_n25q_config(struct stfsm *fsm)
+{
+ uint32_t flags = fsm->info->flags;
+ uint8_t vcr;
+ int ret = 0;
+ bool soc_reset;
+
+ /* Configure 'READ' sequence */
+ if (flags & FLASH_FLAG_32BIT_ADDR)
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ n25q_read4_configs);
+ else
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ n25q_read3_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "failed to prepare READ sequence with flags [0x%08x]\n",
+ flags);
+ return ret;
+ }
+
+ /* Configure 'WRITE' sequence (default configs) */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+ default_write_configs);
+ if (ret) {
+ dev_err(fsm->dev,
+ "preparing WRITE sequence using flags [0x%08x] failed\n",
+ flags);
+ return ret;
+ }
+
+ /* * Configure 'ERASE_SECTOR' sequence */
+ stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector);
+
+ /* Configure 32-bit address support */
+ if (flags & FLASH_FLAG_32BIT_ADDR) {
+ stfsm_n25q_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
+
+ soc_reset = stfsm_can_handle_soc_reset(fsm);
+ if (soc_reset || !fsm->booted_from_spi) {
+ /*
+ * If we can handle SoC resets, we enable 32-bit
+ * address mode pervasively
+ */
+ stfsm_enter_32bit_addr(fsm, 1);
+ } else {
+ /*
+ * If not, enable/disable for WRITE and ERASE
+ * operations (READ uses special commands)
+ */
+ fsm->configuration = (CFG_WRITE_TOGGLE_32BIT_ADDR |
+ CFG_ERASESEC_TOGGLE_32BIT_ADDR);
+ }
+ }
+
+ /*
+ * Configure device to use 8 dummy cycles
+ */
+ vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
+ N25Q_VCR_WRAP_CONT);
+ stfsm_wrvcr(fsm, vcr);
+
+ return 0;
+}
+
+static void stfsm_s25fl_prepare_erasesec_seq_32(struct stfsm_seq *seq)
+{
+ seq->seq_opc[1] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_SE4));
+
+ seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2 |
+ ADR_CFG_CSDEASSERT_ADD2);
+}
+
+static void stfsm_s25fl_read_dyb(struct stfsm *fsm, uint32_t offs, uint8_t *dby)
+{
+ uint32_t tmp;
+ struct stfsm_seq seq = {
+ .data_size = TRANSFER_SIZE(4),
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_DYBRD)),
+ .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2),
+ .addr1 = (offs >> 16) & 0xffff,
+ .addr2 = offs & 0xffff,
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_ADD1,
+ STFSM_INST_ADD2,
+ STFSM_INST_DATA_READ,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+ };
+
+ stfsm_load_seq(fsm, &seq);
+
+ stfsm_read_fifo(fsm, &tmp, 4);
+
+ *dby = (uint8_t)(tmp >> 24);
+
+ stfsm_wait_seq(fsm);
+}
+
+static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
+{
+ struct stfsm_seq seq = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+ SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
+ .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+ ADR_CFG_PADS_1_ADD1 |
+ ADR_CFG_CYCLES_ADD2(16) |
+ ADR_CFG_PADS_1_ADD2),
+ .status = (uint32_t)dby | STA_PADS_1 | STA_CSDEASSERT,
+ .addr1 = (offs >> 16) & 0xffff,
+ .addr2 = offs & 0xffff,
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_ADD1,
+ STFSM_INST_ADD2,
+ STFSM_INST_STA_WR1,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+ };
+
+ stfsm_load_seq(fsm, &seq);
+ stfsm_wait_seq(fsm);
+
+ stfsm_wait_busy(fsm);
+}
+
+static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
+{
+ struct stfsm_seq seq = {
+ .seq_opc[0] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(S25FL_CMD_CLSR) |
+ SEQ_OPC_CSDEASSERT),
+ .seq_opc[1] = (SEQ_OPC_PADS_1 |
+ SEQ_OPC_CYCLES(8) |
+ SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
+ SEQ_OPC_CSDEASSERT),
+ .seq = {
+ STFSM_INST_CMD1,
+ STFSM_INST_CMD2,
+ STFSM_INST_WAIT,
+ STFSM_INST_STOP,
+ },
+ .seq_cfg = (SEQ_CFG_PADS_1 |
+ SEQ_CFG_ERASE |
+ SEQ_CFG_READNOTWRITE |
+ SEQ_CFG_CSDEASSERT |
+ SEQ_CFG_STARTSEQ),
+ };
+
+ stfsm_load_seq(fsm, &seq);
+
+ stfsm_wait_seq(fsm);
+
+ return 0;
+}
+
+static int stfsm_s25fl_config(struct stfsm *fsm)
+{
+ struct flash_info *info = fsm->info;
+ uint32_t flags = info->flags;
+ uint32_t data_pads;
+ uint32_t offs;
+ uint16_t sta_wr;
+ uint8_t sr1, cr1, dyb;
+ int ret;
+
+ if (flags & FLASH_FLAG_32BIT_ADDR) {
+ /*
+ * Prepare Read/Write/Erase sequences according to S25FLxxx
+ * 32-bit address command set
+ */
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+ stfsm_s25fl_read4_configs);
+ if (ret)
+ return ret;
+
+ ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+ stfsm_s25fl_write4_configs);
+ if (ret)
+ return ret;
+
+ stfsm_s25fl_prepare_erasesec_seq_32(&stfsm_seq_erase_sector);
+
+ } else {
+ /* Use default configurations for 24-bit addressing */
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * For devices that support 'DYB' sector locking, check lock status and
+ * unlock sectors if necessary (some variants power-on with sectors
+ * locked by default)
+ */
+ if (flags & FLASH_FLAG_DYB_LOCKING) {
+ offs = 0;
+ for (offs = 0; offs < info->sector_size * info->n_sectors;) {
+ stfsm_s25fl_read_dyb(fsm, offs, &dyb);
+ if (dyb == 0x00)
+ stfsm_s25fl_write_dyb(fsm, offs, 0xff);
+
+ /* Handle bottom/top 4KiB parameter sectors */
+ if ((offs < info->sector_size * 2) ||
+ (offs >= (info->sector_size - info->n_sectors * 4)))
+ offs += 0x1000;
+ else
+ offs += 0x10000;
+ }
+ }
+
+ /* Check status of 'QE' bit */
+ data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+ stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
+ if (data_pads == 4) {
+ if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
+ /* Set 'QE' */
+ cr1 |= STFSM_S25FL_CONFIG_QE;
+
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
+ sta_wr = ((uint16_t)cr1 << 8) | sr1;
+
+ stfsm_write_status(fsm, sta_wr, 2);
+
+ stfsm_wait_busy(fsm);
+ }
+ } else {
+ if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
+ /* Clear 'QE' */
+ cr1 &= ~STFSM_S25FL_CONFIG_QE;
+
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
+ sta_wr = ((uint16_t)cr1 << 8) | sr1;
+
+ stfsm_write_status(fsm, sta_wr, 2);
+
+ stfsm_wait_busy(fsm);
+ }
+
+ }
+
+ /*
+ * S25FLxxx devices support Program and Error error flags.
+ * Configure driver to check flags and clear if necessary.
+ */
+ fsm->configuration |= CFG_S25FL_CHECK_ERROR_FLAGS;
+
+ return 0;
+}
+
+static int stfsm_w25q_config(struct stfsm *fsm)
+{
+ uint32_t data_pads;
+ uint16_t sta_wr;
+ uint8_t sta1, sta2;
+ int ret;
+
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+
+ /* If using QUAD mode, set QE STATUS bit */
+ data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+ if (data_pads == 4) {
+ stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
+ stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
+
+ sta_wr = ((uint16_t)sta2 << 8) | sta1;
+
+ sta_wr |= W25Q_STATUS_QE;
+
+ stfsm_write_status(fsm, sta_wr, 2);
+
+ stfsm_wait_busy(fsm);
+ }
+
+ return 0;
+}
+
+static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
+ uint32_t offset)
+{
+ struct stfsm_seq *seq = &fsm->stfsm_seq_read;
+ uint32_t data_pads;
+ uint32_t read_mask;
+ uint32_t size_ub;
+ uint32_t size_lb;
+ uint32_t size_mop;
+ uint32_t tmp[4];
+ uint32_t page_buf[FLASH_PAGESIZE_32];
+ uint8_t *p;
+
+ dev_dbg(fsm->dev, "reading %d bytes from 0x%08x\n", size, offset);
+
+ /* Enter 32-bit address mode, if required */
+ if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ /* Must read in multiples of 32 cycles (or 32*pads/8 Bytes) */
+ data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
+ read_mask = (data_pads << 2) - 1;
+
+ /* Handle non-aligned buf */
+ p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
+
+ /* Handle non-aligned size */
+ size_ub = (size + read_mask) & ~read_mask;
+ size_lb = size & ~read_mask;
+ size_mop = size & read_mask;
+
+ seq->data_size = TRANSFER_SIZE(size_ub);
+ seq->addr1 = (offset >> 16) & 0xffff;
+ seq->addr2 = offset & 0xffff;
+
+ stfsm_load_seq(fsm, seq);
+
+ if (size_lb)
+ stfsm_read_fifo(fsm, (uint32_t *)p, size_lb);
+
+ if (size_mop) {
+ stfsm_read_fifo(fsm, tmp, read_mask + 1);
+ memcpy(p + size_lb, &tmp, size_mop);
+ }
+
+ /* Handle non-aligned buf */
+ if ((uint32_t)buf & 0x3)
+ memcpy(buf, page_buf, size);
+
+ /* Wait for sequence to finish */
+ stfsm_wait_seq(fsm);
+
+ stfsm_clear_fifo(fsm);
+
+ /* Exit 32-bit address mode, if required */
+ if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 0);
+
+ return 0;
+}
+
+static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
+ uint32_t size, uint32_t offset)
+{
+ struct stfsm_seq *seq = &fsm->stfsm_seq_write;
+ uint32_t data_pads;
+ uint32_t write_mask;
+ uint32_t size_ub;
+ uint32_t size_lb;
+ uint32_t size_mop;
+ uint32_t tmp[4];
+ uint32_t page_buf[FLASH_PAGESIZE_32];
+ uint8_t *t = (uint8_t *)&tmp;
+ const uint8_t *p;
+ int ret;
+ int i;
+
+ dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset);
+
+ /* Enter 32-bit address mode, if required */
+ if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ /* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */
+ data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
+ write_mask = (data_pads << 2) - 1;
+
+ /* Handle non-aligned buf */
+ if ((uint32_t)buf & 0x3) {
+ memcpy(page_buf, buf, size);
+ p = (uint8_t *)page_buf;
+ } else {
+ p = buf;
+ }
+
+ /* Handle non-aligned size */
+ size_ub = (size + write_mask) & ~write_mask;
+ size_lb = size & ~write_mask;
+ size_mop = size & write_mask;
+
+ seq->data_size = TRANSFER_SIZE(size_ub);
+ seq->addr1 = (offset >> 16) & 0xffff;
+ seq->addr2 = offset & 0xffff;
+
+ /* Need to set FIFO to write mode, before writing data to FIFO (see
+ * GNBvb79594)
+ */
+ writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG);
+
+ /*
+ * Before writing data to the FIFO, apply a small delay to allow a
+ * potential change of FIFO direction to complete.
+ */
+ if (fsm->fifo_dir_delay == 0)
+ readl(fsm->base + SPI_FAST_SEQ_CFG);
+ else
+ udelay(fsm->fifo_dir_delay);
+
+
+ /* Write data to FIFO, before starting sequence (see GNBvd79593) */
+ if (size_lb) {
+ stfsm_write_fifo(fsm, (uint32_t *)p, size_lb);
+ p += size_lb;
+ }
+
+ /* Handle non-aligned size */
+ if (size_mop) {
+ memset(t, 0xff, write_mask + 1); /* fill with 0xff's */
+ for (i = 0; i < size_mop; i++)
+ t[i] = *p++;
+
+ stfsm_write_fifo(fsm, tmp, write_mask + 1);
+ }
+
+ /* Start sequence */
+ stfsm_load_seq(fsm, seq);
+
+ /* Wait for sequence to finish */
+ stfsm_wait_seq(fsm);
+
+ /* Wait for completion */
+ ret = stfsm_wait_busy(fsm);
+ if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS)
+ stfsm_s25fl_clear_status_reg(fsm);
+
+ /* Exit 32-bit address mode, if required */
+ if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
+ stfsm_enter_32bit_addr(fsm, 0);
+ if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
+ udelay(1);
+ }
+
+ return 0;
+}
+
+/*
+ * Read an address range from the flash chip. The address range
+ * may be any size provided it is within the physical boundaries.
+ */
+static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+ uint32_t bytes;
+
+ dev_dbg(fsm->dev, "%s from 0x%08x, len %zd\n",
+ __func__, (u32)from, len);
+
+ mutex_lock(&fsm->lock);
+
+ while (len > 0) {
+ bytes = min_t(size_t, len, FLASH_PAGESIZE);
+
+ stfsm_read(fsm, buf, bytes, from);
+
+ buf += bytes;
+ from += bytes;
+ len -= bytes;
+
+ *retlen += bytes;
+ }
+
+ mutex_unlock(&fsm->lock);
+
+ return 0;
+}
+
+static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset)
+{
+ struct stfsm_seq *seq = &stfsm_seq_erase_sector;
+ int ret;
+
+ dev_dbg(fsm->dev, "erasing sector at 0x%08x\n", offset);
+
+ /* Enter 32-bit address mode, if required */
+ if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 1);
+
+ seq->addr1 = (offset >> 16) & 0xffff;
+ seq->addr2 = offset & 0xffff;
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ /* Wait for completion */
+ ret = stfsm_wait_busy(fsm);
+ if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS)
+ stfsm_s25fl_clear_status_reg(fsm);
+
+ /* Exit 32-bit address mode, if required */
+ if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR)
+ stfsm_enter_32bit_addr(fsm, 0);
+
+ return ret;
+}
+
+static int stfsm_erase_chip(struct stfsm *fsm)
+{
+ const struct stfsm_seq *seq = &stfsm_seq_erase_chip;
+
+ dev_dbg(fsm->dev, "erasing chip\n");
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_wait_seq(fsm);
+
+ return stfsm_wait_busy(fsm);
+}
+
+/*
+ * Write an address range to the flash chip. Data must be written in
+ * FLASH_PAGESIZE chunks. The address range may be any size provided
+ * it is within the physical boundaries.
+ */
+static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+
+ u32 page_offs;
+ u32 bytes;
+ uint8_t *b = (uint8_t *)buf;
+ int ret = 0;
+
+ dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len);
+
+ /* Offset within page */
+ page_offs = to % FLASH_PAGESIZE;
+
+ mutex_lock(&fsm->lock);
+
+ while (len) {
+ /* Write up to page boundary */
+ bytes = min(FLASH_PAGESIZE - page_offs, len);
+
+ ret = stfsm_write(fsm, b, bytes, to);
+ if (ret)
+ goto out1;
+
+ b += bytes;
+ len -= bytes;
+ to += bytes;
+
+ /* We are now page-aligned */
+ page_offs = 0;
+
+ *retlen += bytes;
+
+ }
+
+out1:
+ mutex_unlock(&fsm->lock);
+
+ return ret;
+}
+
+/*
+ * Erase an address range on the flash chip. The address range may extend
+ * one or more erase sectors. Return an error is there is a problem erasing.
+ */
+static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+ u32 addr, len;
+ int ret;
+
+ dev_dbg(fsm->dev, "%s at 0x%llx, len %lld\n", __func__,
+ (long long)instr->addr, (long long)instr->len);
+
+ addr = instr->addr;
+ len = instr->len;
+
+ mutex_lock(&fsm->lock);
+
+ /* Whole-chip erase? */
+ if (len == mtd->size) {
+ ret = stfsm_erase_chip(fsm);
+ if (ret)
+ goto out1;
+ } else {
+ while (len) {
+ ret = stfsm_erase_sector(fsm, addr);
+ if (ret)
+ goto out1;
+
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+ }
+
+ mutex_unlock(&fsm->lock);
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+
+out1:
+ instr->state = MTD_ERASE_FAILED;
+ mutex_unlock(&fsm->lock);
+
+ return ret;
+}
+
+static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec)
+{
+ const struct stfsm_seq *seq = &stfsm_seq_read_jedec;
+ uint32_t tmp[2];
+
+ stfsm_load_seq(fsm, seq);
+
+ stfsm_read_fifo(fsm, tmp, 8);
+
+ memcpy(jedec, tmp, 5);
+
+ stfsm_wait_seq(fsm);
+}
+
+static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm)
+{
+ struct flash_info *info;
+ u16 ext_jedec;
+ u32 jedec;
+ u8 id[5];
+
+ stfsm_read_jedec(fsm, id);
+
+ jedec = id[0] << 16 | id[1] << 8 | id[2];
+ /*
+ * JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ */
+ ext_jedec = id[3] << 8 | id[4];
+
+ dev_dbg(fsm->dev, "JEDEC = 0x%08x [%02x %02x %02x %02x %02x]\n",
+ jedec, id[0], id[1], id[2], id[3], id[4]);
+
+ for (info = flash_types; info->name; info++) {
+ if (info->jedec_id == jedec) {
+ if (info->ext_id && info->ext_id != ext_jedec)
+ continue;
+ return info;
+ }
+ }
+ dev_err(fsm->dev, "Unrecognized JEDEC id %06x\n", jedec);
+
+ return NULL;
+}
+
+static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode)
+{
+ int ret, timeout = 10;
+
+ /* Wait for controller to accept mode change */
+ while (--timeout) {
+ ret = readl(fsm->base + SPI_STA_MODE_CHANGE);
+ if (ret & 0x1)
+ break;
+ udelay(1);
+ }
+
+ if (!timeout)
+ return -EBUSY;
+
+ writel(mode, fsm->base + SPI_MODESELECT);
+
+ return 0;
+}
+
+static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq)
+{
+ uint32_t emi_freq;
+ uint32_t clk_div;
+
+ /* TODO: Make this dynamic */
+ emi_freq = STFSM_DEFAULT_EMI_FREQ;
+
+ /*
+ * Calculate clk_div - values between 2 and 128
+ * Multiple of 2, rounded up
+ */
+ clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq);
+ if (clk_div < 2)
+ clk_div = 2;
+ else if (clk_div > 128)
+ clk_div = 128;
+
+ /*
+ * Determine a suitable delay for the IP to complete a change of
+ * direction of the FIFO. The required delay is related to the clock
+ * divider used. The following heuristics are based on empirical tests,
+ * using a 100MHz EMI clock.
+ */
+ if (clk_div <= 4)
+ fsm->fifo_dir_delay = 0;
+ else if (clk_div <= 10)
+ fsm->fifo_dir_delay = 1;
+ else
+ fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10);
+
+ dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n",
+ emi_freq, spi_freq, clk_div);
+
+ writel(clk_div, fsm->base + SPI_CLOCKDIV);
+}
+
+static int stfsm_init(struct stfsm *fsm)
+{
+ int ret;
+
+ /* Perform a soft reset of the FSM controller */
+ writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG);
+ udelay(1);
+ writel(0, fsm->base + SPI_FAST_SEQ_CFG);
+
+ /* Set clock to 'safe' frequency initially */
+ stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ);
+
+ /* Switch to FSM */
+ ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM);
+ if (ret)
+ return ret;
+
+ /* Set timing parameters */
+ writel(SPI_CFG_DEVICE_ST |
+ SPI_CFG_DEFAULT_MIN_CS_HIGH |
+ SPI_CFG_DEFAULT_CS_SETUPHOLD |
+ SPI_CFG_DEFAULT_DATA_HOLD,
+ fsm->base + SPI_CONFIGDATA);
+ writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
+
+ /* Clear FIFO, just in case */
+ stfsm_clear_fifo(fsm);
+
+ return 0;
+}
+
+static void stfsm_fetch_platform_configs(struct platform_device *pdev)
+{
+ struct stfsm *fsm = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct regmap *regmap;
+ uint32_t boot_device_reg;
+ uint32_t boot_device_spi;
+ uint32_t boot_device; /* Value we read from *boot_device_reg */
+ int ret;
+
+ /* Booting from SPI NOR Flash is the default */
+ fsm->booted_from_spi = true;
+
+ regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(regmap))
+ goto boot_device_fail;
+
+ fsm->reset_signal = of_property_read_bool(np, "st,reset-signal");
+
+ fsm->reset_por = of_property_read_bool(np, "st,reset-por");
+
+ /* Where in the syscon the boot device information lives */
+ ret = of_property_read_u32(np, "st,boot-device-reg", &boot_device_reg);
+ if (ret)
+ goto boot_device_fail;
+
+ /* Boot device value when booted from SPI NOR */
+ ret = of_property_read_u32(np, "st,boot-device-spi", &boot_device_spi);
+ if (ret)
+ goto boot_device_fail;
+
+ ret = regmap_read(regmap, boot_device_reg, &boot_device);
+ if (ret)
+ goto boot_device_fail;
+
+ if (boot_device != boot_device_spi)
+ fsm->booted_from_spi = false;
+
+ return;
+
+boot_device_fail:
+ dev_warn(&pdev->dev,
+ "failed to fetch boot device, assuming boot from SPI\n");
+}
+
+static int stfsm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mtd_part_parser_data ppdata;
+ struct flash_info *info;
+ struct resource *res;
+ struct stfsm *fsm;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No DT found\n");
+ return -EINVAL;
+ }
+ ppdata.of_node = np;
+
+ fsm = devm_kzalloc(&pdev->dev, sizeof(*fsm), GFP_KERNEL);
+ if (!fsm)
+ return -ENOMEM;
+
+ fsm->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, fsm);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Resource not found\n");
+ return -ENODEV;
+ }
+
+ fsm->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsm->base)) {
+ dev_err(&pdev->dev,
+ "Failed to reserve memory region %pR\n", res);
+ return PTR_ERR(fsm->base);
+ }
+
+ mutex_init(&fsm->lock);
+
+ ret = stfsm_init(fsm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialise FSM Controller\n");
+ return ret;
+ }
+
+ stfsm_fetch_platform_configs(pdev);
+
+ /* Detect SPI FLASH device */
+ info = stfsm_jedec_probe(fsm);
+ if (!info)
+ return -ENODEV;
+ fsm->info = info;
+
+ /* Use device size to determine address width */
+ if (info->sector_size * info->n_sectors > 0x1000000)
+ info->flags |= FLASH_FLAG_32BIT_ADDR;
+
+ /*
+ * Configure READ/WRITE/ERASE sequences according to platform and
+ * device flags.
+ */
+ if (info->config) {
+ ret = info->config(fsm);
+ if (ret)
+ return ret;
+ } else {
+ ret = stfsm_prepare_rwe_seqs_default(fsm);
+ if (ret)
+ return ret;
+ }
+
+ fsm->mtd.name = info->name;
+ fsm->mtd.dev.parent = &pdev->dev;
+ fsm->mtd.type = MTD_NORFLASH;
+ fsm->mtd.writesize = 4;
+ fsm->mtd.writebufsize = fsm->mtd.writesize;
+ fsm->mtd.flags = MTD_CAP_NORFLASH;
+ fsm->mtd.size = info->sector_size * info->n_sectors;
+ fsm->mtd.erasesize = info->sector_size;
+
+ fsm->mtd._read = stfsm_mtd_read;
+ fsm->mtd._write = stfsm_mtd_write;
+ fsm->mtd._erase = stfsm_mtd_erase;
+
+ dev_info(&pdev->dev,
+ "Found serial flash device: %s\n"
+ " size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n",
+ info->name,
+ (long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20),
+ fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10));
+
+ return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0);
+}
+
+static int stfsm_remove(struct platform_device *pdev)
+{
+ struct stfsm *fsm = platform_get_drvdata(pdev);
+
+ return mtd_device_unregister(&fsm->mtd);
+}
+
+static struct of_device_id stfsm_match[] = {
+ { .compatible = "st,spi-fsm", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stfsm_match);
+
+static struct platform_driver stfsm_driver = {
+ .probe = stfsm_probe,
+ .remove = stfsm_remove,
+ .driver = {
+ .name = "st-spi-fsm",
+ .owner = THIS_MODULE,
+ .of_match_table = stfsm_match,
+ },
+};
+module_platform_driver(stfsm_driver);
+
+MODULE_AUTHOR("Angus Clark <angus.clark@st.com>");
+MODULE_DESCRIPTION("ST SPI FSM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 4adc0374fb6b..487e64f411a5 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -30,7 +30,6 @@
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h>
#include <linux/mtd/inftl.h>
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index d38b6460d505..018c75faadb3 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -55,10 +55,8 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
int i, j;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+ if (!mtd)
return NULL;
- }
mtd->priv = map;
mtd->type = MTD_NORFLASH;
diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c
index 45abed67f1ef..69f2112340b1 100644
--- a/drivers/mtd/lpddr/qinfo_probe.c
+++ b/drivers/mtd/lpddr/qinfo_probe.c
@@ -135,11 +135,8 @@ static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr)
{
lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL);
- if (!lpddr->qinfo) {
- printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n",
- map->name);
+ if (!lpddr->qinfo)
return 0;
- }
/* Get the ManuID */
lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID));
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 310dc7c93425..fce23fe043f7 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -66,11 +66,11 @@ config MTD_PHYSMAP_BANKWIDTH
used internally by the CFI drivers.
config MTD_PHYSMAP_OF
- tristate "Flash device in physical memory map based on OF description"
- depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
+ tristate "Memory device in physical memory map based on OF description"
+ depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM)
help
- This provides a 'mapping' driver which allows the NOR Flash and
- ROM driver code to communicate with chips which are mapped
+ This provides a 'mapping' driver which allows the NOR Flash, ROM
+ and RAM driver code to communicate with chips which are mapped
physically into the CPU's memory. The mapping description here is
taken from OF device tree.
@@ -124,7 +124,7 @@ config MTD_NETSC520
config MTD_TS5500
tristate "JEDEC Flash device mapped on Technologic Systems TS-5500"
- depends on X86
+ depends on TS5500 || COMPILE_TEST
select MTD_JEDECPROBE
select MTD_CFI_AMDSTD
help
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
index 5434d8ded015..6ea51e549045 100644
--- a/drivers/mtd/maps/bfin-async-flash.c
+++ b/drivers/mtd/maps/bfin-async-flash.c
@@ -14,7 +14,6 @@
* Licensed under the GPL-2 or later.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 1adba86474a5..a4c477b9fdd6 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -14,7 +14,6 @@
*/
#include <linux/gpio.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
index 46d195fca942..5ab71f0e1bcd 100644
--- a/drivers/mtd/maps/intel_vr_nor.c
+++ b/drivers/mtd/maps/intel_vr_nor.c
@@ -31,7 +31,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
index d6b2451eab1d..6a589f1e2880 100644
--- a/drivers/mtd/maps/ixp4xx.c
+++ b/drivers/mtd/maps/ixp4xx.c
@@ -16,7 +16,6 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index 93c507a6f862..7aa682cd4d7e 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c
index 98bb5d5375d7..cadfbe051873 100644
--- a/drivers/mtd/maps/latch-addr-flash.c
+++ b/drivers/mtd/maps/latch-addr-flash.c
@@ -10,7 +10,6 @@
* kind, whether express or implied.
*/
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c
index 36da518915b5..eb0242e0b2d9 100644
--- a/drivers/mtd/maps/pci.c
+++ b/drivers/mtd/maps/pci.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index d11109762ac5..217c25d7381b 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 10196f5a897d..d597e89f2692 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -23,7 +23,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
@@ -138,7 +137,6 @@ static int platram_probe(struct platform_device *pdev)
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
- dev_err(&pdev->dev, "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
index 9aad854fe912..cb4d92eea9fe 100644
--- a/drivers/mtd/maps/pxa2xx-flash.c
+++ b/drivers/mtd/maps/pxa2xx-flash.c
@@ -13,7 +13,6 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c
index 93525121d69d..146b6047ed2b 100644
--- a/drivers/mtd/maps/rbtx4939-flash.c
+++ b/drivers/mtd/maps/rbtx4939-flash.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c
index 3051c4c36240..b7a22a612a46 100644
--- a/drivers/mtd/maps/scb2_flash.c
+++ b/drivers/mtd/maps/scb2_flash.c
@@ -47,7 +47,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 39cc4181f025..b6f1aac3510c 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 5073cbc796d8..0b2ccb68c0d0 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -30,7 +30,6 @@
#include <linux/blkpg.h>
#include <linux/spinlock.h>
#include <linux/hdreg.h>
-#include <linux/init.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 2147e733533b..7d4e7b9da3a1 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
default:
ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
}
+
+ /*
+ * Return -ENOSPC only if no data could be written at all.
+ * Otherwise just return the number of bytes that actually
+ * have been written.
+ */
+ if ((ret == -ENOSPC) && (total_retlen))
+ break;
+
if (!ret) {
*ppos += retlen;
total_retlen += retlen;
@@ -889,25 +898,26 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
case OTPGETREGIONINFO:
{
struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
+ size_t retlen;
if (!buf)
return -ENOMEM;
switch (mfi->mode) {
case MTD_FILE_MODE_OTP_FACTORY:
- ret = mtd_get_fact_prot_info(mtd, buf, 4096);
+ ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf);
break;
case MTD_FILE_MODE_OTP_USER:
- ret = mtd_get_user_prot_info(mtd, buf, 4096);
+ ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf);
break;
default:
ret = -EINVAL;
break;
}
- if (ret >= 0) {
+ if (!ret) {
if (cmd == OTPGETREGIONCOUNT) {
- int nbr = ret / sizeof(struct otp_info);
+ int nbr = retlen / sizeof(struct otp_info);
ret = copy_to_user(argp, &nbr, sizeof(int));
} else
- ret = copy_to_user(argp, buf, ret);
+ ret = copy_to_user(argp, buf, retlen);
if (ret)
ret = -EFAULT;
}
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 34c0b16aed5c..d201feeb3ca6 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -883,14 +883,14 @@ EXPORT_SYMBOL_GPL(mtd_read_oob);
* devices. The user data is one time programmable but the factory data is read
* only.
*/
-int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len)
+int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+ struct otp_info *buf)
{
if (!mtd->_get_fact_prot_info)
return -EOPNOTSUPP;
if (!len)
return 0;
- return mtd->_get_fact_prot_info(mtd, buf, len);
+ return mtd->_get_fact_prot_info(mtd, len, retlen, buf);
}
EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
@@ -906,14 +906,14 @@ int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
}
EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
-int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len)
+int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+ struct otp_info *buf)
{
if (!mtd->_get_user_prot_info)
return -EOPNOTSUPP;
if (!len)
return 0;
- return mtd->_get_user_prot_info(mtd, buf, len);
+ return mtd->_get_user_prot_info(mtd, len, retlen, buf);
}
EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
@@ -932,12 +932,22 @@ EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, u_char *buf)
{
+ int ret;
+
*retlen = 0;
if (!mtd->_write_user_prot_reg)
return -EOPNOTSUPP;
if (!len)
return 0;
- return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+ ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+ if (ret)
+ return ret;
+
+ /*
+ * If no data could be written at all, we are out of memory and
+ * must return -ENOSPC.
+ */
+ return (*retlen) ? 0 : -ENOSPC;
}
EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 3c7d6d7623c1..1ca9aec141ff 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -150,11 +150,12 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
retlen, buf);
}
-static int part_get_user_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
struct mtd_part *part = PART(mtd);
- return part->master->_get_user_prot_info(part->master, buf, len);
+ return part->master->_get_user_prot_info(part->master, len, retlen,
+ buf);
}
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
@@ -165,11 +166,12 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
retlen, buf);
}
-static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
- size_t len)
+static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
struct mtd_part *part = PART(mtd);
- return part->master->_get_fact_prot_info(part->master, buf, len);
+ return part->master->_get_fact_prot_info(part->master, len, retlen,
+ buf);
}
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index a4bee41ad5cb..f1cf503517fd 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -460,6 +460,8 @@ config MTD_NAND_MXC
config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+ depends on HAS_IOMEM
+ depends on HAS_DMA
help
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL.
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 8611eb4b45fc..4936e9e0002f 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -17,7 +17,6 @@
*/
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index c36e9b84487c..4ce181a35bcd 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -430,7 +430,7 @@ err_dma:
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
err_buf:
if (err != 0)
- dev_warn(host->dev, "Fall back to CPU I/O\n");
+ dev_dbg(host->dev, "Fall back to CPU I/O\n");
return err;
}
@@ -1220,6 +1220,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
goto err;
}
+ nand_chip->options |= NAND_NO_SUBPAGE_WRITE;
nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
@@ -1659,8 +1660,8 @@ static void nfc_select_chip(struct mtd_info *mtd, int chip)
nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
}
-static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
- unsigned int *addr1234, unsigned int *cycle0)
+static int nfc_make_addr(struct mtd_info *mtd, int command, int column,
+ int page_addr, unsigned int *addr1234, unsigned int *cycle0)
{
struct nand_chip *chip = mtd->priv;
@@ -1674,7 +1675,8 @@ static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
*addr1234 = 0;
if (column != -1) {
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
addr_bytes[acycle++] = column & 0xff;
if (mtd->writesize > 512)
@@ -1787,8 +1789,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
}
if (do_addr)
- acycle = nfc_make_addr(mtd, column, page_addr, &addr1234,
- &cycle0);
+ acycle = nfc_make_addr(mtd, command, column, page_addr,
+ &addr1234, &cycle0);
nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 2880d888cfc5..bc5c518828d2 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -11,7 +11,6 @@
#include <linux/slab.h>
#include <linux/gpio.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
@@ -308,7 +307,8 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16)
+ if (this->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
ctx->write_byte(mtd, column);
}
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 94f55dbde995..b7a24946ca26 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -37,7 +37,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index f2f64addb5e8..4e66726da9aa 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -627,6 +627,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
struct cafe_priv *cafe;
uint32_t ctrl;
int err = 0;
+ int old_dma;
+ struct nand_buffers *nbuf;
/* Very old versions shared the same PCI ident for all three
functions on the chip. Verify the class too... */
@@ -655,13 +657,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
err = -ENOMEM;
goto out_free_mtd;
}
- cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
- &cafe->dmaaddr, GFP_KERNEL);
- if (!cafe->dmabuf) {
- err = -ENOMEM;
- goto out_ior;
- }
- cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
if (!cafe->rs) {
@@ -721,7 +716,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
"CAFE NAND", mtd);
if (err) {
dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
- goto out_free_dma;
+ goto out_ior;
}
/* Disable master reset, enable NAND clock */
@@ -735,6 +730,32 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+ /* Enable NAND IRQ in global IRQ mask register */
+ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+ cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
+ cafe_readl(cafe, GLOBAL_CTRL),
+ cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+ /* Do not use the DMA for the nand_scan_ident() */
+ old_dma = usedma;
+ usedma = 0;
+
+ /* Scan to find existence of the device */
+ if (nand_scan_ident(mtd, 2, NULL)) {
+ err = -ENXIO;
+ goto out_irq;
+ }
+
+ cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev,
+ 2112 + sizeof(struct nand_buffers) +
+ mtd->writesize + mtd->oobsize,
+ &cafe->dmaaddr, GFP_KERNEL);
+ if (!cafe->dmabuf) {
+ err = -ENOMEM;
+ goto out_irq;
+ }
+ cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112;
+
/* Set up DMA address */
cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
if (sizeof(cafe->dmaaddr) > 4)
@@ -746,16 +767,13 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
- /* Enable NAND IRQ in global IRQ mask register */
- cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
- cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
- cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+ /* this driver does not need the @ecccalc and @ecccode */
+ nbuf->ecccalc = NULL;
+ nbuf->ecccode = NULL;
+ nbuf->databuf = (uint8_t *)(nbuf + 1);
- /* Scan to find existence of the device */
- if (nand_scan_ident(mtd, 2, NULL)) {
- err = -ENXIO;
- goto out_irq;
- }
+ /* Restore the DMA flag */
+ usedma = old_dma;
cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
if (mtd->writesize == 2048)
@@ -773,7 +791,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
} else {
printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
mtd->writesize);
- goto out_irq;
+ goto out_free_dma;
}
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
cafe->nand.ecc.size = mtd->writesize;
@@ -790,7 +808,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
err = nand_scan_tail(mtd);
if (err)
- goto out_irq;
+ goto out_free_dma;
pci_set_drvdata(pdev, mtd);
@@ -799,12 +817,15 @@ static int cafe_nand_probe(struct pci_dev *pdev,
goto out;
+ out_free_dma:
+ dma_free_coherent(&cafe->pdev->dev,
+ 2112 + sizeof(struct nand_buffers) +
+ mtd->writesize + mtd->oobsize,
+ cafe->dmabuf, cafe->dmaaddr);
out_irq:
/* Disable NAND IRQ in global IRQ mask register */
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
free_irq(pdev->irq, mtd);
- out_free_dma:
- dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
out_ior:
pci_iounmap(pdev, cafe->mmio);
out_free_mtd:
@@ -824,7 +845,10 @@ static void cafe_nand_remove(struct pci_dev *pdev)
nand_release(mtd);
free_rs(cafe->rs);
pci_iounmap(pdev, cafe->mmio);
- dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+ dma_free_coherent(&cafe->pdev->dev,
+ 2112 + sizeof(struct nand_buffers) +
+ mtd->writesize + mtd->oobsize,
+ cafe->dmabuf, cafe->dmaaddr);
kfree(mtd);
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index a4989ec6292e..4615d79fc93f 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -24,7 +24,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@@ -746,28 +745,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
goto err_clk_enable;
}
- /*
- * Setup Async configuration register in case we did not boot from
- * NAND and so bootloader did not bother to set it up.
- */
- val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4);
-
- /* Extended Wait is not valid and Select Strobe mode is not used */
- val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK);
- if (info->chip.options & NAND_BUSWIDTH_16)
- val |= 0x1;
-
- davinci_nand_writel(info, A1CR_OFFSET + info->core_chipsel * 4, val);
-
- ret = 0;
- if (info->timing)
- ret = davinci_aemif_setup_timing(info->timing, info->base,
- info->core_chipsel);
- if (ret < 0) {
- dev_dbg(&pdev->dev, "NAND timing values setup fail\n");
- goto err;
- }
-
spin_lock_irq(&davinci_nand_lock);
/* put CSxNAND into NAND mode */
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index babb02c4b220..35cb17f57800 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -30,24 +30,6 @@ struct denali_dt {
struct clk *clk;
};
-static void __iomem *request_and_map(struct device *dev,
- const struct resource *res)
-{
- void __iomem *ptr;
-
- if (!devm_request_mem_region(dev, res->start, resource_size(res),
- "denali-dt")) {
- dev_err(dev, "unable to request %s\n", res->name);
- return NULL;
- }
-
- ptr = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (!ptr)
- dev_err(dev, "ioremap_nocache of %s failed!", res->name);
-
- return ptr;
-}
-
static const struct of_device_id denali_nand_dt_ids[] = {
{ .compatible = "denali,denali-nand-dt" },
{ /* sentinel */ }
@@ -78,13 +60,6 @@ static int denali_dt_probe(struct platform_device *ofdev)
return -ENOMEM;
denali = &dt->denali;
- denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
- nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
- if (!denali_reg || !nand_data) {
- dev_err(&ofdev->dev, "resources not completely defined\n");
- return -EINVAL;
- }
-
denali->platform = DT;
denali->dev = &ofdev->dev;
denali->irq = platform_get_irq(ofdev, 0);
@@ -93,13 +68,15 @@ static int denali_dt_probe(struct platform_device *ofdev)
return denali->irq;
}
- denali->flash_reg = request_and_map(&ofdev->dev, denali_reg);
- if (!denali->flash_reg)
- return -ENOMEM;
+ denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
+ denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
+ if (IS_ERR(denali->flash_reg))
+ return PTR_ERR(denali->flash_reg);
- denali->flash_mem = request_and_map(&ofdev->dev, nand_data);
- if (!denali->flash_mem)
- return -ENOMEM;
+ nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
+ denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
+ if (IS_ERR(denali->flash_mem))
+ return PTR_ERR(denali->flash_mem);
if (!of_property_read_u32(ofdev->dev.of_node,
"dma-mask", (u32 *)&denali_dma_mask)) {
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index fec31d71b84e..f68a7bccecdc 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -698,7 +698,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16)
+ if (this->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
WriteDOC(column, docptr, Mplus_FlashAddress);
}
@@ -1438,7 +1439,7 @@ static int __init doc_probe(unsigned long physadr)
int reg, len, numchips;
int ret = 0;
- if (!request_mem_region(physadr, DOC_IOREMAP_LEN, NULL))
+ if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip"))
return -EBUSY;
virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
if (!virtadr) {
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index bcf60800c3ce..ec549cd9849f 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 50d9161c4faf..cb45d2f8e208 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -22,7 +22,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/slab.h>
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c
index 8e6148aa4539..117ce333fdd4 100644
--- a/drivers/mtd/nand/gpio.c
+++ b/drivers/mtd/nand/gpio.c
@@ -18,7 +18,6 @@
#include <linux/kernel.h>
#include <linux/err.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index ca6369fe91ff..bb77f750e75a 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -27,6 +27,7 @@
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include "gpmi-nand.h"
+#include "bch-regs.h"
/* Resource names for the GPMI NAND driver. */
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
@@ -985,7 +986,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
int ret;
dev_dbg(this->dev, "page number is : %d\n", page);
- ret = read_page_prepare(this, buf, mtd->writesize,
+ ret = read_page_prepare(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
&payload_virt, &payload_phys);
@@ -999,7 +1000,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* go! */
ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
- read_page_end(this, buf, mtd->writesize,
+ read_page_end(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
payload_virt, payload_phys);
@@ -1041,7 +1042,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
}
- read_page_swap_end(this, buf, mtd->writesize,
+ read_page_swap_end(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
payload_virt, payload_phys);
@@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return max_bitflips;
}
+/* Fake a virtual small page for the subpage read */
+static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offs, uint32_t len, uint8_t *buf, int page)
+{
+ struct gpmi_nand_data *this = chip->priv;
+ void __iomem *bch_regs = this->resources.bch_regs;
+ struct bch_geometry old_geo = this->bch_geometry;
+ struct bch_geometry *geo = &this->bch_geometry;
+ int size = chip->ecc.size; /* ECC chunk size */
+ int meta, n, page_size;
+ u32 r1_old, r2_old, r1_new, r2_new;
+ unsigned int max_bitflips;
+ int first, last, marker_pos;
+ int ecc_parity_size;
+ int col = 0;
+
+ /* The size of ECC parity */
+ ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
+
+ /* Align it with the chunk size */
+ first = offs / size;
+ last = (offs + len - 1) / size;
+
+ /*
+ * Find the chunk which contains the Block Marker. If this chunk is
+ * in the range of [first, last], we have to read out the whole page.
+ * Why? since we had swapped the data at the position of Block Marker
+ * to the metadata which is bound with the chunk 0.
+ */
+ marker_pos = geo->block_mark_byte_offset / size;
+ if (last >= marker_pos && first <= marker_pos) {
+ dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n",
+ page, first, last, marker_pos);
+ return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+ }
+
+ meta = geo->metadata_size;
+ if (first) {
+ col = meta + (size + ecc_parity_size) * first;
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
+
+ meta = 0;
+ buf = buf + first * size;
+ }
+
+ /* Save the old environment */
+ r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
+ r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* change the BCH registers and bch_geometry{} */
+ n = last - first + 1;
+ page_size = meta + (size + ecc_parity_size) * n;
+
+ r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
+ BM_BCH_FLASH0LAYOUT0_META_SIZE);
+ r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
+ writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
+ r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
+ writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ geo->ecc_chunk_count = n;
+ geo->payload_size = n * size;
+ geo->page_size = page_size;
+ geo->auxiliary_status_offset = ALIGN(meta, 4);
+
+ dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
+ page, offs, len, col, first, n, page_size);
+
+ /* Read the subpage now */
+ this->swap_block_mark = false;
+ max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+
+ /* Restore */
+ writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
+ writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
+ this->bch_geometry = old_geo;
+ this->swap_block_mark = true;
+
+ return max_bitflips;
+}
+
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
@@ -1566,6 +1651,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->layout = &gpmi_hw_ecclayout;
/*
+ * We only enable the subpage read when:
+ * (1) the chip is imx6, and
+ * (2) the size of the ECC parity is byte aligned.
+ */
+ if (GPMI_IS_MX6Q(this) &&
+ ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
+ ecc->read_subpage = gpmi_ecc_read_subpage;
+ chip->options |= NAND_SUBPAGE_READ;
+ }
+
+ /*
* Can we enable the extra features? such as EDO or Sync mode.
*
* We do not check the return value now. That's means if we fail in
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 31ee7cfbc12b..e78841a2dcc3 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -30,7 +30,6 @@
#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/err.h>
-#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index e9a4835c4dd9..dba262bf766f 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1501,6 +1501,8 @@ static int mxcnd_probe(struct platform_device *pdev)
init_completion(&host->op_completion);
host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0)
+ return host->irq;
/*
* Use host->devtype_data->irq_control() here instead of irq_control()
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 9715a7ba164a..9d01c4df838c 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -589,7 +589,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
chip->cmd_ctrl(mtd, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
@@ -680,7 +681,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
chip->cmd_ctrl(mtd, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
@@ -1160,9 +1162,11 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @data_offs: offset of requested data within the page
* @readlen: data length
* @bufpoi: buffer to store read data
+ * @page: page number to read
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+ int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1170,13 +1174,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
int data_col_addr, i, gaps = 0;
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
- int index = 0;
+ int index;
unsigned int max_bitflips = 0;
/* Column address within the page aligned to ECC size (256bytes) */
start_step = data_offs / chip->ecc.size;
end_step = (data_offs + readlen - 1) / chip->ecc.size;
num_steps = end_step - start_step + 1;
+ index = start_step * chip->ecc.bytes;
/* Data size aligned to ECC ecc.size */
datafrag_len = num_steps * chip->ecc.size;
@@ -1213,8 +1218,6 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
* Send the command to read the particular ECC bytes take care
* about buswidth alignment in read_buf.
*/
- index = start_step * chip->ecc.bytes;
-
aligned_pos = eccpos[index] & ~(busw - 1);
aligned_len = eccfrag_len;
if (eccpos[index] & (busw - 1))
@@ -1538,7 +1541,8 @@ read_retry:
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
ret = chip->ecc.read_subpage(mtd, chip,
- col, bytes, bufpoi);
+ col, bytes, bufpoi,
+ page);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
oob_required, page);
@@ -2000,7 +2004,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
oob += chip->ecc.prepad;
}
- chip->read_buf(mtd, oob, eccbytes);
+ chip->write_buf(mtd, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
@@ -3063,7 +3067,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
int *busw)
{
struct nand_onfi_params *p = &chip->onfi_params;
- int i;
+ int i, j;
int val;
/* Try ONFI for unknown chip or LP */
@@ -3072,18 +3076,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
- /*
- * ONFI must be probed in 8-bit mode or with NAND_BUSWIDTH_AUTO, not
- * with NAND_BUSWIDTH_16
- */
- if (chip->options & NAND_BUSWIDTH_16) {
- pr_err("ONFI cannot be probed in 16-bit mode; aborting\n");
- return 0;
- }
-
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
- chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+ for (j = 0; j < sizeof(*p); j++)
+ ((uint8_t *)p)[j] = chip->read_byte(mtd);
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
break;
@@ -3169,6 +3165,87 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
}
/*
+ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
+ int *busw)
+{
+ struct nand_jedec_params *p = &chip->jedec_params;
+ struct jedec_ecc_info *ecc;
+ int val;
+ int i, j;
+
+ /* Try JEDEC for unknown chip or LP */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
+ if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' ||
+ chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' ||
+ chip->read_byte(mtd) != 'C')
+ return 0;
+
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1);
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < sizeof(*p); j++)
+ ((uint8_t *)p)[j] = chip->read_byte(mtd);
+
+ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
+ le16_to_cpu(p->crc))
+ break;
+ }
+
+ if (i == 3) {
+ pr_err("Could not find valid JEDEC parameter page; aborting\n");
+ return 0;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 2))
+ chip->jedec_version = 10;
+ else if (val & (1 << 1))
+ chip->jedec_version = 1; /* vendor specific version */
+
+ if (!chip->jedec_version) {
+ pr_info("unsupported JEDEC version: %d\n", val);
+ return 0;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ if (!mtd->name)
+ mtd->name = p->model;
+
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ chip->bits_per_cell = p->bits_per_cell;
+
+ if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
+ *busw = NAND_BUSWIDTH_16;
+ else
+ *busw = 0;
+
+ /* ECC info */
+ ecc = &p->ecc_info[0];
+
+ if (ecc->codeword_size >= 9) {
+ chip->ecc_strength_ds = ecc->ecc_bits;
+ chip->ecc_step_ds = 1 << ecc->codeword_size;
+ } else {
+ pr_warn("Invalid codeword size\n");
+ }
+
+ return 1;
+}
+
+/*
* nand_id_has_period - Check if an ID string has a given wraparound period
* @id_data: the ID string
* @arrlen: the length of the @id_data array
@@ -3474,10 +3551,10 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
- int busw,
int *maf_id, int *dev_id,
struct nand_flash_dev *type)
{
+ int busw;
int i, maf_idx;
u8 id_data[8];
@@ -3533,6 +3610,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/* Check is chip is ONFI compliant */
if (nand_flash_detect_onfi(mtd, chip, &busw))
goto ident_done;
+
+ /* Check if the chip is JEDEC compliant */
+ if (nand_flash_detect_jedec(mtd, chip, &busw))
+ goto ident_done;
}
if (!type->name)
@@ -3612,8 +3693,17 @@ ident_done:
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
*maf_id, *dev_id);
- pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
- chip->onfi_version ? chip->onfi_params.model : type->name);
+
+ if (chip->onfi_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->onfi_params.model);
+ else if (chip->jedec_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->jedec_params.model);
+ else
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ type->name);
+
pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
mtd->writesize, mtd->oobsize);
@@ -3634,18 +3724,16 @@ ident_done:
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
struct nand_flash_dev *table)
{
- int i, busw, nand_maf_id, nand_dev_id;
+ int i, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
- /* Get buswidth to select the correct functions */
- busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
- nand_set_defaults(chip, busw);
+ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
/* Read the flash type */
- type = nand_get_flash_type(mtd, chip, busw,
- &nand_maf_id, &nand_dev_id, table);
+ type = nand_get_flash_type(mtd, chip, &nand_maf_id,
+ &nand_dev_id, table);
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -3696,15 +3784,26 @@ int nand_scan_tail(struct mtd_info *mtd)
int i;
struct nand_chip *chip = mtd->priv;
struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_buffers *nbuf;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH));
- if (!(chip->options & NAND_OWN_BUFFERS))
- chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
- if (!chip->buffers)
- return -ENOMEM;
+ if (!(chip->options & NAND_OWN_BUFFERS)) {
+ nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
+ + mtd->oobsize * 3, GFP_KERNEL);
+ if (!nbuf)
+ return -ENOMEM;
+ nbuf->ecccalc = (uint8_t *)(nbuf + 1);
+ nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
+ nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+
+ chip->buffers = nbuf;
+ } else {
+ if (!chip->buffers)
+ return -ENOMEM;
+ }
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
@@ -3825,7 +3924,7 @@ int nand_scan_tail(struct mtd_info *mtd)
case NAND_ECC_SOFT_BCH:
if (!mtd_nand_has_bch()) {
- pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
+ pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
BUG();
}
ecc->calculate = nand_bch_calculate_ecc;
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index daa2faacd7d0..3d7c89fc1031 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -43,6 +43,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TC58NVG6D2 64G 3.3V 8-bit",
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+ {"SDTNRGAMA 64G 3.3V 8-bit",
+ { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
+ SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c
index 9ee09a8177c6..e8a5fffd6ab2 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/nuc900_nand.c
@@ -10,7 +10,6 @@
*/
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -152,7 +151,8 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
if (column != -1 || page_addr != -1) {
if (column != -1) {
- if (chip->options & NAND_BUSWIDTH_16)
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
column >>= 1;
write_addr_reg(nand, column);
write_addr_reg(nand, column >> 8 | ENDADDR);
@@ -225,7 +225,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand)
val = __raw_readl(nand->reg + REG_FMICSR);
if (!(val & NAND_EN))
- __raw_writel(val | NAND_EN, REG_FMICSR);
+ __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR);
val = __raw_readl(nand->reg + REG_SMCSR);
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index bf642ceef681..1ff49b80bdaf 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -118,14 +118,9 @@
#define OMAP24XX_DMA_GPMC 4
-#define BCH8_MAX_ERROR 8 /* upto 8 bit correctable */
-#define BCH4_MAX_ERROR 4 /* upto 4 bit correctable */
-
#define SECTOR_BYTES 512
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
#define BCH4_BIT_PAD 4
-#define BCH8_ECC_MAX ((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8)
-#define BCH4_ECC_MAX ((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8)
/* GPMC ecc engine settings for read */
#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
@@ -159,7 +154,7 @@ struct omap_nand_info {
int gpmc_cs;
unsigned long phys_base;
- unsigned long mem_size;
+ enum omap_ecc ecc_opt;
struct completion comp;
struct dma_chan *dma;
int gpmc_irq_fifo;
@@ -172,7 +167,6 @@ struct omap_nand_info {
int buf_len;
struct gpmc_nand_regs reg;
/* fields specific for BCHx_HW ECC scheme */
- bool is_elm_used;
struct device *elm_dev;
struct device_node *of_node;
};
@@ -1043,9 +1037,8 @@ static int omap_dev_ready(struct mtd_info *mtd)
}
}
-#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH)
/**
- * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
+ * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
* @mtd: MTD device structure
* @mode: Read/Write mode
*
@@ -1056,50 +1049,73 @@ static int omap_dev_ready(struct mtd_info *mtd)
* eccsize0 = 0 (no additional protected byte in spare area)
* eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
*/
-static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
+static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
{
- int nerrors;
+ unsigned int bch_type;
unsigned int dev_width, nsectors;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
+ enum omap_ecc ecc_opt = info->ecc_opt;
struct nand_chip *chip = mtd->priv;
u32 val, wr_mode;
unsigned int ecc_size1, ecc_size0;
- /* Using wrapping mode 6 for writing */
- wr_mode = BCH_WRAPMODE_6;
-
- /*
- * ECC engine enabled for valid ecc_size0 nibbles
- * and disabled for ecc_size1 nibbles.
- */
- ecc_size0 = BCH_ECC_SIZE0;
- ecc_size1 = BCH_ECC_SIZE1;
-
- /* Perform ecc calculation on 512-byte sector */
- nsectors = 1;
-
- /* Update number of error correction */
- nerrors = info->nand.ecc.strength;
-
- /* Multi sector reading/writing for NAND flash with page size < 4096 */
- if (info->is_elm_used && (mtd->writesize <= 4096)) {
+ /* GPMC configurations for calculating ECC */
+ switch (ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ bch_type = 0;
+ nsectors = 1;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ bch_type = 0;
+ nsectors = chip->ecc.steps;
if (mode == NAND_ECC_READ) {
- /* Using wrapping mode 1 for reading */
- wr_mode = BCH_WRAPMODE_1;
-
- /*
- * ECC engine enabled for ecc_size0 nibbles
- * and disabled for ecc_size1 nibbles.
- */
- ecc_size0 = (nerrors == 8) ?
- BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
- ecc_size1 = (nerrors == 8) ?
- BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
+ wr_mode = BCH_WRAPMODE_1;
+ ecc_size0 = BCH4R_ECC_SIZE0;
+ ecc_size1 = BCH4R_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
}
-
- /* Perform ecc calculation for one page (< 4096) */
- nsectors = info->nand.ecc.steps;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ bch_type = 1;
+ nsectors = 1;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_type = 1;
+ nsectors = chip->ecc.steps;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_1;
+ ecc_size0 = BCH8R_ECC_SIZE0;
+ ecc_size1 = BCH8R_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ default:
+ return;
}
writel(ECC1, info->reg.gpmc_ecc_control);
@@ -1112,7 +1128,7 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
/* BCH configuration */
val = ((1 << 16) | /* enable BCH */
- (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
+ (bch_type << 12) | /* BCH4/BCH8/BCH16 */
(wr_mode << 8) | /* wrap mode */
(dev_width << 7) | /* bus width */
(((nsectors-1) & 0x7) << 4) | /* number of sectors */
@@ -1124,132 +1140,40 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
/* Clear ecc and enable bits */
writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
}
-#endif
-
-#ifdef CONFIG_MTD_NAND_ECC_BCH
-/**
- * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
- unsigned long nsectors, val1, val2;
- int i;
-
- nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
- for (i = 0; i < nsectors; i++) {
- /* Read hw-computed remainder */
- val1 = readl(info->reg.gpmc_bch_result0[i]);
- val2 = readl(info->reg.gpmc_bch_result1[i]);
-
- /*
- * Add constant polynomial to remainder, in order to get an ecc
- * sequence of 0xFFs for a buffer filled with 0xFFs; and
- * left-justify the resulting polynomial.
- */
- *ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
- *ecc_code++ = 0x13 ^ ((val2 >> 4) & 0xFF);
- *ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
- *ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
- *ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
- *ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
- *ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
- }
-
- return 0;
-}
+static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
+static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+ 0x97, 0x79, 0xe5, 0x24, 0xb5};
/**
- * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
- unsigned long nsectors, val1, val2, val3, val4;
- int i;
-
- nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
- for (i = 0; i < nsectors; i++) {
-
- /* Read hw-computed remainder */
- val1 = readl(info->reg.gpmc_bch_result0[i]);
- val2 = readl(info->reg.gpmc_bch_result1[i]);
- val3 = readl(info->reg.gpmc_bch_result2[i]);
- val4 = readl(info->reg.gpmc_bch_result3[i]);
-
- /*
- * Add constant polynomial to remainder, in order to get an ecc
- * sequence of 0xFFs for a buffer filled with 0xFFs.
- */
- *ecc_code++ = 0xef ^ (val4 & 0xFF);
- *ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
- *ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
- *ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
- *ecc_code++ = 0xed ^ (val3 & 0xFF);
- *ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
- *ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
- *ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
- *ecc_code++ = 0x97 ^ (val2 & 0xFF);
- *ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
- *ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
- *ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
- *ecc_code++ = 0xb5 ^ (val1 & 0xFF);
- }
-
- return 0;
-}
-#endif /* CONFIG_MTD_NAND_ECC_BCH */
-
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-/**
- * omap3_calculate_ecc_bch - Generate bytes of ECC bytes
+ * omap_calculate_ecc_bch - Generate bytes of ECC bytes
* @mtd: MTD device structure
* @dat: The pointer to data on which ecc is computed
* @ecc_code: The ecc_code buffer
*
* Support calculating of BCH4/8 ecc vectors for the page
*/
-static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
+static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_calc)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
+ int eccbytes = info->nand.ecc.bytes;
+ struct gpmc_nand_regs *gpmc_regs = &info->reg;
+ u8 *ecc_code;
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
- int i, eccbchtsel;
+ int i;
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
- /*
- * find BCH scheme used
- * 0 -> BCH4
- * 1 -> BCH8
- */
- eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3);
-
for (i = 0; i < nsectors; i++) {
-
- /* Read hw-computed remainder */
- bch_val1 = readl(info->reg.gpmc_bch_result0[i]);
- bch_val2 = readl(info->reg.gpmc_bch_result1[i]);
- if (eccbchtsel) {
- bch_val3 = readl(info->reg.gpmc_bch_result2[i]);
- bch_val4 = readl(info->reg.gpmc_bch_result3[i]);
- }
-
- if (eccbchtsel) {
- /* BCH8 ecc scheme */
+ ecc_code = ecc_calc;
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
+ bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
+ bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
*ecc_code++ = (bch_val4 & 0xFF);
*ecc_code++ = ((bch_val3 >> 24) & 0xFF);
*ecc_code++ = ((bch_val3 >> 16) & 0xFF);
@@ -1263,14 +1187,11 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
*ecc_code++ = ((bch_val1 >> 16) & 0xFF);
*ecc_code++ = ((bch_val1 >> 8) & 0xFF);
*ecc_code++ = (bch_val1 & 0xFF);
- /*
- * Setting 14th byte to zero to handle
- * erased page & maintain compatibility
- * with RBL
- */
- *ecc_code++ = 0x0;
- } else {
- /* BCH4 ecc scheme */
+ break;
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH4_CODE_HW:
+ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
*ecc_code++ = ((bch_val2 >> 12) & 0xFF);
*ecc_code++ = ((bch_val2 >> 4) & 0xFF);
*ecc_code++ = ((bch_val2 & 0xF) << 4) |
@@ -1279,12 +1200,38 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
*ecc_code++ = ((bch_val1 >> 12) & 0xFF);
*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
*ecc_code++ = ((bch_val1 & 0xF) << 4);
- /*
- * Setting 8th byte to zero to handle
- * erased page
- */
- *ecc_code++ = 0x0;
+ break;
+ default:
+ return -EINVAL;
}
+
+ /* ECC scheme specific syndrome customizations */
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ /* Add constant polynomial to remainder, so that
+ * ECC of blank pages results in 0x0 on reading back */
+ for (i = 0; i < eccbytes; i++)
+ ecc_calc[i] ^= bch4_polynomial[i];
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* Set 8th ECC byte as 0x0 for ROM compatibility */
+ ecc_calc[eccbytes - 1] = 0x0;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ /* Add constant polynomial to remainder, so that
+ * ECC of blank pages results in 0x0 on reading back */
+ for (i = 0; i < eccbytes; i++)
+ ecc_calc[i] ^= bch8_polynomial[i];
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* Set 14th ECC byte as 0x0 for ROM compatibility */
+ ecc_calc[eccbytes - 1] = 0x0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ecc_calc += eccbytes;
}
return 0;
@@ -1329,6 +1276,7 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
return flip_bits;
}
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
/**
* omap_elm_correct_data - corrects page data area in case error reported
* @mtd: MTD device structure
@@ -1337,55 +1285,46 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
* @calc_ecc: ecc read from HW ECC registers
*
* Calculated ecc vector reported as zero in case of non-error pages.
- * In case of error/erased pages non-zero error vector is reported.
- * In case of non-zero ecc vector, check read_ecc at fixed offset
- * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
- * To handle bit flips in this data, count the number of 0's in
- * read_ecc[x] and check if it greater than 4. If it is less, it is
- * programmed page, else erased page.
- *
- * 1. If page is erased, check with standard ecc vector (ecc vector
- * for erased page to find any bit flip). If check fails, bit flip
- * is present in erased page. Count the bit flips in erased page and
- * if it falls under correctable level, report page with 0xFF and
- * update the correctable bit information.
- * 2. If error is reported on programmed page, update elm error
- * vector and correct the page with ELM error correction routine.
- *
+ * In case of non-zero ecc vector, first filter out erased-pages, and
+ * then process data via ELM to detect bit-flips.
*/
static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
u_char *read_ecc, u_char *calc_ecc)
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
+ struct nand_ecc_ctrl *ecc = &info->nand.ecc;
int eccsteps = info->nand.ecc.steps;
int i , j, stat = 0;
- int eccsize, eccflag, ecc_vector_size;
+ int eccflag, actual_eccbytes;
struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
u_char *ecc_vec = calc_ecc;
u_char *spare_ecc = read_ecc;
u_char *erased_ecc_vec;
- enum bch_ecc type;
+ u_char *buf;
+ int bitflip_count;
bool is_error_reported = false;
+ u32 bit_pos, byte_pos, error_max, pos;
+ int err;
- /* Initialize elm error vector to zero */
- memset(err_vec, 0, sizeof(err_vec));
-
- if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
- type = BCH8_ECC;
- erased_ecc_vec = bch8_vector;
- } else {
- type = BCH4_ECC;
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* omit 7th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
erased_ecc_vec = bch4_vector;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* omit 14th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
+ erased_ecc_vec = bch8_vector;
+ break;
+ default:
+ pr_err("invalid driver configuration\n");
+ return -EINVAL;
}
- ecc_vector_size = info->nand.ecc.bytes;
-
- /*
- * Remove extra byte padding for BCH8 RBL
- * compatibility and erased page handling
- */
- eccsize = ecc_vector_size - 1;
+ /* Initialize elm error vector to zero */
+ memset(err_vec, 0, sizeof(err_vec));
for (i = 0; i < eccsteps ; i++) {
eccflag = 0; /* initialize eccflag */
@@ -1394,8 +1333,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
* Check any error reported,
* In case of error, non zero ecc reported.
*/
-
- for (j = 0; (j < eccsize); j++) {
+ for (j = 0; j < actual_eccbytes; j++) {
if (calc_ecc[j] != 0) {
eccflag = 1; /* non zero ecc, error present */
break;
@@ -1403,50 +1341,43 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
}
if (eccflag == 1) {
- /*
- * Set threshold to minimum of 4, half of ecc.strength/2
- * to allow max bit flip in byte to 4
- */
- unsigned int threshold = min_t(unsigned int, 4,
- info->nand.ecc.strength / 2);
-
- /*
- * Check data area is programmed by counting
- * number of 0's at fixed offset in spare area.
- * Checking count of 0's against threshold.
- * In case programmed page expects at least threshold
- * zeros in byte.
- * If zeros are less than threshold for programmed page/
- * zeros are more than threshold erased page, either
- * case page reported as uncorrectable.
- */
- if (hweight8(~read_ecc[eccsize]) >= threshold) {
+ if (memcmp(calc_ecc, erased_ecc_vec,
+ actual_eccbytes) == 0) {
/*
- * Update elm error vector as
- * data area is programmed
+ * calc_ecc[] matches pattern for ECC(all 0xff)
+ * so this is definitely an erased-page
*/
- err_vec[i].error_reported = true;
- is_error_reported = true;
} else {
- /* Error reported in erased page */
- int bitflip_count;
- u_char *buf = &data[info->nand.ecc.size * i];
-
- if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
- bitflip_count = erased_sector_bitflips(
- buf, read_ecc, info);
-
- if (bitflip_count)
- stat += bitflip_count;
- else
- return -EINVAL;
+ buf = &data[info->nand.ecc.size * i];
+ /*
+ * count number of 0-bits in read_buf.
+ * This check can be removed once a similar
+ * check is introduced in generic NAND driver
+ */
+ bitflip_count = erased_sector_bitflips(
+ buf, read_ecc, info);
+ if (bitflip_count) {
+ /*
+ * number of 0-bits within ECC limits
+ * So this may be an erased-page
+ */
+ stat += bitflip_count;
+ } else {
+ /*
+ * Too many 0-bits. It may be a
+ * - programmed-page, OR
+ * - erased-page with many bit-flips
+ * So this page requires check by ELM
+ */
+ err_vec[i].error_reported = true;
+ is_error_reported = true;
}
}
}
/* Update the ecc vector */
- calc_ecc += ecc_vector_size;
- read_ecc += ecc_vector_size;
+ calc_ecc += ecc->bytes;
+ read_ecc += ecc->bytes;
}
/* Check if any error reported */
@@ -1456,23 +1387,26 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
/* Decode BCH error using ELM module */
elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
+ err = 0;
for (i = 0; i < eccsteps; i++) {
- if (err_vec[i].error_reported) {
+ if (err_vec[i].error_uncorrectable) {
+ pr_err("nand: uncorrectable bit-flips found\n");
+ err = -EBADMSG;
+ } else if (err_vec[i].error_reported) {
for (j = 0; j < err_vec[i].error_count; j++) {
- u32 bit_pos, byte_pos, error_max, pos;
-
- if (type == BCH8_ECC)
- error_max = BCH8_ECC_MAX;
- else
- error_max = BCH4_ECC_MAX;
-
- if (info->nand.ecc.strength == BCH8_MAX_ERROR)
- pos = err_vec[i].error_loc[j];
- else
- /* Add 4 to take care 4 bit padding */
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* Add 4 bits to take care of padding */
pos = err_vec[i].error_loc[j] +
BCH4_BIT_PAD;
-
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ pos = err_vec[i].error_loc[j];
+ break;
+ default:
+ return -EINVAL;
+ }
+ error_max = (ecc->size + actual_eccbytes) * 8;
/* Calculate bit position of error */
bit_pos = pos % 8;
@@ -1480,13 +1414,22 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
byte_pos = (error_max - pos - 1) / 8;
if (pos < error_max) {
- if (byte_pos < 512)
+ if (byte_pos < 512) {
+ pr_debug("bitflip@dat[%d]=%x\n",
+ byte_pos, data[byte_pos]);
data[byte_pos] ^= 1 << bit_pos;
- else
+ } else {
+ pr_debug("bitflip@oob[%d]=%x\n",
+ (byte_pos - 512),
+ spare_ecc[byte_pos - 512]);
spare_ecc[byte_pos - 512] ^=
1 << bit_pos;
+ }
+ } else {
+ pr_err("invalid bit-flip @ %d:%d\n",
+ byte_pos, bit_pos);
+ err = -EBADMSG;
}
- /* else, not interested to correct ecc */
}
}
@@ -1494,16 +1437,11 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
stat += err_vec[i].error_count;
/* Update page data with sector size */
- data += info->nand.ecc.size;
- spare_ecc += ecc_vector_size;
+ data += ecc->size;
+ spare_ecc += ecc->bytes;
}
- for (i = 0; i < eccsteps; i++)
- /* Return error if uncorrectable error present */
- if (err_vec[i].error_uncorrectable)
- return -EINVAL;
-
- return stat;
+ return (err) ? err : stat;
}
/**
@@ -1601,7 +1539,8 @@ static int is_elm_present(struct omap_nand_info *info,
struct device_node *elm_node, enum bch_ecc bch_type)
{
struct platform_device *pdev;
- info->is_elm_used = false;
+ struct nand_ecc_ctrl *ecc = &info->nand.ecc;
+ int err;
/* check whether elm-id is passed via DT */
if (!elm_node) {
pr_err("nand: error: ELM DT node not found\n");
@@ -1615,10 +1554,10 @@ static int is_elm_present(struct omap_nand_info *info,
}
/* ELM module available, now configure it */
info->elm_dev = &pdev->dev;
- if (elm_config(info->elm_dev, bch_type))
- return -ENODEV;
- info->is_elm_used = true;
- return 0;
+ err = elm_config(info->elm_dev, bch_type,
+ (info->mtd.writesize / ecc->size), ecc->size, ecc->bytes);
+
+ return err;
}
#endif /* CONFIG_MTD_NAND_ECC_BCH */
@@ -1657,6 +1596,7 @@ static int omap_nand_probe(struct platform_device *pdev)
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
info->of_node = pdata->of_node;
+ info->ecc_opt = pdata->ecc_opt;
mtd = &info->mtd;
mtd->priv = &info->nand;
mtd->name = dev_name(&pdev->dev);
@@ -1666,27 +1606,11 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_SKIP_BBTSCAN;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- err = -EINVAL;
- dev_err(&pdev->dev, "error getting memory resource\n");
- goto return_error;
- }
+ nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nand_chip->IO_ADDR_R))
+ return PTR_ERR(nand_chip->IO_ADDR_R);
info->phys_base = res->start;
- info->mem_size = resource_size(res);
-
- if (!devm_request_mem_region(&pdev->dev, info->phys_base,
- info->mem_size, pdev->dev.driver->name)) {
- err = -EBUSY;
- goto return_error;
- }
-
- nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base,
- info->mem_size);
- if (!nand_chip->IO_ADDR_R) {
- err = -ENOMEM;
- goto return_error;
- }
nand_chip->controller = &info->controller;
@@ -1812,7 +1736,7 @@ static int omap_nand_probe(struct platform_device *pdev)
/* populate MTD interface based on ECC scheme */
nand_chip->ecc.layout = &omap_oobinfo;
ecclayout = &omap_oobinfo;
- switch (pdata->ecc_opt) {
+ switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
@@ -1844,9 +1768,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.size = 512;
nand_chip->ecc.bytes = 7;
nand_chip->ecc.strength = 4;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch4;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
@@ -1884,9 +1808,9 @@ static int omap_nand_probe(struct platform_device *pdev)
/* 14th bit is kept reserved for ROM-code compatibility */
nand_chip->ecc.bytes = 7 + 1;
nand_chip->ecc.strength = 4;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
/* define ECC layout */
@@ -1919,9 +1843,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.size = 512;
nand_chip->ecc.bytes = 13;
nand_chip->ecc.strength = 8;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch8;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
@@ -1960,9 +1884,9 @@ static int omap_nand_probe(struct platform_device *pdev)
/* 14th bit is kept reserved for ROM-code compatibility */
nand_chip->ecc.bytes = 13 + 1;
nand_chip->ecc.strength = 8;
- nand_chip->ecc.hwctl = omap3_enable_hwecc_bch;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.calculate = omap3_calculate_ecc_bch;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
/* This ECC scheme requires ELM H/W block */
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index 90f871acb0ef..2c98f9da7471 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -23,7 +23,6 @@
#undef DEBUG
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 2a7a0b27ac38..7588fe2c127f 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -38,7 +38,6 @@
#include <linux/platform_data/mtd-nand-pxa3xx.h>
-#define NAND_DEV_READY_TIMEOUT 50
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
#define NAND_STOP_DELAY (2 * HZ/50)
#define PAGE_CHUNK_SIZE (2048)
@@ -1531,7 +1530,7 @@ KEEP_CONFIG:
if (!ret) {
dev_err(&info->pdev->dev,
"ECC strength %d at page size %d is not supported\n",
- chip->ecc_strength_ds, mtd->writesize);
+ ecc_strength, mtd->writesize);
return -ENODEV;
}
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index f0918e7411d9..79acbb8691b5 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -29,7 +29,6 @@
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/io.h>
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
index 8e1919b6f074..093c29ac1a13 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/onenand/generic.c
@@ -13,7 +13,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 6547c84afc3a..d945473c3882 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -25,7 +25,6 @@
#include <linux/device.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 1de33b5d3903..635ee0027691 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -3238,20 +3237,17 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
/**
* onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
* @param mtd MTD device structure
- * @param buf the databuffer to put/get data
* @param len number of bytes to read
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param buf the databuffer to put/get data
*
* Read factory OTP info.
*/
-static int onenand_get_fact_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
- size_t retlen;
- int ret;
-
- ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
-
- return ret ? : retlen;
+ return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+ MTD_OTP_FACTORY);
}
/**
@@ -3273,20 +3269,17 @@ static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
/**
* onenand_get_user_prot_info - [MTD Interface] Read user OTP info
* @param mtd MTD device structure
- * @param buf the databuffer to put/get data
+ * @param retlen pointer to variable to store the number of read bytes
* @param len number of bytes to read
+ * @param buf the databuffer to put/get data
*
* Read user OTP info.
*/
-static int onenand_get_user_prot_info(struct mtd_info *mtd,
- struct otp_info *buf, size_t len)
+static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
{
- size_t retlen;
- int ret;
-
- ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
-
- return ret ? : retlen;
+ return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+ MTD_OTP_USER);
}
/**
@@ -3995,11 +3988,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
/* Allocate buffers, if necessary */
if (!this->page_buf) {
this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
- if (!this->page_buf) {
- printk(KERN_ERR "%s: Can't allocate page_buf\n",
- __func__);
+ if (!this->page_buf)
return -ENOMEM;
- }
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
if (!this->verify_buf) {
@@ -4012,8 +4002,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
if (!this->oob_buf) {
this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
if (!this->oob_buf) {
- printk(KERN_ERR "%s: Can't allocate oob_buf\n",
- __func__);
if (this->options & ONENAND_PAGEBUF_ALLOC) {
this->options &= ~ONENAND_PAGEBUF_ALLOC;
kfree(this->page_buf);
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index df7400dd4df8..b1a792fd1c23 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -872,10 +872,8 @@ static int s3c_onenand_probe(struct platform_device *pdev)
size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
mtd = kzalloc(size, GFP_KERNEL);
- if (!mtd) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ if (!mtd)
return -ENOMEM;
- }
onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL);
if (!onenand) {
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index 233b946e5d66..d1cbf26db2c0 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -602,8 +602,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
if (rc) {
printk(KERN_ERR PREFIX "error writing '%s' at "
"0x%lx\n", part->mbd.mtd->name, addr);
- if (rc)
- goto err;
+ goto err;
}
if (block == part->current_block)
part->header_cache[offset + HEADER_MAP_OFFSET] = del;
@@ -675,8 +674,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
if (rc) {
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
part->mbd.mtd->name, addr);
- if (rc)
- goto err;
+ goto err;
}
part->sector_map[sector] = addr;
@@ -695,8 +693,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
if (rc) {
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
part->mbd.mtd->name, addr);
- if (rc)
- goto err;
+ goto err;
}
block->used_sectors++;
block->free_sectors--;
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 4b8e89583f2a..cf49c22673b9 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -59,15 +59,12 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
struct attribute_group *attr_group;
struct attribute **attributes;
struct sm_sysfs_attribute *vendor_attribute;
+ char *vendor;
- int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
- SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
-
- char *vendor = kmalloc(vendor_len, GFP_KERNEL);
+ vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
+ SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL);
if (!vendor)
goto error1;
- memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
- vendor[vendor_len] = 0;
/* Initialize sysfs attributes */
vendor_attribute =
@@ -78,7 +75,7 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
sysfs_attr_init(&vendor_attribute->dev_attr.attr);
vendor_attribute->data = vendor;
- vendor_attribute->len = vendor_len;
+ vendor_attribute->len = strlen(vendor);
vendor_attribute->dev_attr.attr.name = "vendor";
vendor_attribute->dev_attr.attr.mode = S_IRUGO;
vendor_attribute->dev_attr.show = sm_attr_show;
diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c
index c818a63532e7..111ee46a7428 100644
--- a/drivers/mtd/tests/mtd_test.c
+++ b/drivers/mtd/tests/mtd_test.c
@@ -1,6 +1,5 @@
#define pr_fmt(fmt) "mtd_test: " fmt
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/printk.h>
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 0ba8b0a28838..7bf416329c19 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -22,7 +22,6 @@
#ifndef __UBI_UBI_H__
#define __UBI_UBI_H__
-#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/rbtree.h>
diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig
index 4b18b8765523..1e65cb6c2591 100644
--- a/drivers/net/can/sja1000/Kconfig
+++ b/drivers/net/can/sja1000/Kconfig
@@ -39,7 +39,7 @@ config CAN_EMS_PCI
config CAN_PEAK_PCMCIA
tristate "PEAK PCAN-PC Card"
depends on PCMCIA
- depends on HAS_IOPORT
+ depends on HAS_IOPORT_MAP
---help---
This driver is for the PCAN-PC Card PCMCIA adapter (1 or 2 channels)
from PEAK-System (http://www.peak-system.com). To compile this
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 65b735d4a6ad..afaab4b2333f 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -66,7 +66,7 @@ config PCMCIA_3C589
config VORTEX
tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
- depends on (PCI || EISA) && HAS_IOPORT
+ depends on (PCI || EISA) && HAS_IOPORT_MAP
select MII
---help---
This option enables driver support for a large number of 10Mbps and
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 6d1f6ed3113f..a8497183ff8b 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -493,6 +493,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
ndev->netdev_ops = &rionet_netdev_ops;
ndev->mtu = RIO_MAX_MSG_SIZE - 14;
ndev->features = NETIF_F_LLTX;
+ SET_NETDEV_DEV(ndev, &mport->dev);
SET_ETHTOOL_OPS(ndev, &rionet_ethtool_ops);
spin_lock_init(&rnet->lock);
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index a27ec94877e4..b7361ed70537 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -50,6 +50,40 @@ int of_get_nand_ecc_mode(struct device_node *np)
EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
/**
+ * of_get_nand_ecc_step_size - Get ECC step size associated to
+ * the required ECC strength (see below).
+ * @np: Pointer to the given device_node
+ *
+ * return the ECC step size, or errno in error case.
+ */
+int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+ return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
+
+/**
+ * of_get_nand_ecc_strength - Get required ECC strength over the
+ * correspnding step size as defined by 'nand-ecc-size'
+ * @np: Pointer to the given device_node
+ *
+ * return the ECC strength, or errno in error case.
+ */
+int of_get_nand_ecc_strength(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+ return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
+
+/**
* of_get_nand_bus_width - Get nand bus witdh for given device_node
* @np: Pointer to the given device_node
*
diff --git a/drivers/oprofile/nmi_timer_int.c b/drivers/oprofile/nmi_timer_int.c
index 76f1c9357f39..9559829fb234 100644
--- a/drivers/oprofile/nmi_timer_int.c
+++ b/drivers/oprofile/nmi_timer_int.c
@@ -108,8 +108,8 @@ static void nmi_timer_shutdown(void)
struct perf_event *event;
int cpu;
- get_online_cpus();
- unregister_cpu_notifier(&nmi_timer_cpu_nb);
+ cpu_notifier_register_begin();
+ __unregister_cpu_notifier(&nmi_timer_cpu_nb);
for_each_possible_cpu(cpu) {
event = per_cpu(nmi_timer_events, cpu);
if (!event)
@@ -119,7 +119,7 @@ static void nmi_timer_shutdown(void)
perf_event_release_kernel(event);
}
- put_online_cpus();
+ cpu_notifier_register_done();
}
static int nmi_timer_setup(void)
@@ -132,20 +132,23 @@ static int nmi_timer_setup(void)
do_div(period, HZ);
nmi_timer_attr.sample_period = period;
- get_online_cpus();
- err = register_cpu_notifier(&nmi_timer_cpu_nb);
+ cpu_notifier_register_begin();
+ err = __register_cpu_notifier(&nmi_timer_cpu_nb);
if (err)
goto out;
+
/* can't attach events to offline cpus: */
for_each_online_cpu(cpu) {
err = nmi_timer_start_cpu(cpu);
- if (err)
- break;
+ if (err) {
+ cpu_notifier_register_done();
+ nmi_timer_shutdown();
+ return err;
+ }
}
- if (err)
- nmi_timer_shutdown();
+
out:
- put_online_cpus();
+ cpu_notifier_register_done();
return err;
}
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 7dd62fa9d0bd..396c200b9ddb 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -116,11 +116,11 @@ static void pci_slot_release(struct kobject *kobj)
}
static struct pci_slot_attribute pci_slot_attr_address =
- __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
+ __ATTR(address, S_IRUGO, address_read_file, NULL);
static struct pci_slot_attribute pci_slot_attr_max_speed =
- __ATTR(max_bus_speed, (S_IFREG | S_IRUGO), max_speed_read_file, NULL);
+ __ATTR(max_bus_speed, S_IRUGO, max_speed_read_file, NULL);
static struct pci_slot_attribute pci_slot_attr_cur_speed =
- __ATTR(cur_bus_speed, (S_IFREG | S_IRUGO), cur_speed_read_file, NULL);
+ __ATTR(cur_bus_speed, S_IRUGO, cur_speed_read_file, NULL);
static struct attribute *pci_slot_default_attrs[] = {
&pci_slot_attr_address.attr,
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 8d3c49cc500f..3bb05f17b9b4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -27,7 +27,7 @@ config PHY_EXYNOS_MIPI_VIDEO
config PHY_MVEBU_SATA
def_bool y
- depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
+ depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
depends on OF
select GENERIC_PHY
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 6d452a78b19c..fa0e4e057b99 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -22,7 +22,7 @@ config POWER_RESET_GPIO
config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver"
- depends on POWER_RESET && ARCH_MSM
+ depends on POWER_RESET && ARCH_QCOM
help
Power off and restart support for Qualcomm boards.
diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c
index 37f56f7ee926..a75db7f8a92f 100644
--- a/drivers/power/reset/qnap-poweroff.c
+++ b/drivers/power/reset/qnap-poweroff.c
@@ -1,5 +1,5 @@
/*
- * QNAP Turbo NAS Board power off
+ * QNAP Turbo NAS Board power off. Can also be used on Synology devices.
*
* Copyright (C) 2012 Andrew Lunn <andrew@lunn.ch>
*
@@ -25,17 +25,43 @@
#define UART1_REG(x) (base + ((UART_##x) << 2))
+struct power_off_cfg {
+ u32 baud;
+ char cmd;
+};
+
+static const struct power_off_cfg qnap_power_off_cfg = {
+ .baud = 19200,
+ .cmd = 'A',
+};
+
+static const struct power_off_cfg synology_power_off_cfg = {
+ .baud = 9600,
+ .cmd = '1',
+};
+
+static const struct of_device_id qnap_power_off_of_match_table[] = {
+ { .compatible = "qnap,power-off",
+ .data = &qnap_power_off_cfg,
+ },
+ { .compatible = "synology,power-off",
+ .data = &synology_power_off_cfg,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
+
static void __iomem *base;
static unsigned long tclk;
+static const struct power_off_cfg *cfg;
static void qnap_power_off(void)
{
- /* 19200 baud divisor */
- const unsigned divisor = ((tclk + (8 * 19200)) / (16 * 19200));
+ const unsigned divisor = ((tclk + (8 * cfg->baud)) / (16 * cfg->baud));
pr_err("%s: triggering power-off...\n", __func__);
- /* hijack UART1 and reset into sane state (19200,8n1) */
+ /* hijack UART1 and reset into sane state */
writel(0x83, UART1_REG(LCR));
writel(divisor & 0xff, UART1_REG(DLL));
writel((divisor >> 8) & 0xff, UART1_REG(DLM));
@@ -44,16 +70,21 @@ static void qnap_power_off(void)
writel(0x00, UART1_REG(FCR));
writel(0x00, UART1_REG(MCR));
- /* send the power-off command 'A' to PIC */
- writel('A', UART1_REG(TX));
+ /* send the power-off command to PIC */
+ writel(cfg->cmd, UART1_REG(TX));
}
static int qnap_power_off_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct resource *res;
struct clk *clk;
char symname[KSYM_NAME_LEN];
+ const struct of_device_id *match =
+ of_match_node(qnap_power_off_of_match_table, np);
+ cfg = match->data;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Missing resource");
@@ -94,12 +125,6 @@ static int qnap_power_off_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id qnap_power_off_of_match_table[] = {
- { .compatible = "qnap,power-off", },
- {}
-};
-MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
-
static struct platform_driver qnap_power_off_driver = {
.probe = qnap_power_off_probe,
.remove = qnap_power_off_remove,
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 61b51e17d932..d9a0770b6c73 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -1374,6 +1374,9 @@ static int __init rapl_init(void)
return -ENODEV;
}
+
+ cpu_notifier_register_begin();
+
/* prevent CPU hotplug during detection */
get_online_cpus();
ret = rapl_detect_topology();
@@ -1385,20 +1388,23 @@ static int __init rapl_init(void)
ret = -ENODEV;
goto done;
}
- register_hotcpu_notifier(&rapl_cpu_notifier);
+ __register_hotcpu_notifier(&rapl_cpu_notifier);
done:
put_online_cpus();
+ cpu_notifier_register_done();
return ret;
}
static void __exit rapl_exit(void)
{
+ cpu_notifier_register_begin();
get_online_cpus();
- unregister_hotcpu_notifier(&rapl_cpu_notifier);
+ __unregister_hotcpu_notifier(&rapl_cpu_notifier);
rapl_unregister_powercap();
rapl_cleanup_data();
put_online_cpus();
+ cpu_notifier_register_done();
}
module_init(rapl_init);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f2857b82..5b34ff29ea38 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -71,6 +71,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
will be called pwm-bfin.
+config PWM_CLPS711X
+ tristate "CLPS711X PWM support"
+ depends on ARCH_CLPS711X || COMPILE_TEST
+ help
+ Generic PWM framework driver for Cirrus Logic CLPS711X.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-clps711x.
+
config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX
@@ -80,6 +89,16 @@ config PWM_EP93XX
To compile this driver as a module, choose M here: the module
will be called pwm-ep93xx.
+config PWM_FSL_FTM
+ tristate "Freescale FlexTimer Module (FTM) PWM support"
+ depends on OF
+ help
+ Generic FTM PWM framework driver for Freescale VF610 and
+ Layerscape LS-1 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-fsl-ftm.
+
config PWM_IMX
tristate "i.MX PWM support"
depends on ARCH_MXC
@@ -119,6 +138,16 @@ config PWM_LPC32XX
To compile this driver as a module, choose M here: the module
will be called pwm-lpc32xx.
+config PWM_LPSS
+ tristate "Intel LPSS PWM support"
+ depends on ACPI
+ help
+ Generic PWM framework driver for Intel Low Power Subsystem PWM
+ controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpss.
+
config PWM_MXS
tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF
@@ -160,6 +189,7 @@ config PWM_PXA
config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support"
depends on ARCH_SHMOBILE || COMPILE_TEST
+ depends on HAS_IOMEM
help
This driver exposes the Timer Pulse Unit (TPU) PWM controller found
in Renesas chips through the PWM API.
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index d8906ec69976..e57d2c38a794 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -4,11 +4,14 @@ obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
+obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
+obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
+obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index bf4144a14661..0adc952cc4ef 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -32,6 +32,7 @@
/* Bit field in CMR */
#define PWM_CMR_CPOL (1 << 9)
#define PWM_CMR_UPD_CDTY (1 << 10)
+#define PWM_CMR_CPRE_MSK 0xF
/* The following registers for PWM v1 */
#define PWMV1_CDTY 0x04
@@ -104,6 +105,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long clk_rate, prd, dty;
unsigned long long div;
unsigned int pres = 0;
+ u32 val;
int ret;
if (test_bit(PWMF_ENABLED, &pwm->flags) && (period_ns != pwm->period)) {
@@ -131,7 +133,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
prd = div;
div *= duty_ns;
do_div(div, period_ns);
- dty = div;
+ dty = prd - div;
ret = clk_enable(atmel_pwm->clk);
if (ret) {
@@ -139,7 +141,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
- atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, pres);
+ /* It is necessary to preserve CPOL, inside CMR */
+ val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+ val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
atmel_pwm->config(chip, pwm, dty, prd);
clk_disable(atmel_pwm->clk);
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
new file mode 100644
index 000000000000..fafb6a0111b0
--- /dev/null
+++ b/drivers/pwm/pwm-clps711x.c
@@ -0,0 +1,176 @@
+/*
+ * Cirrus Logic CLPS711X PWM driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct clps711x_chip {
+ struct pwm_chip chip;
+ void __iomem *pmpcon;
+ struct clk *clk;
+ spinlock_t lock;
+};
+
+static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct clps711x_chip, chip);
+}
+
+static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
+{
+ /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
+ u32 shift = (n + 1) * 4;
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ tmp = readl(priv->pmpcon);
+ tmp &= ~(0xf << shift);
+ tmp |= v << shift;
+ writel(tmp, priv->pmpcon);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
+{
+ /* Duty cycle 0..15 max */
+ return DIV_ROUND_CLOSEST(v * 0xf, pwm_get_period(pwm));
+}
+
+static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int freq = clk_get_rate(priv->clk);
+
+ if (!freq)
+ return -EINVAL;
+
+ /* Store constant period value */
+ pwm_set_period(pwm, DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq));
+
+ return 0;
+}
+
+static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int duty;
+
+ if (period_ns != pwm_get_period(pwm))
+ return -EINVAL;
+
+ duty = clps711x_get_duty(pwm, duty_ns);
+ clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
+
+ return 0;
+}
+
+static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int duty;
+
+ duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
+ clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
+
+ return 0;
+}
+
+static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+
+ clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
+}
+
+static const struct pwm_ops clps711x_pwm_ops = {
+ .request = clps711x_pwm_request,
+ .config = clps711x_pwm_config,
+ .enable = clps711x_pwm_enable,
+ .disable = clps711x_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
+ const struct of_phandle_args *args)
+{
+ if (args->args[0] >= chip->npwm)
+ return ERR_PTR(-EINVAL);
+
+ return pwm_request_from_chip(chip, args->args[0], NULL);
+}
+
+static int clps711x_pwm_probe(struct platform_device *pdev)
+{
+ struct clps711x_chip *priv;
+ struct resource *res;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->pmpcon = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->pmpcon))
+ return PTR_ERR(priv->pmpcon);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->chip.ops = &clps711x_pwm_ops;
+ priv->chip.dev = &pdev->dev;
+ priv->chip.base = -1;
+ priv->chip.npwm = 2;
+ priv->chip.of_xlate = clps711x_pwm_xlate;
+ priv->chip.of_pwm_n_cells = 1;
+
+ spin_lock_init(&priv->lock);
+
+ platform_set_drvdata(pdev, priv);
+
+ return pwmchip_add(&priv->chip);
+}
+
+static int clps711x_pwm_remove(struct platform_device *pdev)
+{
+ struct clps711x_chip *priv = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&priv->chip);
+}
+
+static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
+ { .compatible = "cirrus,clps711x-pwm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
+
+static struct platform_driver clps711x_pwm_driver = {
+ .driver = {
+ .name = "clps711x-pwm",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
+ },
+ .probe = clps711x_pwm_probe,
+ .remove = clps711x_pwm_remove,
+};
+module_platform_driver(clps711x_pwm_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
new file mode 100644
index 000000000000..420169e96b5f
--- /dev/null
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -0,0 +1,495 @@
+/*
+ * Freescale FlexTimer Module (FTM) PWM Driver
+ *
+ * Copyright 2012-2013 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define FTM_SC 0x00
+#define FTM_SC_CLK_MASK 0x3
+#define FTM_SC_CLK_SHIFT 3
+#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT)
+#define FTM_SC_PS_MASK 0x7
+#define FTM_SC_PS_SHIFT 0
+
+#define FTM_CNT 0x04
+#define FTM_MOD 0x08
+
+#define FTM_CSC_BASE 0x0C
+#define FTM_CSC_MSB BIT(5)
+#define FTM_CSC_MSA BIT(4)
+#define FTM_CSC_ELSB BIT(3)
+#define FTM_CSC_ELSA BIT(2)
+#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8))
+
+#define FTM_CV_BASE 0x10
+#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8))
+
+#define FTM_CNTIN 0x4C
+#define FTM_STATUS 0x50
+
+#define FTM_MODE 0x54
+#define FTM_MODE_FTMEN BIT(0)
+#define FTM_MODE_INIT BIT(2)
+#define FTM_MODE_PWMSYNC BIT(3)
+
+#define FTM_SYNC 0x58
+#define FTM_OUTINIT 0x5C
+#define FTM_OUTMASK 0x60
+#define FTM_COMBINE 0x64
+#define FTM_DEADTIME 0x68
+#define FTM_EXTTRIG 0x6C
+#define FTM_POL 0x70
+#define FTM_FMS 0x74
+#define FTM_FILTER 0x78
+#define FTM_FLTCTRL 0x7C
+#define FTM_QDCTRL 0x80
+#define FTM_CONF 0x84
+#define FTM_FLTPOL 0x88
+#define FTM_SYNCONF 0x8C
+#define FTM_INVCTRL 0x90
+#define FTM_SWOCTRL 0x94
+#define FTM_PWMLOAD 0x98
+
+enum fsl_pwm_clk {
+ FSL_PWM_CLK_SYS,
+ FSL_PWM_CLK_FIX,
+ FSL_PWM_CLK_EXT,
+ FSL_PWM_CLK_CNTEN,
+ FSL_PWM_CLK_MAX
+};
+
+struct fsl_pwm_chip {
+ struct pwm_chip chip;
+
+ struct mutex lock;
+
+ unsigned int use_count;
+ unsigned int cnt_select;
+ unsigned int clk_ps;
+
+ void __iomem *base;
+
+ int period_ns;
+
+ struct clk *clk[FSL_PWM_CLK_MAX];
+};
+
+static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct fsl_pwm_chip, chip);
+}
+
+static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
+}
+
+static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
+}
+
+static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc,
+ enum fsl_pwm_clk index)
+{
+ unsigned long sys_rate, cnt_rate;
+ unsigned long long ratio;
+
+ sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]);
+ if (!sys_rate)
+ return -EINVAL;
+
+ cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]);
+ if (!cnt_rate)
+ return -EINVAL;
+
+ switch (index) {
+ case FSL_PWM_CLK_SYS:
+ fpc->clk_ps = 1;
+ break;
+ case FSL_PWM_CLK_FIX:
+ ratio = 2 * cnt_rate - 1;
+ do_div(ratio, sys_rate);
+ fpc->clk_ps = ratio;
+ break;
+ case FSL_PWM_CLK_EXT:
+ ratio = 4 * cnt_rate - 1;
+ do_div(ratio, sys_rate);
+ fpc->clk_ps = ratio;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns)
+{
+ unsigned long long c, c0;
+
+ c = clk_get_rate(fpc->clk[fpc->cnt_select]);
+ c = c * period_ns;
+ do_div(c, 1000000000UL);
+
+ do {
+ c0 = c;
+ do_div(c0, (1 << fpc->clk_ps));
+ if (c0 <= 0xFFFF)
+ return (unsigned long)c0;
+ } while (++fpc->clk_ps < 8);
+
+ return 0;
+}
+
+static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns,
+ enum fsl_pwm_clk index)
+{
+ int ret;
+
+ ret = fsl_pwm_calculate_default_ps(fpc, index);
+ if (ret) {
+ dev_err(fpc->chip.dev,
+ "failed to calculate default prescaler: %d\n",
+ ret);
+ return 0;
+ }
+
+ return fsl_pwm_calculate_cycles(fpc, period_ns);
+}
+
+static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns)
+{
+ enum fsl_pwm_clk m0, m1;
+ unsigned long fix_rate, ext_rate, cycles;
+
+ cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns,
+ FSL_PWM_CLK_SYS);
+ if (cycles) {
+ fpc->cnt_select = FSL_PWM_CLK_SYS;
+ return cycles;
+ }
+
+ fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]);
+ ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]);
+
+ if (fix_rate > ext_rate) {
+ m0 = FSL_PWM_CLK_FIX;
+ m1 = FSL_PWM_CLK_EXT;
+ } else {
+ m0 = FSL_PWM_CLK_EXT;
+ m1 = FSL_PWM_CLK_FIX;
+ }
+
+ cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0);
+ if (cycles) {
+ fpc->cnt_select = m0;
+ return cycles;
+ }
+
+ fpc->cnt_select = m1;
+
+ return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1);
+}
+
+static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
+ unsigned long period_ns,
+ unsigned long duty_ns)
+{
+ unsigned long long val, duty;
+
+ val = readl(fpc->base + FTM_MOD);
+ duty = duty_ns * (val + 1);
+ do_div(duty, period_ns);
+
+ return (unsigned long)duty;
+}
+
+static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val, period, duty;
+
+ mutex_lock(&fpc->lock);
+
+ /*
+ * The Freescale FTM controller supports only a single period for
+ * all PWM channels, therefore incompatible changes need to be
+ * refused.
+ */
+ if (fpc->period_ns && fpc->period_ns != period_ns) {
+ dev_err(fpc->chip.dev,
+ "conflicting period requested for PWM %u\n",
+ pwm->hwpwm);
+ mutex_unlock(&fpc->lock);
+ return -EBUSY;
+ }
+
+ if (!fpc->period_ns && duty_ns) {
+ period = fsl_pwm_calculate_period(fpc, period_ns);
+ if (!period) {
+ dev_err(fpc->chip.dev, "failed to calculate period\n");
+ mutex_unlock(&fpc->lock);
+ return -EINVAL;
+ }
+
+ val = readl(fpc->base + FTM_SC);
+ val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT);
+ val |= fpc->clk_ps;
+ writel(val, fpc->base + FTM_SC);
+ writel(period - 1, fpc->base + FTM_MOD);
+
+ fpc->period_ns = period_ns;
+ }
+
+ mutex_unlock(&fpc->lock);
+
+ duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns);
+
+ writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm));
+ writel(duty, fpc->base + FTM_CV(pwm->hwpwm));
+
+ return 0;
+}
+
+static int fsl_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val;
+
+ val = readl(fpc->base + FTM_POL);
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ val |= BIT(pwm->hwpwm);
+ else
+ val &= ~BIT(pwm->hwpwm);
+
+ writel(val, fpc->base + FTM_POL);
+
+ return 0;
+}
+
+static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
+{
+ u32 val;
+ int ret;
+
+ if (fpc->use_count != 0)
+ return 0;
+
+ /* select counter clock source */
+ val = readl(fpc->base + FTM_SC);
+ val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
+ val |= FTM_SC_CLK(fpc->cnt_select);
+ writel(val, fpc->base + FTM_SC);
+
+ ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ if (ret) {
+ clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
+ return ret;
+ }
+
+ fpc->use_count++;
+
+ return 0;
+}
+
+static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val;
+ int ret;
+
+ mutex_lock(&fpc->lock);
+ val = readl(fpc->base + FTM_OUTMASK);
+ val &= ~BIT(pwm->hwpwm);
+ writel(val, fpc->base + FTM_OUTMASK);
+
+ ret = fsl_counter_clock_enable(fpc);
+ mutex_unlock(&fpc->lock);
+
+ return ret;
+}
+
+static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
+{
+ u32 val;
+
+ /*
+ * already disabled, do nothing
+ */
+ if (fpc->use_count == 0)
+ return;
+
+ /* there are still users, so can't disable yet */
+ if (--fpc->use_count > 0)
+ return;
+
+ /* no users left, disable PWM counter clock */
+ val = readl(fpc->base + FTM_SC);
+ val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
+ writel(val, fpc->base + FTM_SC);
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
+}
+
+static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ u32 val;
+
+ mutex_lock(&fpc->lock);
+ val = readl(fpc->base + FTM_OUTMASK);
+ val |= BIT(pwm->hwpwm);
+ writel(val, fpc->base + FTM_OUTMASK);
+
+ fsl_counter_clock_disable(fpc);
+
+ val = readl(fpc->base + FTM_OUTMASK);
+
+ if ((val & 0xFF) == 0xFF)
+ fpc->period_ns = 0;
+
+ mutex_unlock(&fpc->lock);
+}
+
+static const struct pwm_ops fsl_pwm_ops = {
+ .request = fsl_pwm_request,
+ .free = fsl_pwm_free,
+ .config = fsl_pwm_config,
+ .set_polarity = fsl_pwm_set_polarity,
+ .enable = fsl_pwm_enable,
+ .disable = fsl_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]);
+ if (ret)
+ return ret;
+
+ writel(0x00, fpc->base + FTM_CNTIN);
+ writel(0x00, fpc->base + FTM_OUTINIT);
+ writel(0xFF, fpc->base + FTM_OUTMASK);
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
+
+ return 0;
+}
+
+static int fsl_pwm_probe(struct platform_device *pdev)
+{
+ struct fsl_pwm_chip *fpc;
+ struct resource *res;
+ int ret;
+
+ fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
+ if (!fpc)
+ return -ENOMEM;
+
+ mutex_init(&fpc->lock);
+
+ fpc->chip.dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fpc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fpc->base))
+ return PTR_ERR(fpc->base);
+
+ fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
+ dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n");
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]);
+ }
+
+ fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]);
+
+ fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]);
+
+ fpc->clk[FSL_PWM_CLK_CNTEN] =
+ devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]);
+
+ fpc->chip.ops = &fsl_pwm_ops;
+ fpc->chip.of_xlate = of_pwm_xlate_with_flags;
+ fpc->chip.of_pwm_n_cells = 3;
+ fpc->chip.base = -1;
+ fpc->chip.npwm = 8;
+
+ ret = pwmchip_add(&fpc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, fpc);
+
+ return fsl_pwm_init(fpc);
+}
+
+static int fsl_pwm_remove(struct platform_device *pdev)
+{
+ struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&fpc->chip);
+}
+
+static const struct of_device_id fsl_pwm_dt_ids[] = {
+ { .compatible = "fsl,vf610-ftm-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);
+
+static struct platform_driver fsl_pwm_driver = {
+ .driver = {
+ .name = "fsl-ftm-pwm",
+ .of_match_table = fsl_pwm_dt_ids,
+ },
+ .probe = fsl_pwm_probe,
+ .remove = fsl_pwm_remove,
+};
+module_platform_driver(fsl_pwm_driver);
+
+MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver");
+MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>");
+MODULE_ALIAS("platform:fsl-ftm-pwm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c
new file mode 100644
index 000000000000..449e372050a0
--- /dev/null
+++ b/drivers/pwm/pwm-lpss.c
@@ -0,0 +1,183 @@
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Author: Chew Kean Ho <kean.ho.chew@intel.com>
+ * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
+ * Author: Chew Chiau Ee <chiau.ee.chew@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+
+#define PWM 0x00000000
+#define PWM_ENABLE BIT(31)
+#define PWM_SW_UPDATE BIT(30)
+#define PWM_BASE_UNIT_SHIFT 8
+#define PWM_BASE_UNIT_MASK 0x00ffff00
+#define PWM_ON_TIME_DIV_MASK 0x000000ff
+#define PWM_DIVISION_CORRECTION 0x2
+#define PWM_LIMIT (0x8000 + PWM_DIVISION_CORRECTION)
+#define NSECS_PER_SEC 1000000000UL
+
+struct pwm_lpss_chip {
+ struct pwm_chip chip;
+ void __iomem *regs;
+ struct clk *clk;
+};
+
+static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
+{
+ return container_of(chip, struct pwm_lpss_chip, chip);
+}
+
+static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ u8 on_time_div;
+ unsigned long c;
+ unsigned long long base_unit, freq = NSECS_PER_SEC;
+ u32 ctrl;
+
+ do_div(freq, period_ns);
+
+ /* The equation is: base_unit = ((freq / c) * 65536) + correction */
+ base_unit = freq * 65536;
+
+ c = clk_get_rate(lpwm->clk);
+ if (!c)
+ return -EINVAL;
+
+ do_div(base_unit, c);
+ base_unit += PWM_DIVISION_CORRECTION;
+ if (base_unit > PWM_LIMIT)
+ return -EINVAL;
+
+ if (duty_ns <= 0)
+ duty_ns = 1;
+ on_time_div = 255 - (255 * duty_ns / period_ns);
+
+ ctrl = readl(lpwm->regs + PWM);
+ ctrl &= ~(PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK);
+ ctrl |= (u16) base_unit << PWM_BASE_UNIT_SHIFT;
+ ctrl |= on_time_div;
+ /* request PWM to update on next cycle */
+ ctrl |= PWM_SW_UPDATE;
+ writel(ctrl, lpwm->regs + PWM);
+
+ return 0;
+}
+
+static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ u32 ctrl;
+ int ret;
+
+ ret = clk_prepare_enable(lpwm->clk);
+ if (ret)
+ return ret;
+
+ ctrl = readl(lpwm->regs + PWM);
+ writel(ctrl | PWM_ENABLE, lpwm->regs + PWM);
+
+ return 0;
+}
+
+static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ u32 ctrl;
+
+ ctrl = readl(lpwm->regs + PWM);
+ writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
+
+ clk_disable_unprepare(lpwm->clk);
+}
+
+static const struct pwm_ops pwm_lpss_ops = {
+ .config = pwm_lpss_config,
+ .enable = pwm_lpss_enable,
+ .disable = pwm_lpss_disable,
+ .owner = THIS_MODULE,
+};
+
+static const struct acpi_device_id pwm_lpss_acpi_match[] = {
+ { "80860F09", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
+
+static int pwm_lpss_probe(struct platform_device *pdev)
+{
+ struct pwm_lpss_chip *lpwm;
+ struct resource *r;
+ int ret;
+
+ lpwm = devm_kzalloc(&pdev->dev, sizeof(*lpwm), GFP_KERNEL);
+ if (!lpwm)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ lpwm->regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(lpwm->regs))
+ return PTR_ERR(lpwm->regs);
+
+ lpwm->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(lpwm->clk)) {
+ dev_err(&pdev->dev, "failed to get PWM clock\n");
+ return PTR_ERR(lpwm->clk);
+ }
+
+ lpwm->chip.dev = &pdev->dev;
+ lpwm->chip.ops = &pwm_lpss_ops;
+ lpwm->chip.base = -1;
+ lpwm->chip.npwm = 1;
+
+ ret = pwmchip_add(&lpwm->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lpwm);
+ return 0;
+}
+
+static int pwm_lpss_remove(struct platform_device *pdev)
+{
+ struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
+ u32 ctrl;
+
+ ctrl = readl(lpwm->regs + PWM);
+ writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
+
+ return pwmchip_remove(&lpwm->chip);
+}
+
+static struct platform_driver pwm_lpss_driver = {
+ .driver = {
+ .name = "pwm-lpss",
+ .acpi_match_table = pwm_lpss_acpi_match,
+ },
+ .probe = pwm_lpss_probe,
+ .remove = pwm_lpss_remove,
+};
+module_platform_driver(pwm_lpss_driver);
+
+MODULE_DESCRIPTION("PWM driver for Intel LPSS");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pwm-lpss");
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index 8d995731cef8..cd356d870244 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -127,12 +127,12 @@ static struct pwm_ops pxa_pwm_ops = {
#ifdef CONFIG_OF
/*
- * Device tree users must create one device instance for each pwm channel.
+ * Device tree users must create one device instance for each PWM channel.
* Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
* code that this is a single channel pxa25x-pwm. Currently all devices are
* supported identically.
*/
-static struct of_device_id pwm_of_match[] = {
+static const struct of_device_id pwm_of_match[] = {
{ .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]},
{ .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index b59639e0c029..d66529a995a1 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -598,9 +598,8 @@ static int pwm_samsung_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops pwm_samsung_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pwm_samsung_suspend, pwm_samsung_resume)
-};
+static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend,
+ pwm_samsung_resume);
static struct platform_driver pwm_samsung_driver = {
.driver = {
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index ff7cbf2d28e3..1753dc693c15 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -2256,6 +2256,7 @@ static int tsi721_setup_mport(struct tsi721_device *priv)
mport->phy_type = RIO_PHY_SERIAL;
mport->priv = (void *)priv;
mport->phys_efptr = 0x100;
+ mport->dev.parent = &pdev->dev;
priv->mport = mport;
INIT_LIST_HEAD(&mport->dbells);
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
index 7061ac0ad428..0305675270ee 100644
--- a/drivers/rapidio/devices/tsi721.h
+++ b/drivers/rapidio/devices/tsi721.h
@@ -644,6 +644,9 @@ enum tsi721_smsg_int_flag {
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+#define TSI721_BDMA_BD_RING_SZ 128
+#define TSI721_BDMA_MAX_BCOUNT (TSI721_DMAD_BCOUNT1 + 1)
+
struct tsi721_tx_desc {
struct dma_async_tx_descriptor txd;
struct tsi721_dma_desc *hw_desc;
@@ -652,6 +655,7 @@ struct tsi721_tx_desc {
u64 rio_addr;
/* upper 2-bits of 66-bit RIO address */
u8 rio_addr_u;
+ u32 bcount;
bool interrupt;
struct list_head desc_node;
struct list_head tx_list;
diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c
index 91245f5dbe81..9b60b1f3261c 100644
--- a/drivers/rapidio/devices/tsi721_dma.c
+++ b/drivers/rapidio/devices/tsi721_dma.c
@@ -304,35 +304,17 @@ struct tsi721_tx_desc *tsi721_desc_get(struct tsi721_bdma_chan *bdma_chan)
}
static int
-tsi721_fill_desc(struct tsi721_bdma_chan *bdma_chan,
- struct tsi721_tx_desc *desc, struct scatterlist *sg,
+tsi721_desc_fill_init(struct tsi721_tx_desc *desc, struct scatterlist *sg,
enum dma_rtype rtype, u32 sys_size)
{
struct tsi721_dma_desc *bd_ptr = desc->hw_desc;
u64 rio_addr;
- if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) {
- dev_err(bdma_chan->dchan.device->dev,
- "SG element is too large\n");
- return -EINVAL;
- }
-
- dev_dbg(bdma_chan->dchan.device->dev,
- "desc: 0x%llx, addr: 0x%llx len: 0x%x\n",
- (u64)desc->txd.phys, (unsigned long long)sg_dma_address(sg),
- sg_dma_len(sg));
-
- dev_dbg(bdma_chan->dchan.device->dev,
- "bd_ptr = %p did=%d raddr=0x%llx\n",
- bd_ptr, desc->destid, desc->rio_addr);
-
/* Initialize DMA descriptor */
bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) |
(rtype << 19) | desc->destid);
- if (desc->interrupt)
- bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF);
bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) |
- (sys_size << 26) | sg_dma_len(sg));
+ (sys_size << 26));
rio_addr = (desc->rio_addr >> 2) |
((u64)(desc->rio_addr_u & 0x3) << 62);
bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff);
@@ -346,6 +328,20 @@ tsi721_fill_desc(struct tsi721_bdma_chan *bdma_chan,
return 0;
}
+static int
+tsi721_desc_fill_end(struct tsi721_tx_desc *desc)
+{
+ struct tsi721_dma_desc *bd_ptr = desc->hw_desc;
+
+ /* Update DMA descriptor */
+ if (desc->interrupt)
+ bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF);
+ bd_ptr->bcount |= cpu_to_le32(desc->bcount & TSI721_DMAD_BCOUNT1);
+
+ return 0;
+}
+
+
static void tsi721_dma_chain_complete(struct tsi721_bdma_chan *bdma_chan,
struct tsi721_tx_desc *desc)
{
@@ -674,6 +670,7 @@ struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan,
unsigned int i;
u32 sys_size = dma_to_mport(dchan->device)->sys_size;
enum dma_rtype rtype;
+ dma_addr_t next_addr = -1;
if (!sgl || !sg_len) {
dev_err(dchan->device->dev, "%s: No SG list\n", __func__);
@@ -704,36 +701,84 @@ struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan,
for_each_sg(sgl, sg, sg_len, i) {
int err;
- dev_dbg(dchan->device->dev, "%s: sg #%d\n", __func__, i);
+ if (sg_dma_len(sg) > TSI721_BDMA_MAX_BCOUNT) {
+ dev_err(dchan->device->dev,
+ "%s: SG entry %d is too large\n", __func__, i);
+ goto err_desc_put;
+ }
+
+ /*
+ * If this sg entry forms contiguous block with previous one,
+ * try to merge it into existing DMA descriptor
+ */
+ if (desc) {
+ if (next_addr == sg_dma_address(sg) &&
+ desc->bcount + sg_dma_len(sg) <=
+ TSI721_BDMA_MAX_BCOUNT) {
+ /* Adjust byte count of the descriptor */
+ desc->bcount += sg_dma_len(sg);
+ goto entry_done;
+ }
+
+ /*
+ * Finalize this descriptor using total
+ * byte count value.
+ */
+ tsi721_desc_fill_end(desc);
+ dev_dbg(dchan->device->dev, "%s: desc final len: %d\n",
+ __func__, desc->bcount);
+ }
+
+ /*
+ * Obtain and initialize a new descriptor
+ */
desc = tsi721_desc_get(bdma_chan);
if (!desc) {
dev_err(dchan->device->dev,
- "Not enough descriptors available\n");
- goto err_desc_get;
+ "%s: Failed to get new descriptor for SG %d\n",
+ __func__, i);
+ goto err_desc_put;
}
- if (sg_is_last(sg))
- desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0;
- else
- desc->interrupt = false;
-
desc->destid = rext->destid;
desc->rio_addr = rio_addr;
desc->rio_addr_u = 0;
+ desc->bcount = sg_dma_len(sg);
+
+ dev_dbg(dchan->device->dev,
+ "sg%d desc: 0x%llx, addr: 0x%llx len: %d\n",
+ i, (u64)desc->txd.phys,
+ (unsigned long long)sg_dma_address(sg),
+ sg_dma_len(sg));
+
+ dev_dbg(dchan->device->dev,
+ "bd_ptr = %p did=%d raddr=0x%llx\n",
+ desc->hw_desc, desc->destid, desc->rio_addr);
- err = tsi721_fill_desc(bdma_chan, desc, sg, rtype, sys_size);
+ err = tsi721_desc_fill_init(desc, sg, rtype, sys_size);
if (err) {
dev_err(dchan->device->dev,
"Failed to build desc: %d\n", err);
- goto err_desc_get;
+ goto err_desc_put;
}
- rio_addr += sg_dma_len(sg);
+ next_addr = sg_dma_address(sg);
if (!first)
first = desc;
else
list_add_tail(&desc->desc_node, &first->tx_list);
+
+entry_done:
+ if (sg_is_last(sg)) {
+ desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0;
+ tsi721_desc_fill_end(desc);
+ dev_dbg(dchan->device->dev, "%s: desc final len: %d\n",
+ __func__, desc->bcount);
+ } else {
+ rio_addr += sg_dma_len(sg);
+ next_addr += sg_dma_len(sg);
+ }
}
first->txd.cookie = -EBUSY;
@@ -741,7 +786,7 @@ struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan,
return &first->txd;
-err_desc_get:
+err_desc_put:
tsi721_desc_put(bdma_chan, first);
return NULL;
}
@@ -792,7 +837,7 @@ int tsi721_register_dma(struct tsi721_device *priv)
if (i == TSI721_DMACH_MAINT)
continue;
- bdma_chan->bd_num = 64;
+ bdma_chan->bd_num = TSI721_BDMA_BD_RING_SZ;
bdma_chan->regs = priv->regs + TSI721_DMAC_BASE(i);
bdma_chan->dchan.device = &mport->dma;
diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c
index c9ae692d3451..f301f059bb85 100644
--- a/drivers/rapidio/rio-driver.c
+++ b/drivers/rapidio/rio-driver.c
@@ -167,7 +167,6 @@ void rio_unregister_driver(struct rio_driver *rdrv)
void rio_attach_device(struct rio_dev *rdev)
{
rdev->dev.bus = &rio_bus_type;
- rdev->dev.parent = &rio_bus;
}
EXPORT_SYMBOL_GPL(rio_attach_device);
@@ -216,9 +215,12 @@ static int rio_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-struct device rio_bus = {
- .init_name = "rapidio",
+struct class rio_mport_class = {
+ .name = "rapidio_port",
+ .owner = THIS_MODULE,
+ .dev_groups = rio_mport_groups,
};
+EXPORT_SYMBOL_GPL(rio_mport_class);
struct bus_type rio_bus_type = {
.name = "rapidio",
@@ -233,14 +235,20 @@ struct bus_type rio_bus_type = {
/**
* rio_bus_init - Register the RapidIO bus with the device model
*
- * Registers the RIO bus device and RIO bus type with the Linux
+ * Registers the RIO mport device class and RIO bus type with the Linux
* device model.
*/
static int __init rio_bus_init(void)
{
- if (device_register(&rio_bus) < 0)
- printk("RIO: failed to register RIO bus device\n");
- return bus_register(&rio_bus_type);
+ int ret;
+
+ ret = class_register(&rio_mport_class);
+ if (!ret) {
+ ret = bus_register(&rio_bus_type);
+ if (ret)
+ class_unregister(&rio_mport_class);
+ }
+ return ret;
}
postcore_initcall(rio_bus_init);
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index d3a6539a77cc..47a1b2ea76c4 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -461,6 +461,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rdev->comp_tag & RIO_CTAG_UDEVID);
}
+ rdev->dev.parent = &port->dev;
rio_attach_device(rdev);
device_initialize(&rdev->dev);
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
index e0221c6d0cc2..cdb005c0094d 100644
--- a/drivers/rapidio/rio-sysfs.c
+++ b/drivers/rapidio/rio-sysfs.c
@@ -341,3 +341,43 @@ const struct attribute_group *rio_bus_groups[] = {
&rio_bus_group,
NULL,
};
+
+static ssize_t
+port_destid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct rio_mport *mport = to_rio_mport(dev);
+
+ if (mport)
+ return sprintf(buf, "0x%04x\n", mport->host_deviceid);
+ else
+ return -ENODEV;
+}
+static DEVICE_ATTR_RO(port_destid);
+
+static ssize_t sys_size_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct rio_mport *mport = to_rio_mport(dev);
+
+ if (mport)
+ return sprintf(buf, "%u\n", mport->sys_size);
+ else
+ return -ENODEV;
+}
+static DEVICE_ATTR_RO(sys_size);
+
+static struct attribute *rio_mport_attrs[] = {
+ &dev_attr_port_destid.attr,
+ &dev_attr_sys_size.attr,
+ NULL,
+};
+
+static const struct attribute_group rio_mport_group = {
+ .attrs = rio_mport_attrs,
+};
+
+const struct attribute_group *rio_mport_groups[] = {
+ &rio_mport_group,
+ NULL,
+};
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 2e8a20cac588..a54ba0494dd3 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -1884,6 +1884,7 @@ static int rio_get_hdid(int index)
int rio_register_mport(struct rio_mport *port)
{
struct rio_scan_node *scan = NULL;
+ int res = 0;
if (next_portid >= RIO_MAX_MPORTS) {
pr_err("RIO: reached specified max number of mports\n");
@@ -1894,6 +1895,16 @@ int rio_register_mport(struct rio_mport *port)
port->host_deviceid = rio_get_hdid(port->id);
port->nscan = NULL;
+ dev_set_name(&port->dev, "rapidio%d", port->id);
+ port->dev.class = &rio_mport_class;
+
+ res = device_register(&port->dev);
+ if (res)
+ dev_err(&port->dev, "RIO: mport%d registration failed ERR=%d\n",
+ port->id, res);
+ else
+ dev_dbg(&port->dev, "RIO: mport%d registered\n", port->id);
+
mutex_lock(&rio_mport_list_lock);
list_add_tail(&port->node, &rio_mports);
diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h
index 5f99d22ad0b0..2d0550e08ea2 100644
--- a/drivers/rapidio/rio.h
+++ b/drivers/rapidio/rio.h
@@ -50,6 +50,7 @@ extern int rio_mport_scan(int mport_id);
/* Structures internal to the RIO core code */
extern const struct attribute_group *rio_dev_groups[];
extern const struct attribute_group *rio_bus_groups[];
+extern const struct attribute_group *rio_mport_groups[];
#define RIO_GET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16))
#define RIO_SET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x000000ff) << 16))
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c9d04f797862..0615f50a14cd 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -11,3 +11,5 @@ menuconfig RESET_CONTROLLER
via GPIOs or SoC-internal reset controller modules.
If unsure, say no.
+
+source "drivers/reset/sti/Kconfig"
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index cc29832c9638..4f60caf750ce 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_RESET_CONTROLLER) += core.o
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
+obj-$(CONFIG_ARCH_STI) += sti/
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index d1b6089a0ef8..baeaf82d40d9 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -43,7 +43,7 @@ struct reset_control {
* This simple translation function should be used for reset controllers
* with 1:1 mapping, where reset lines can be indexed by number without gaps.
*/
-int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
+static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
@@ -54,7 +54,6 @@ int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
return reset_spec->args[0];
}
-EXPORT_SYMBOL_GPL(of_reset_simple_xlate);
/**
* reset_controller_register - register a reset controller device
@@ -127,15 +126,16 @@ int reset_control_deassert(struct reset_control *rstc)
EXPORT_SYMBOL_GPL(reset_control_deassert);
/**
- * reset_control_get - Lookup and obtain a reference to a reset controller.
- * @dev: device to be reset by the controller
+ * of_reset_control_get - Lookup and obtain a reference to a reset controller.
+ * @node: device to be reset by the controller
* @id: reset line name
*
* Returns a struct reset_control or IS_ERR() condition containing errno.
*
* Use of id names is optional.
*/
-struct reset_control *reset_control_get(struct device *dev, const char *id)
+struct reset_control *of_reset_control_get(struct device_node *node,
+ const char *id)
{
struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
struct reset_controller_dev *r, *rcdev;
@@ -144,13 +144,10 @@ struct reset_control *reset_control_get(struct device *dev, const char *id)
int rstc_id;
int ret;
- if (!dev)
- return ERR_PTR(-EINVAL);
-
if (id)
- index = of_property_match_string(dev->of_node,
+ index = of_property_match_string(node,
"reset-names", id);
- ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells",
+ ret = of_parse_phandle_with_args(node, "resets", "#reset-cells",
index, &args);
if (ret)
return ERR_PTR(ret);
@@ -167,7 +164,7 @@ struct reset_control *reset_control_get(struct device *dev, const char *id)
if (!rcdev) {
mutex_unlock(&reset_controller_list_mutex);
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-EPROBE_DEFER);
}
rstc_id = rcdev->of_xlate(rcdev, &args);
@@ -185,12 +182,35 @@ struct reset_control *reset_control_get(struct device *dev, const char *id)
return ERR_PTR(-ENOMEM);
}
- rstc->dev = dev;
rstc->rcdev = rcdev;
rstc->id = rstc_id;
return rstc;
}
+EXPORT_SYMBOL_GPL(of_reset_control_get);
+
+/**
+ * reset_control_get - Lookup and obtain a reference to a reset controller.
+ * @dev: device to be reset by the controller
+ * @id: reset line name
+ *
+ * Returns a struct reset_control or IS_ERR() condition containing errno.
+ *
+ * Use of id names is optional.
+ */
+struct reset_control *reset_control_get(struct device *dev, const char *id)
+{
+ struct reset_control *rstc;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ rstc = of_reset_control_get(dev->of_node, id);
+ if (!IS_ERR(rstc))
+ rstc->dev = dev;
+
+ return rstc;
+}
EXPORT_SYMBOL_GPL(reset_control_get);
/**
@@ -243,33 +263,6 @@ struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
}
EXPORT_SYMBOL_GPL(devm_reset_control_get);
-static int devm_reset_control_match(struct device *dev, void *res, void *data)
-{
- struct reset_control **rstc = res;
- if (WARN_ON(!rstc || !*rstc))
- return 0;
- return *rstc == data;
-}
-
-/**
- * devm_reset_control_put - resource managed reset_control_put()
- * @rstc: reset controller to free
- *
- * Deallocate a reset control allocated withd devm_reset_control_get().
- * This function will not need to be called normally, as devres will take
- * care of freeing the resource.
- */
-void devm_reset_control_put(struct reset_control *rstc)
-{
- int ret;
-
- ret = devres_release(rstc->dev, devm_reset_control_release,
- devm_reset_control_match, rstc);
- if (ret)
- WARN_ON(ret);
-}
-EXPORT_SYMBOL_GPL(devm_reset_control_put);
-
/**
* device_reset - find reset controller associated with the device
* and perform reset
diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig
new file mode 100644
index 000000000000..88d2d0316613
--- /dev/null
+++ b/drivers/reset/sti/Kconfig
@@ -0,0 +1,15 @@
+if ARCH_STI
+
+config STI_RESET_SYSCFG
+ bool
+ select RESET_CONTROLLER
+
+config STIH415_RESET
+ bool
+ select STI_RESET_SYSCFG
+
+config STIH416_RESET
+ bool
+ select STI_RESET_SYSCFG
+
+endif
diff --git a/drivers/reset/sti/Makefile b/drivers/reset/sti/Makefile
new file mode 100644
index 000000000000..be1c97647871
--- /dev/null
+++ b/drivers/reset/sti/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_STI_RESET_SYSCFG) += reset-syscfg.o
+
+obj-$(CONFIG_STIH415_RESET) += reset-stih415.o
+obj-$(CONFIG_STIH416_RESET) += reset-stih416.o
diff --git a/drivers/reset/sti/reset-stih415.c b/drivers/reset/sti/reset-stih415.c
new file mode 100644
index 000000000000..e6f6c41abe12
--- /dev/null
+++ b/drivers/reset/sti/reset-stih415.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/reset-controller/stih415-resets.h>
+
+#include "reset-syscfg.h"
+
+/*
+ * STiH415 Peripheral powerdown definitions.
+ */
+static const char stih415_front[] = "st,stih415-front-syscfg";
+static const char stih415_rear[] = "st,stih415-rear-syscfg";
+static const char stih415_sbc[] = "st,stih415-sbc-syscfg";
+static const char stih415_lpm[] = "st,stih415-lpm-syscfg";
+
+#define STIH415_PDN_FRONT(_bit) \
+ _SYSCFG_RST_CH(stih415_front, SYSCFG_114, _bit, SYSSTAT_187, _bit)
+
+#define STIH415_PDN_REAR(_cntl, _stat) \
+ _SYSCFG_RST_CH(stih415_rear, SYSCFG_336, _cntl, SYSSTAT_384, _stat)
+
+#define STIH415_SRST_REAR(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_rear, _reg, _bit)
+
+#define STIH415_SRST_SBC(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_sbc, _reg, _bit)
+
+#define STIH415_SRST_FRONT(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_front, _reg, _bit)
+
+#define STIH415_SRST_LPM(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih415_lpm, _reg, _bit)
+
+#define SYSCFG_114 0x38 /* Powerdown request EMI/NAND/Keyscan */
+#define SYSSTAT_187 0x15c /* Powerdown status EMI/NAND/Keyscan */
+
+#define SYSCFG_336 0x90 /* Powerdown request USB/SATA/PCIe */
+#define SYSSTAT_384 0x150 /* Powerdown status USB/SATA/PCIe */
+
+#define SYSCFG_376 0x130 /* Reset generator 0 control 0 */
+#define SYSCFG_166 0x108 /* Softreset Ethernet 0 */
+#define SYSCFG_31 0x7c /* Softreset Ethernet 1 */
+#define LPM_SYSCFG_1 0x4 /* Softreset IRB */
+
+static const struct syscfg_reset_channel_data stih415_powerdowns[] = {
+ [STIH415_EMISS_POWERDOWN] = STIH415_PDN_FRONT(0),
+ [STIH415_NAND_POWERDOWN] = STIH415_PDN_FRONT(1),
+ [STIH415_KEYSCAN_POWERDOWN] = STIH415_PDN_FRONT(2),
+ [STIH415_USB0_POWERDOWN] = STIH415_PDN_REAR(0, 0),
+ [STIH415_USB1_POWERDOWN] = STIH415_PDN_REAR(1, 1),
+ [STIH415_USB2_POWERDOWN] = STIH415_PDN_REAR(2, 2),
+ [STIH415_SATA0_POWERDOWN] = STIH415_PDN_REAR(3, 3),
+ [STIH415_SATA1_POWERDOWN] = STIH415_PDN_REAR(4, 4),
+ [STIH415_PCIE_POWERDOWN] = STIH415_PDN_REAR(5, 8),
+};
+
+static const struct syscfg_reset_channel_data stih415_softresets[] = {
+ [STIH415_ETH0_SOFTRESET] = STIH415_SRST_FRONT(SYSCFG_166, 0),
+ [STIH415_ETH1_SOFTRESET] = STIH415_SRST_SBC(SYSCFG_31, 0),
+ [STIH415_IRB_SOFTRESET] = STIH415_SRST_LPM(LPM_SYSCFG_1, 6),
+ [STIH415_USB0_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 9),
+ [STIH415_USB1_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 10),
+ [STIH415_USB2_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 11),
+};
+
+static struct syscfg_reset_controller_data stih415_powerdown_controller = {
+ .wait_for_ack = true,
+ .nr_channels = ARRAY_SIZE(stih415_powerdowns),
+ .channels = stih415_powerdowns,
+};
+
+static struct syscfg_reset_controller_data stih415_softreset_controller = {
+ .wait_for_ack = false,
+ .active_low = true,
+ .nr_channels = ARRAY_SIZE(stih415_softresets),
+ .channels = stih415_softresets,
+};
+
+static struct of_device_id stih415_reset_match[] = {
+ { .compatible = "st,stih415-powerdown",
+ .data = &stih415_powerdown_controller, },
+ { .compatible = "st,stih415-softreset",
+ .data = &stih415_softreset_controller, },
+ {},
+};
+
+static struct platform_driver stih415_reset_driver = {
+ .probe = syscfg_reset_probe,
+ .driver = {
+ .name = "reset-stih415",
+ .owner = THIS_MODULE,
+ .of_match_table = stih415_reset_match,
+ },
+};
+
+static int __init stih415_reset_init(void)
+{
+ return platform_driver_register(&stih415_reset_driver);
+}
+arch_initcall(stih415_reset_init);
diff --git a/drivers/reset/sti/reset-stih416.c b/drivers/reset/sti/reset-stih416.c
new file mode 100644
index 000000000000..fe3bf02bdc8c
--- /dev/null
+++ b/drivers/reset/sti/reset-stih416.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/reset-controller/stih416-resets.h>
+
+#include "reset-syscfg.h"
+
+/*
+ * STiH416 Peripheral powerdown definitions.
+ */
+static const char stih416_front[] = "st,stih416-front-syscfg";
+static const char stih416_rear[] = "st,stih416-rear-syscfg";
+static const char stih416_sbc[] = "st,stih416-sbc-syscfg";
+static const char stih416_lpm[] = "st,stih416-lpm-syscfg";
+static const char stih416_cpu[] = "st,stih416-cpu-syscfg";
+
+#define STIH416_PDN_FRONT(_bit) \
+ _SYSCFG_RST_CH(stih416_front, SYSCFG_1500, _bit, SYSSTAT_1578, _bit)
+
+#define STIH416_PDN_REAR(_cntl, _stat) \
+ _SYSCFG_RST_CH(stih416_rear, SYSCFG_2525, _cntl, SYSSTAT_2583, _stat)
+
+#define SYSCFG_1500 0x7d0 /* Powerdown request EMI/NAND/Keyscan */
+#define SYSSTAT_1578 0x908 /* Powerdown status EMI/NAND/Keyscan */
+
+#define SYSCFG_2525 0x834 /* Powerdown request USB/SATA/PCIe */
+#define SYSSTAT_2583 0x91c /* Powerdown status USB/SATA/PCIe */
+
+#define SYSCFG_2552 0x8A0 /* Reset Generator control 0 */
+#define SYSCFG_1539 0x86c /* Softreset Ethernet 0 */
+#define SYSCFG_510 0x7f8 /* Softreset Ethernet 1 */
+#define LPM_SYSCFG_1 0x4 /* Softreset IRB */
+#define SYSCFG_2553 0x8a4 /* Softreset SATA0/1, PCIE0/1 */
+#define SYSCFG_7563 0x8cc /* MPE softresets 0 */
+#define SYSCFG_7564 0x8d0 /* MPE softresets 1 */
+
+#define STIH416_SRST_CPU(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_cpu, _reg, _bit)
+
+#define STIH416_SRST_FRONT(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_front, _reg, _bit)
+
+#define STIH416_SRST_REAR(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_rear, _reg, _bit)
+
+#define STIH416_SRST_LPM(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_lpm, _reg, _bit)
+
+#define STIH416_SRST_SBC(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih416_sbc, _reg, _bit)
+
+static const struct syscfg_reset_channel_data stih416_powerdowns[] = {
+ [STIH416_EMISS_POWERDOWN] = STIH416_PDN_FRONT(0),
+ [STIH416_NAND_POWERDOWN] = STIH416_PDN_FRONT(1),
+ [STIH416_KEYSCAN_POWERDOWN] = STIH416_PDN_FRONT(2),
+ [STIH416_USB0_POWERDOWN] = STIH416_PDN_REAR(0, 0),
+ [STIH416_USB1_POWERDOWN] = STIH416_PDN_REAR(1, 1),
+ [STIH416_USB2_POWERDOWN] = STIH416_PDN_REAR(2, 2),
+ [STIH416_USB3_POWERDOWN] = STIH416_PDN_REAR(6, 5),
+ [STIH416_SATA0_POWERDOWN] = STIH416_PDN_REAR(3, 3),
+ [STIH416_SATA1_POWERDOWN] = STIH416_PDN_REAR(4, 4),
+ [STIH416_PCIE0_POWERDOWN] = STIH416_PDN_REAR(7, 9),
+ [STIH416_PCIE1_POWERDOWN] = STIH416_PDN_REAR(5, 8),
+};
+
+static const struct syscfg_reset_channel_data stih416_softresets[] = {
+ [STIH416_ETH0_SOFTRESET] = STIH416_SRST_FRONT(SYSCFG_1539, 0),
+ [STIH416_ETH1_SOFTRESET] = STIH416_SRST_SBC(SYSCFG_510, 0),
+ [STIH416_IRB_SOFTRESET] = STIH416_SRST_LPM(LPM_SYSCFG_1, 6),
+ [STIH416_USB0_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 9),
+ [STIH416_USB1_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 10),
+ [STIH416_USB2_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 11),
+ [STIH416_USB3_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 28),
+ [STIH416_SATA0_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 7),
+ [STIH416_SATA1_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 3),
+ [STIH416_PCIE0_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 15),
+ [STIH416_PCIE1_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 2),
+ [STIH416_AUD_DAC_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 14),
+ [STIH416_HDTVOUT_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 5),
+ [STIH416_VTAC_M_RX_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 25),
+ [STIH416_VTAC_A_RX_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2552, 26),
+ [STIH416_SYNC_HD_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 5),
+ [STIH416_SYNC_SD_SOFTRESET] = STIH416_SRST_REAR(SYSCFG_2553, 6),
+ [STIH416_BLITTER_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 10),
+ [STIH416_GPU_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 11),
+ [STIH416_VTAC_M_TX_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 18),
+ [STIH416_VTAC_A_TX_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 19),
+ [STIH416_VTG_AUX_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 21),
+ [STIH416_JPEG_DEC_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7563, 23),
+ [STIH416_HVA_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 2),
+ [STIH416_COMPO_M_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 3),
+ [STIH416_COMPO_A_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 4),
+ [STIH416_VP8_DEC_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 10),
+ [STIH416_VTG_MAIN_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 16),
+};
+
+static struct syscfg_reset_controller_data stih416_powerdown_controller = {
+ .wait_for_ack = true,
+ .nr_channels = ARRAY_SIZE(stih416_powerdowns),
+ .channels = stih416_powerdowns,
+};
+
+static struct syscfg_reset_controller_data stih416_softreset_controller = {
+ .wait_for_ack = false,
+ .active_low = true,
+ .nr_channels = ARRAY_SIZE(stih416_softresets),
+ .channels = stih416_softresets,
+};
+
+static struct of_device_id stih416_reset_match[] = {
+ { .compatible = "st,stih416-powerdown",
+ .data = &stih416_powerdown_controller, },
+ { .compatible = "st,stih416-softreset",
+ .data = &stih416_softreset_controller, },
+ {},
+};
+
+static struct platform_driver stih416_reset_driver = {
+ .probe = syscfg_reset_probe,
+ .driver = {
+ .name = "reset-stih416",
+ .owner = THIS_MODULE,
+ .of_match_table = stih416_reset_match,
+ },
+};
+
+static int __init stih416_reset_init(void)
+{
+ return platform_driver_register(&stih416_reset_driver);
+}
+arch_initcall(stih416_reset_init);
diff --git a/drivers/reset/sti/reset-syscfg.c b/drivers/reset/sti/reset-syscfg.c
new file mode 100644
index 000000000000..a145cc066d4a
--- /dev/null
+++ b/drivers/reset/sti/reset-syscfg.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ *
+ * Inspired by mach-imx/src.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "reset-syscfg.h"
+
+/**
+ * Reset channel regmap configuration
+ *
+ * @reset: regmap field for the channel's reset bit.
+ * @ack: regmap field for the channel's ack bit (optional).
+ */
+struct syscfg_reset_channel {
+ struct regmap_field *reset;
+ struct regmap_field *ack;
+};
+
+/**
+ * A reset controller which groups together a set of related reset bits, which
+ * may be located in different system configuration registers.
+ *
+ * @rst: base reset controller structure.
+ * @active_low: are the resets in this controller active low, i.e. clearing
+ * the reset bit puts the hardware into reset.
+ * @channels: An array of reset channels for this controller.
+ */
+struct syscfg_reset_controller {
+ struct reset_controller_dev rst;
+ bool active_low;
+ struct syscfg_reset_channel *channels;
+};
+
+#define to_syscfg_reset_controller(_rst) \
+ container_of(_rst, struct syscfg_reset_controller, rst)
+
+static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev,
+ unsigned long idx, int assert)
+{
+ struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
+ const struct syscfg_reset_channel *ch;
+ u32 ctrl_val = rst->active_low ? !assert : !!assert;
+ int err;
+
+ if (idx >= rcdev->nr_resets)
+ return -EINVAL;
+
+ ch = &rst->channels[idx];
+
+ err = regmap_field_write(ch->reset, ctrl_val);
+ if (err)
+ return err;
+
+ if (ch->ack) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ u32 ack_val;
+
+ while (true) {
+ err = regmap_field_read(ch->ack, &ack_val);
+ if (err)
+ return err;
+
+ if (ack_val == ctrl_val)
+ break;
+
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+
+ cpu_relax();
+ }
+ }
+
+ return 0;
+}
+
+static int syscfg_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return syscfg_reset_program_hw(rcdev, idx, true);
+}
+
+static int syscfg_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return syscfg_reset_program_hw(rcdev, idx, false);
+}
+
+static int syscfg_reset_dev(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ int err = syscfg_reset_assert(rcdev, idx);
+ if (err)
+ return err;
+
+ return syscfg_reset_deassert(rcdev, idx);
+}
+
+static struct reset_control_ops syscfg_reset_ops = {
+ .reset = syscfg_reset_dev,
+ .assert = syscfg_reset_assert,
+ .deassert = syscfg_reset_deassert,
+};
+
+static int syscfg_reset_controller_register(struct device *dev,
+ const struct syscfg_reset_controller_data *data)
+{
+ struct syscfg_reset_controller *rc;
+ size_t size;
+ int i, err;
+
+ rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ size = sizeof(struct syscfg_reset_channel) * data->nr_channels;
+
+ rc->channels = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!rc->channels)
+ return -ENOMEM;
+
+ rc->rst.ops = &syscfg_reset_ops,
+ rc->rst.of_node = dev->of_node;
+ rc->rst.nr_resets = data->nr_channels;
+ rc->active_low = data->active_low;
+
+ for (i = 0; i < data->nr_channels; i++) {
+ struct regmap *map;
+ struct regmap_field *f;
+ const char *compatible = data->channels[i].compatible;
+
+ map = syscon_regmap_lookup_by_compatible(compatible);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ f = devm_regmap_field_alloc(dev, map, data->channels[i].reset);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ rc->channels[i].reset = f;
+
+ if (!data->wait_for_ack)
+ continue;
+
+ f = devm_regmap_field_alloc(dev, map, data->channels[i].ack);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ rc->channels[i].ack = f;
+ }
+
+ err = reset_controller_register(&rc->rst);
+ if (!err)
+ dev_info(dev, "registered\n");
+
+ return err;
+}
+
+int syscfg_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = pdev ? &pdev->dev : NULL;
+ const struct of_device_id *match;
+
+ if (!dev || !dev->driver)
+ return -ENODEV;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data)
+ return -EINVAL;
+
+ return syscfg_reset_controller_register(dev, match->data);
+}
diff --git a/drivers/reset/sti/reset-syscfg.h b/drivers/reset/sti/reset-syscfg.h
new file mode 100644
index 000000000000..2cc2283bac40
--- /dev/null
+++ b/drivers/reset/sti/reset-syscfg.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __STI_RESET_SYSCFG_H
+#define __STI_RESET_SYSCFG_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+/**
+ * Reset channel description for a system configuration register based
+ * reset controller.
+ *
+ * @compatible: Compatible string of the syscon regmap containing this
+ * channel's control and ack (status) bits.
+ * @reset: Regmap field description of the channel's reset bit.
+ * @ack: Regmap field description of the channel's acknowledge bit.
+ */
+struct syscfg_reset_channel_data {
+ const char *compatible;
+ struct reg_field reset;
+ struct reg_field ack;
+};
+
+#define _SYSCFG_RST_CH(_c, _rr, _rb, _ar, _ab) \
+ { .compatible = _c, \
+ .reset = REG_FIELD(_rr, _rb, _rb), \
+ .ack = REG_FIELD(_ar, _ab, _ab), }
+
+#define _SYSCFG_RST_CH_NO_ACK(_c, _rr, _rb) \
+ { .compatible = _c, \
+ .reset = REG_FIELD(_rr, _rb, _rb), }
+
+/**
+ * Description of a system configuration register based reset controller.
+ *
+ * @wait_for_ack: The controller will wait for reset assert and de-assert to
+ * be "ack'd" in a channel's ack field.
+ * @active_low: Are the resets in this controller active low, i.e. clearing
+ * the reset bit puts the hardware into reset.
+ * @nr_channels: The number of reset channels in this controller.
+ * @channels: An array of reset channel descriptions.
+ */
+struct syscfg_reset_controller_data {
+ bool wait_for_ack;
+ bool active_low;
+ int nr_channels;
+ const struct syscfg_reset_channel_data *channels;
+};
+
+/**
+ * syscfg_reset_probe(): platform device probe function used by syscfg
+ * reset controller drivers. This registers a reset
+ * controller configured by the OF match data for
+ * the compatible device which should be of type
+ * "struct syscfg_reset_controller_data".
+ *
+ * @pdev: platform device
+ */
+int syscfg_reset_probe(struct platform_device *pdev);
+
+#endif /* __STI_RESET_SYSCFG_H */
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 309b8b342d9c..596374304532 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -24,7 +24,7 @@
#include <mach/at91_rtt.h>
#include <mach/cpu.h>
-
+#include <mach/hardware.h>
/*
* This driver uses two configurable hardware resources that live in the
diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c
index 7e5ead936a04..41bd76aaff76 100644
--- a/drivers/rtc/rtc-isl12057.c
+++ b/drivers/rtc/rtc-isl12057.c
@@ -274,10 +274,7 @@ static int isl12057_probe(struct i2c_client *client,
dev_set_drvdata(dev, data);
rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
-
- return 0;
+ return PTR_ERR_OR_ZERO(rtc);
}
#ifdef CONFIG_OF
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index d536c5962c99..d15a999363fc 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -222,6 +222,7 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
struct resource *res;
struct rtc_plat_data *pdata;
u32 rtc_time;
+ u32 rtc_date;
int ret = 0;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
@@ -257,6 +258,17 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
}
}
+ /*
+ * A date after January 19th, 2038 does not fit on 32 bits and
+ * will confuse the kernel and userspace. Reset to a sane date
+ * (January 1st, 2013) if we're after 2038.
+ */
+ rtc_date = readl(pdata->ioaddr + RTC_DATE_REG_OFFS);
+ if (bcd2bin((rtc_date >> RTC_YEAR_OFFS) & 0xff) >= 38) {
+ dev_info(&pdev->dev, "invalid RTC date, resetting to January 1st, 2013\n");
+ writel(0x130101, pdata->ioaddr + RTC_DATE_REG_OFFS);
+ }
+
pdata->irq = platform_get_irq(pdev, 0);
platform_set_drvdata(pdev, pdata);
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index a355f2b82bb8..cccbf9d89729 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -32,7 +32,6 @@
#include <mach/hardware.h>
-#define TIMER_FREQ CLOCK_TICK_RATE
#define RTC_DEF_DIVIDER (32768 - 1)
#define RTC_DEF_TRIM 0
#define MAXFREQ_PERIODIC 1000
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 9cbc567698ce..c062f1620c58 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -646,7 +646,7 @@ dasd_diag_init(void)
ASCEBC(dasd_diag_discipline.ebcname, 4);
irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
- register_external_interrupt(0x2603, dasd_ext_handler);
+ register_external_irq(EXT_IRQ_CP_SERVICE, dasd_ext_handler);
dasd_diag_discipline_pointer = &dasd_diag_discipline;
return 0;
}
@@ -654,7 +654,7 @@ dasd_diag_init(void)
static void __exit
dasd_diag_cleanup(void)
{
- unregister_external_interrupt(0x2603, dasd_ext_handler);
+ unregister_external_irq(EXT_IRQ_CP_SERVICE, dasd_ext_handler);
irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
dasd_diag_discipline_pointer = NULL;
}
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 9f849df4381e..15b3459f8656 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -632,6 +632,8 @@ raw3270_reset_device_cb(struct raw3270_request *rq, void *data)
raw3270_size_device_done(rp);
} else
raw3270_writesf_readpart(rp);
+ memset(&rp->init_reset, 0, sizeof(rp->init_reset));
+ memset(&rp->init_data, 0, sizeof(rp->init_data));
}
static int
@@ -639,9 +641,10 @@ __raw3270_reset_device(struct raw3270 *rp)
{
int rc;
+ /* Check if reset is already pending */
+ if (rp->init_reset.view)
+ return -EBUSY;
/* Store reset data stream to init_data/init_reset */
- memset(&rp->init_reset, 0, sizeof(rp->init_reset));
- memset(&rp->init_data, 0, sizeof(rp->init_data));
rp->init_data[0] = TW_KR;
rp->init_reset.ccw.cmd_code = TC_EWRITEA;
rp->init_reset.ccw.flags = CCW_FLAG_SLI;
@@ -850,7 +853,7 @@ raw3270_create_device(struct ccw_device *cdev)
char *ascebc;
int rc;
- rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA);
+ rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA);
if (!rp)
return ERR_PTR(-ENOMEM);
ascebc = kmalloc(256, GFP_KERNEL);
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 1fe264379e0d..1990285296c6 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -91,6 +91,9 @@ static struct sclp_req sclp_suspend_req;
/* Timer for request retries. */
static struct timer_list sclp_request_timer;
+/* Timer for queued requests. */
+static struct timer_list sclp_queue_timer;
+
/* Internal state: is the driver initialized? */
static volatile enum sclp_init_state_t {
sclp_init_state_uninitialized,
@@ -215,6 +218,76 @@ sclp_request_timeout(unsigned long data)
sclp_process_queue();
}
+/*
+ * Returns the expire value in jiffies of the next pending request timeout,
+ * if any. Needs to be called with sclp_lock.
+ */
+static unsigned long __sclp_req_queue_find_next_timeout(void)
+{
+ unsigned long expires_next = 0;
+ struct sclp_req *req;
+
+ list_for_each_entry(req, &sclp_req_queue, list) {
+ if (!req->queue_expires)
+ continue;
+ if (!expires_next ||
+ (time_before(req->queue_expires, expires_next)))
+ expires_next = req->queue_expires;
+ }
+ return expires_next;
+}
+
+/*
+ * Returns expired request, if any, and removes it from the list.
+ */
+static struct sclp_req *__sclp_req_queue_remove_expired_req(void)
+{
+ unsigned long flags, now;
+ struct sclp_req *req;
+
+ spin_lock_irqsave(&sclp_lock, flags);
+ now = jiffies;
+ /* Don't need list_for_each_safe because we break out after list_del */
+ list_for_each_entry(req, &sclp_req_queue, list) {
+ if (!req->queue_expires)
+ continue;
+ if (time_before_eq(req->queue_expires, now)) {
+ if (req->status == SCLP_REQ_QUEUED) {
+ req->status = SCLP_REQ_QUEUED_TIMEOUT;
+ list_del(&req->list);
+ goto out;
+ }
+ }
+ }
+ req = NULL;
+out:
+ spin_unlock_irqrestore(&sclp_lock, flags);
+ return req;
+}
+
+/*
+ * Timeout handler for queued requests. Removes request from list and
+ * invokes callback. This timer can be set per request in situations where
+ * waiting too long would be harmful to the system, e.g. during SE reboot.
+ */
+static void sclp_req_queue_timeout(unsigned long data)
+{
+ unsigned long flags, expires_next;
+ struct sclp_req *req;
+
+ do {
+ req = __sclp_req_queue_remove_expired_req();
+ if (req && req->callback)
+ req->callback(req, req->callback_data);
+ } while (req);
+
+ spin_lock_irqsave(&sclp_lock, flags);
+ expires_next = __sclp_req_queue_find_next_timeout();
+ if (expires_next)
+ mod_timer(&sclp_queue_timer, expires_next);
+ spin_unlock_irqrestore(&sclp_lock, flags);
+}
+
/* Try to start a request. Return zero if the request was successfully
* started or if it will be started at a later time. Return non-zero otherwise.
* Called while sclp_lock is locked. */
@@ -317,6 +390,13 @@ sclp_add_request(struct sclp_req *req)
req->start_count = 0;
list_add_tail(&req->list, &sclp_req_queue);
rc = 0;
+ if (req->queue_timeout) {
+ req->queue_expires = jiffies + req->queue_timeout * HZ;
+ if (!timer_pending(&sclp_queue_timer) ||
+ time_after(sclp_queue_timer.expires, req->queue_expires))
+ mod_timer(&sclp_queue_timer, req->queue_expires);
+ } else
+ req->queue_expires = 0;
/* Start if request is first in list */
if (sclp_running_state == sclp_running_state_idle &&
req->list.prev == &sclp_req_queue) {
@@ -892,7 +972,7 @@ sclp_check_interface(void)
spin_lock_irqsave(&sclp_lock, flags);
/* Prepare init mask command */
- rc = register_external_interrupt(0x2401, sclp_check_handler);
+ rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);
if (rc) {
spin_unlock_irqrestore(&sclp_lock, flags);
return rc;
@@ -925,7 +1005,7 @@ sclp_check_interface(void)
} else
rc = -EBUSY;
}
- unregister_external_interrupt(0x2401, sclp_check_handler);
+ unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);
spin_unlock_irqrestore(&sclp_lock, flags);
return rc;
}
@@ -1113,6 +1193,8 @@ sclp_init(void)
INIT_LIST_HEAD(&sclp_reg_list);
list_add(&sclp_state_change_event.list, &sclp_reg_list);
init_timer(&sclp_request_timer);
+ init_timer(&sclp_queue_timer);
+ sclp_queue_timer.function = sclp_req_queue_timeout;
/* Check interface */
spin_unlock_irqrestore(&sclp_lock, flags);
rc = sclp_check_interface();
@@ -1124,7 +1206,7 @@ sclp_init(void)
if (rc)
goto fail_init_state_uninitialized;
/* Register interrupt handler */
- rc = register_external_interrupt(0x2401, sclp_interrupt_handler);
+ rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_interrupt_handler);
if (rc)
goto fail_unregister_reboot_notifier;
sclp_init_state = sclp_init_state_initialized;
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index fea76aed9eea..a68b5ec7d042 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -133,6 +133,11 @@ struct sclp_req {
/* Callback that is called after reaching final status. */
void (*callback)(struct sclp_req *, void *data);
void *callback_data;
+ int queue_timeout; /* request queue timeout (sec), set by
+ caller of sclp_add_request(), if
+ needed */
+ /* Internal fields */
+ unsigned long queue_expires; /* request queue timeout (jiffies) */
};
#define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */
@@ -140,6 +145,9 @@ struct sclp_req {
#define SCLP_REQ_RUNNING 0x02 /* request is currently running */
#define SCLP_REQ_DONE 0x03 /* request is completed successfully */
#define SCLP_REQ_FAILED 0x05 /* request is finally failed */
+#define SCLP_REQ_QUEUED_TIMEOUT 0x06 /* request on queue timed out */
+
+#define SCLP_QUEUE_INTERVAL 5 /* timeout interval for request queue */
/* function pointers that a high level driver has to use for registration */
/* of some routines it wants to be called from the low level driver */
@@ -173,6 +181,7 @@ int sclp_deactivate(void);
int sclp_reactivate(void);
int sclp_service_call(sclp_cmdw_t command, void *sccb);
int sclp_sync_request(sclp_cmdw_t command, void *sccb);
+int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout);
int sclp_sdias_init(void);
void sclp_sdias_exit(void);
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 49af8eeb90ea..6e8f90f84e49 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -37,6 +37,11 @@ static void sclp_sync_callback(struct sclp_req *req, void *data)
int sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
{
+ return sclp_sync_request_timeout(cmd, sccb, 0);
+}
+
+int sclp_sync_request_timeout(sclp_cmdw_t cmd, void *sccb, int timeout)
+{
struct completion completion;
struct sclp_req *request;
int rc;
@@ -44,6 +49,8 @@ int sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
request = kzalloc(sizeof(*request), GFP_KERNEL);
if (!request)
return -ENOMEM;
+ if (timeout)
+ request->queue_timeout = timeout;
request->command = cmd;
request->sccb = sccb;
request->status = SCLP_REQ_FILLED;
@@ -110,7 +117,8 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
if (!sccb)
return -ENOMEM;
sccb->header.length = sizeof(*sccb);
- rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
+ rc = sclp_sync_request_timeout(SCLP_CMDW_READ_CPU_INFO, sccb,
+ SCLP_QUEUE_INTERVAL);
if (rc)
goto out;
if (sccb->header.response_code != 0x0010) {
@@ -144,7 +152,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd)
if (!sccb)
return -ENOMEM;
sccb->header.length = sizeof(*sccb);
- rc = sclp_sync_request(cmd, sccb);
+ rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
if (rc)
goto out;
switch (sccb->header.response_code) {
@@ -214,7 +222,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
return -ENOMEM;
sccb->header.length = PAGE_SIZE;
sccb->rn = rn;
- rc = sclp_sync_request(cmd, sccb);
+ rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
if (rc)
goto out;
switch (sccb->header.response_code) {
@@ -269,7 +277,8 @@ static int sclp_attach_storage(u8 id)
if (!sccb)
return -ENOMEM;
sccb->header.length = PAGE_SIZE;
- rc = sclp_sync_request(0x00080001 | id << 8, sccb);
+ rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb,
+ SCLP_QUEUE_INTERVAL);
if (rc)
goto out;
switch (sccb->header.response_code) {
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 981a99fd8d42..3478e19ae194 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -78,7 +78,8 @@ tape_std_assign(struct tape_device *device)
rc = tape_do_io_interruptible(device, request);
- del_timer(&timeout);
+ del_timer_sync(&timeout);
+ destroy_timer_on_stack(&timeout);
if (rc != 0) {
DBF_EVENT(3, "%08x: assign failed - device might be busy\n",
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 4b824b15194f..5222ebe15705 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -626,8 +626,8 @@ static long zcrypt_send_ep11_cprb(struct ep11_urb *xcrb)
return -ENOMEM;
if (copy_from_user(ep11_dev_list.targets,
- (struct ep11_target_dev *)xcrb->targets,
- xcrb->targets_num *
+ (struct ep11_target_dev __force __user *)
+ xcrb->targets, xcrb->targets_num *
sizeof(struct ep11_target_dev)))
return -EFAULT;
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 0bc91e46395a..46b324ce6c7a 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -315,6 +315,10 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
char *function_code;
+ if (CEIL4(xcRB->request_control_blk_length) <
+ xcRB->request_control_blk_length)
+ return -EINVAL; /* overflow after alignment*/
+
/* length checks */
ap_msg->length = sizeof(struct type6_hdr) +
CEIL4(xcRB->request_control_blk_length) +
@@ -333,6 +337,10 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
return -EINVAL;
}
+ if (CEIL4(xcRB->reply_control_blk_length) <
+ xcRB->reply_control_blk_length)
+ return -EINVAL; /* overflow after alignment*/
+
replylen = sizeof(struct type86_fmt2_msg) +
CEIL4(xcRB->reply_control_blk_length) +
xcRB->reply_data_length;
@@ -415,12 +423,18 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(struct zcrypt_device *zdev,
unsigned int dom_val; /* domain id */
} __packed * payload_hdr;
+ if (CEIL4(xcRB->req_len) < xcRB->req_len)
+ return -EINVAL; /* overflow after alignment*/
+
/* length checks */
ap_msg->length = sizeof(struct type6_hdr) + xcRB->req_len;
if (CEIL4(xcRB->req_len) > MSGTYPE06_MAX_MSG_SIZE -
(sizeof(struct type6_hdr)))
return -EINVAL;
+ if (CEIL4(xcRB->resp_len) < xcRB->resp_len)
+ return -EINVAL; /* overflow after alignment*/
+
if (CEIL4(xcRB->resp_len) > MSGTYPE06_MAX_MSG_SIZE -
(sizeof(struct type86_fmt2_msg)))
return -EINVAL;
@@ -432,7 +446,7 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(struct zcrypt_device *zdev,
/* Import CPRB data from the ioctl input parameter */
if (copy_from_user(&(msg->cprbx.cprb_len),
- (char *)xcRB->req, xcRB->req_len)) {
+ (char __force __user *)xcRB->req, xcRB->req_len)) {
return -EFAULT;
}
@@ -645,7 +659,7 @@ static int convert_type86_ep11_xcrb(struct zcrypt_device *zdev,
return -EINVAL;
/* Copy response CPRB to user */
- if (copy_to_user((char *)xcRB->resp,
+ if (copy_to_user((char __force __user *)xcRB->resp,
data + msg->fmt2.offset1, msg->fmt2.count1))
return -EFAULT;
xcRB->resp_len = msg->fmt2.count1;
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 1abd0db29915..a1349653c6d9 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -477,7 +477,7 @@ static int __init kvm_devices_init(void)
INIT_WORK(&hotplug_work, hotplug_devices);
irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
- register_external_interrupt(0x2603, kvm_extint_handler);
+ register_external_irq(EXT_IRQ_CP_SERVICE, kvm_extint_handler);
scan_devices();
return 0;
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index f404f55b3191..c461f2aac610 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -899,6 +899,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
add_timer(&timer);
wait_event(reply->wait_q, reply->received);
del_timer_sync(&timer);
+ destroy_timer_on_stack(&timer);
LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc);
rc = reply->rc;
lcs_put_reply(reply);
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index 6287f6a8b79d..1d41f4b9114f 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -2592,12 +2592,16 @@ static int __init bnx2fc_mod_init(void)
spin_lock_init(&p->fp_work_lock);
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu) {
bnx2fc_percpu_thread_create(cpu);
}
/* Initialize per CPU interrupt thread */
- register_hotcpu_notifier(&bnx2fc_cpu_notifier);
+ __register_hotcpu_notifier(&bnx2fc_cpu_notifier);
+
+ cpu_notifier_register_done();
cnic_register_driver(CNIC_ULP_FCOE, &bnx2fc_cnic_cb);
@@ -2662,13 +2666,17 @@ static void __exit bnx2fc_mod_exit(void)
if (l2_thread)
kthread_stop(l2_thread);
- unregister_hotcpu_notifier(&bnx2fc_cpu_notifier);
+ cpu_notifier_register_begin();
/* Destroy per cpu threads */
for_each_online_cpu(cpu) {
bnx2fc_percpu_thread_destroy(cpu);
}
+ __unregister_hotcpu_notifier(&bnx2fc_cpu_notifier);
+
+ cpu_notifier_register_done();
+
destroy_workqueue(bnx2fc_wq);
/*
* detach from scsi transport
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index 34c294b42c84..80c03b452d61 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -537,11 +537,15 @@ static int __init bnx2i_mod_init(void)
p->iothread = NULL;
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu)
bnx2i_percpu_thread_create(cpu);
/* Initialize per CPU interrupt thread */
- register_hotcpu_notifier(&bnx2i_cpu_notifier);
+ __register_hotcpu_notifier(&bnx2i_cpu_notifier);
+
+ cpu_notifier_register_done();
return 0;
@@ -581,11 +585,15 @@ static void __exit bnx2i_mod_exit(void)
}
mutex_unlock(&bnx2i_dev_lock);
- unregister_hotcpu_notifier(&bnx2i_cpu_notifier);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
bnx2i_percpu_thread_destroy(cpu);
+ __unregister_hotcpu_notifier(&bnx2i_cpu_notifier);
+
+ cpu_notifier_register_done();
+
iscsi_unregister_transport(&bnx2i_iscsi_transport);
cnic_unregister_driver(CNIC_ULP_ISCSI);
}
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index f3170008ae71..d5e105b173f0 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -2633,14 +2633,18 @@ static int __init fcoe_init(void)
skb_queue_head_init(&p->fcoe_rx_list);
}
+ cpu_notifier_register_begin();
+
for_each_online_cpu(cpu)
fcoe_percpu_thread_create(cpu);
/* Initialize per CPU interrupt thread */
- rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+ rc = __register_hotcpu_notifier(&fcoe_cpu_notifier);
if (rc)
goto out_free;
+ cpu_notifier_register_done();
+
/* Setup link change notification */
fcoe_dev_setup();
@@ -2655,6 +2659,9 @@ out_free:
for_each_online_cpu(cpu) {
fcoe_percpu_thread_destroy(cpu);
}
+
+ cpu_notifier_register_done();
+
mutex_unlock(&fcoe_config_mutex);
destroy_workqueue(fcoe_wq);
return rc;
@@ -2687,11 +2694,15 @@ static void __exit fcoe_exit(void)
}
rtnl_unlock();
- unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
fcoe_percpu_thread_destroy(cpu);
+ __unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+ cpu_notifier_register_done();
+
mutex_unlock(&fcoe_config_mutex);
/*
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index 1ebe67cd1833..7442bc130055 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -36,9 +36,47 @@ static void sh_clk_write(int value, struct clk *clk)
iowrite32(value, clk->mapped_reg);
}
+static unsigned int r8(const void __iomem *addr)
+{
+ return ioread8(addr);
+}
+
+static unsigned int r16(const void __iomem *addr)
+{
+ return ioread16(addr);
+}
+
+static unsigned int r32(const void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
static int sh_clk_mstp_enable(struct clk *clk)
{
sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk);
+ if (clk->status_reg) {
+ unsigned int (*read)(const void __iomem *addr);
+ int i;
+ void __iomem *mapped_status = (phys_addr_t)clk->status_reg -
+ (phys_addr_t)clk->enable_reg + clk->mapped_reg;
+
+ if (clk->flags & CLK_ENABLE_REG_8BIT)
+ read = r8;
+ else if (clk->flags & CLK_ENABLE_REG_16BIT)
+ read = r16;
+ else
+ read = r32;
+
+ for (i = 1000;
+ (read(mapped_status) & (1 << clk->enable_bit)) && i;
+ i--)
+ cpu_relax();
+ if (!i) {
+ pr_err("cpg: failed to enable %p[%d]\n",
+ clk->enable_reg, clk->enable_bit);
+ return -ETIMEDOUT;
+ }
+ }
return 0;
}
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig
index a305731742a9..f7d90617c9d9 100644
--- a/drivers/sh/intc/Kconfig
+++ b/drivers/sh/intc/Kconfig
@@ -6,7 +6,7 @@ comment "Interrupt controller options"
config INTC_USERIMASK
bool "Userspace interrupt masking support"
- depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
+ depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) || COMPILE_TEST
help
This enables support for hardware-assisted userspace hardirq
masking.
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c
index a8d017854615..c48f640db006 100644
--- a/drivers/staging/imx-drm/ipuv3-crtc.c
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -121,7 +121,7 @@ static int ipu_page_flip(struct drm_crtc *crtc,
ipu_crtc->newfb = fb;
ipu_crtc->page_flip_event = event;
- crtc->fb = fb;
+ crtc->primary->fb = fb;
return 0;
}
@@ -193,7 +193,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
return ret;
}
- return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->fb,
+ return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
x, y, mode->hdisplay, mode->vdisplay);
}
@@ -219,7 +219,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
if (ipu_crtc->newfb) {
ipu_crtc->newfb = NULL;
- ipu_plane_set_base(ipu_crtc->plane[0], ipu_crtc->base.fb,
+ ipu_plane_set_base(ipu_crtc->plane[0], ipu_crtc->base.primary->fb,
ipu_crtc->plane[0]->x, ipu_crtc->plane[0]->y);
ipu_crtc_handle_pageflip(ipu_crtc);
}
diff --git a/drivers/staging/imx-drm/ipuv3-plane.c b/drivers/staging/imx-drm/ipuv3-plane.c
index b0c9b6ce4854..27a8d735dae0 100644
--- a/drivers/staging/imx-drm/ipuv3-plane.c
+++ b/drivers/staging/imx-drm/ipuv3-plane.c
@@ -68,7 +68,7 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
if (!cma_obj) {
- DRM_LOG_KMS("entry is null.\n");
+ DRM_DEBUG_KMS("entry is null.\n");
return -EFAULT;
}
diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c
index e2f597ee6261..1ca91f7092b1 100644
--- a/drivers/staging/speakup/kobjects.c
+++ b/drivers/staging/speakup/kobjects.c
@@ -851,75 +851,75 @@ static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
* Declare the attributes.
*/
static struct kobj_attribute keymap_attribute =
- __ATTR(keymap, ROOT_W, keymap_show, keymap_store);
+ __ATTR(keymap, S_IWUSR|S_IRUGO, keymap_show, keymap_store);
static struct kobj_attribute silent_attribute =
- __ATTR(silent, USER_W, NULL, silent_store);
+ __ATTR(silent, S_IWUGO, NULL, silent_store);
static struct kobj_attribute synth_attribute =
- __ATTR(synth, USER_RW, synth_show, synth_store);
+ __ATTR(synth, S_IWUGO|S_IRUGO, synth_show, synth_store);
static struct kobj_attribute synth_direct_attribute =
- __ATTR(synth_direct, USER_W, NULL, synth_direct_store);
+ __ATTR(synth_direct, S_IWUGO, NULL, synth_direct_store);
static struct kobj_attribute version_attribute =
__ATTR_RO(version);
static struct kobj_attribute delimiters_attribute =
- __ATTR(delimiters, USER_RW, punc_show, punc_store);
+ __ATTR(delimiters, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute ex_num_attribute =
- __ATTR(ex_num, USER_RW, punc_show, punc_store);
+ __ATTR(ex_num, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute punc_all_attribute =
- __ATTR(punc_all, USER_RW, punc_show, punc_store);
+ __ATTR(punc_all, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute punc_most_attribute =
- __ATTR(punc_most, USER_RW, punc_show, punc_store);
+ __ATTR(punc_most, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute punc_some_attribute =
- __ATTR(punc_some, USER_RW, punc_show, punc_store);
+ __ATTR(punc_some, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute repeats_attribute =
- __ATTR(repeats, USER_RW, punc_show, punc_store);
+ __ATTR(repeats, S_IWUGO|S_IRUGO, punc_show, punc_store);
static struct kobj_attribute attrib_bleep_attribute =
- __ATTR(attrib_bleep, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(attrib_bleep, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute bell_pos_attribute =
- __ATTR(bell_pos, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(bell_pos, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute bleep_time_attribute =
- __ATTR(bleep_time, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(bleep_time, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute bleeps_attribute =
- __ATTR(bleeps, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(bleeps, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute cursor_time_attribute =
- __ATTR(cursor_time, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(cursor_time, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute key_echo_attribute =
- __ATTR(key_echo, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(key_echo, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute no_interrupt_attribute =
- __ATTR(no_interrupt, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(no_interrupt, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punc_level_attribute =
- __ATTR(punc_level, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punc_level, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute reading_punc_attribute =
- __ATTR(reading_punc, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(reading_punc, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute say_control_attribute =
- __ATTR(say_control, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(say_control, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute say_word_ctl_attribute =
- __ATTR(say_word_ctl, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(say_word_ctl, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute spell_delay_attribute =
- __ATTR(spell_delay, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(spell_delay, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
/*
* These attributes are i18n related.
*/
static struct kobj_attribute announcements_attribute =
- __ATTR(announcements, USER_RW, message_show, message_store);
+ __ATTR(announcements, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute characters_attribute =
- __ATTR(characters, USER_RW, chars_chartab_show, chars_chartab_store);
+ __ATTR(characters, S_IWUGO|S_IRUGO, chars_chartab_show, chars_chartab_store);
static struct kobj_attribute chartab_attribute =
- __ATTR(chartab, USER_RW, chars_chartab_show, chars_chartab_store);
+ __ATTR(chartab, S_IWUGO|S_IRUGO, chars_chartab_show, chars_chartab_store);
static struct kobj_attribute ctl_keys_attribute =
- __ATTR(ctl_keys, USER_RW, message_show, message_store);
+ __ATTR(ctl_keys, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute colors_attribute =
- __ATTR(colors, USER_RW, message_show, message_store);
+ __ATTR(colors, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute formatted_attribute =
- __ATTR(formatted, USER_RW, message_show, message_store);
+ __ATTR(formatted, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute function_names_attribute =
- __ATTR(function_names, USER_RW, message_show, message_store);
+ __ATTR(function_names, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute key_names_attribute =
- __ATTR(key_names, USER_RW, message_show, message_store);
+ __ATTR(key_names, S_IWUGO|S_IRUGO, message_show, message_store);
static struct kobj_attribute states_attribute =
- __ATTR(states, USER_RW, message_show, message_store);
+ __ATTR(states, S_IWUGO|S_IRUGO, message_show, message_store);
/*
* Create groups of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h
index 0126f714821a..a7bcceec436a 100644
--- a/drivers/staging/speakup/speakup.h
+++ b/drivers/staging/speakup/speakup.h
@@ -12,8 +12,6 @@
/* proc permissions */
#define USER_R (S_IFREG|S_IRUGO)
#define USER_W (S_IFREG|S_IWUGO)
-#define USER_RW (S_IFREG|S_IRUGO|S_IWUGO)
-#define ROOT_W (S_IFREG|S_IRUGO|S_IWUSR)
#define TOGGLE_0 .u.n = {NULL, 0, 0, 1, 0, 0, NULL }
#define TOGGLE_1 .u.n = {NULL, 1, 0, 1, 0, 0, NULL }
diff --git a/drivers/staging/speakup/speakup_acntpc.c b/drivers/staging/speakup/speakup_acntpc.c
index 1c8a7f4a0ef5..e7dfa434bd96 100644
--- a/drivers/staging/speakup/speakup_acntpc.c
+++ b/drivers/staging/speakup/speakup_acntpc.c
@@ -62,28 +62,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/acntpc.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_acntsa.c b/drivers/staging/speakup/speakup_acntsa.c
index 22a8b7291098..c7f014ed9628 100644
--- a/drivers/staging/speakup/speakup_acntsa.c
+++ b/drivers/staging/speakup/speakup_acntsa.c
@@ -47,28 +47,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/acntsa.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IRUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_apollo.c b/drivers/staging/speakup/speakup_apollo.c
index 70cf1591676a..38c8c2221e4e 100644
--- a/drivers/staging/speakup/speakup_apollo.c
+++ b/drivers/staging/speakup/speakup_apollo.c
@@ -53,30 +53,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/apollo.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute lang_attribute =
- __ATTR(lang, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(lang, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_audptr.c b/drivers/staging/speakup/speakup_audptr.c
index 61a3ceeb0d3a..de5b4a5f43b6 100644
--- a/drivers/staging/speakup/speakup_audptr.c
+++ b/drivers/staging/speakup/speakup_audptr.c
@@ -49,30 +49,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/audptr.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_bns.c b/drivers/staging/speakup/speakup_bns.c
index 4bfe3d458dc0..4939e8c7272e 100644
--- a/drivers/staging/speakup/speakup_bns.c
+++ b/drivers/staging/speakup/speakup_bns.c
@@ -44,28 +44,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/bns.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_decext.c b/drivers/staging/speakup/speakup_decext.c
index d306e010d3ea..b17af9803929 100644
--- a/drivers/staging/speakup/speakup_decext.c
+++ b/drivers/staging/speakup/speakup_decext.c
@@ -70,30 +70,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/decext.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_decpc.c b/drivers/staging/speakup/speakup_decpc.c
index ea6b72d40b31..cfa4bc032358 100644
--- a/drivers/staging/speakup/speakup_decpc.c
+++ b/drivers/staging/speakup/speakup_decpc.c
@@ -164,30 +164,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/decpc.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_dectlk.c b/drivers/staging/speakup/speakup_dectlk.c
index 756d01535d3e..1fcae55dabba 100644
--- a/drivers/staging/speakup/speakup_dectlk.c
+++ b/drivers/staging/speakup/speakup_dectlk.c
@@ -70,30 +70,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/dectlk.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c
index 1feb0fba1b43..5c6c34191e8d 100644
--- a/drivers/staging/speakup/speakup_dtlk.c
+++ b/drivers/staging/speakup/speakup_dtlk.c
@@ -67,34 +67,34 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/dtlk.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute freq_attribute =
- __ATTR(freq, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(freq, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_dummy.c b/drivers/staging/speakup/speakup_dummy.c
index 4a24b9c1e8e3..e19e9994bbb5 100644
--- a/drivers/staging/speakup/speakup_dummy.c
+++ b/drivers/staging/speakup/speakup_dummy.c
@@ -46,28 +46,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/dummy.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_keypc.c b/drivers/staging/speakup/speakup_keypc.c
index 2f2fe5eeff63..9c246d701a95 100644
--- a/drivers/staging/speakup/speakup_keypc.c
+++ b/drivers/staging/speakup/speakup_keypc.c
@@ -59,24 +59,24 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/keypc.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_ltlk.c b/drivers/staging/speakup/speakup_ltlk.c
index 326f94d6b079..c9be6f52c254 100644
--- a/drivers/staging/speakup/speakup_ltlk.c
+++ b/drivers/staging/speakup/speakup_ltlk.c
@@ -50,34 +50,34 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/ltlk.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute freq_attribute =
- __ATTR(freq, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(freq, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c
index 243c3d52fe5e..ee6089502a96 100644
--- a/drivers/staging/speakup/speakup_soft.c
+++ b/drivers/staging/speakup/speakup_soft.c
@@ -61,41 +61,41 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/soft.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute freq_attribute =
- __ATTR(freq, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(freq, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute voice_attribute =
- __ATTR(voice, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(voice, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
/*
* We should uncomment the following definition, when we agree on a
* method of passing a language designation to the software synthesizer.
* static struct kobj_attribute lang_attribute =
- * __ATTR(lang, USER_RW, spk_var_show, spk_var_store);
+ * __ATTR(lang, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
*/
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_spkout.c b/drivers/staging/speakup/speakup_spkout.c
index e74f85620c68..711cf114df83 100644
--- a/drivers/staging/speakup/speakup_spkout.c
+++ b/drivers/staging/speakup/speakup_spkout.c
@@ -48,30 +48,30 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/spkout.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute punct_attribute =
- __ATTR(punct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(punct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/staging/speakup/speakup_txprt.c b/drivers/staging/speakup/speakup_txprt.c
index 5a29b9fcc930..3f0be04df071 100644
--- a/drivers/staging/speakup/speakup_txprt.c
+++ b/drivers/staging/speakup/speakup_txprt.c
@@ -44,28 +44,28 @@ static struct var_t vars[] = {
* These attributes will appear in /sys/accessibility/speakup/txprt.
*/
static struct kobj_attribute caps_start_attribute =
- __ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_start, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute caps_stop_attribute =
- __ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(caps_stop, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute pitch_attribute =
- __ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(pitch, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute rate_attribute =
- __ATTR(rate, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(rate, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute tone_attribute =
- __ATTR(tone, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(tone, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute vol_attribute =
- __ATTR(vol, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(vol, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute delay_time_attribute =
- __ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute direct_attribute =
- __ATTR(direct, USER_RW, spk_var_show, spk_var_store);
+ __ATTR(direct, S_IWUGO|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute full_time_attribute =
- __ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute jiffy_delta_attribute =
- __ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
static struct kobj_attribute trigger_time_attribute =
- __ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
/*
* Create a group of attributes so that we can create and destroy them all
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 5f88d767671e..2d51912a6e40 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -143,7 +143,7 @@ config RCAR_THERMAL
config KIRKWOOD_THERMAL
tristate "Temperature sensor on Marvell Kirkwood SoCs"
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || MACH_KIRKWOOD
depends on OF
help
Support for the Kirkwood thermal sensor driver into the Linux thermal
diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c
index 081fd7e6a9f0..9ea3d9d49ffc 100644
--- a/drivers/thermal/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/x86_pkg_temp_thermal.c
@@ -590,12 +590,12 @@ static int __init pkg_temp_thermal_init(void)
platform_thermal_package_rate_control =
pkg_temp_thermal_platform_thermal_rate_control;
- get_online_cpus();
+ cpu_notifier_register_begin();
for_each_online_cpu(i)
if (get_core_online(i))
goto err_ret;
- register_hotcpu_notifier(&pkg_temp_thermal_notifier);
- put_online_cpus();
+ __register_hotcpu_notifier(&pkg_temp_thermal_notifier);
+ cpu_notifier_register_done();
pkg_temp_debugfs_init(); /* Don't care if fails */
@@ -604,7 +604,7 @@ static int __init pkg_temp_thermal_init(void)
err_ret:
for_each_online_cpu(i)
put_core_offline(i);
- put_online_cpus();
+ cpu_notifier_register_done();
kfree(pkg_work_scheduled);
platform_thermal_package_notify = NULL;
platform_thermal_package_rate_control = NULL;
@@ -617,8 +617,8 @@ static void __exit pkg_temp_thermal_exit(void)
struct phy_dev_entry *phdev, *n;
int i;
- get_online_cpus();
- unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
mutex_lock(&phy_dev_list_mutex);
list_for_each_entry_safe(phdev, n, &phy_dev_list, list) {
/* Retore old MSR value for package thermal interrupt */
@@ -636,7 +636,7 @@ static void __exit pkg_temp_thermal_exit(void)
for_each_online_cpu(i)
cancel_delayed_work_sync(
&per_cpu(pkg_temp_thermal_threshold_work, i));
- put_online_cpus();
+ cpu_notifier_register_done();
kfree(pkg_work_scheduled);
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2577d67bacb2..2e6d8ddc4425 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1024,7 +1024,7 @@ config SERIAL_SGI_IOC3
config SERIAL_MSM
bool "MSM on-chip serial port support"
- depends on ARCH_MSM
+ depends on ARCH_MSM || ARCH_QCOM
select SERIAL_CORE
config SERIAL_MSM_CONSOLE
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index b0603e1f7d82..53eeea13ff16 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -35,21 +35,18 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
#include <linux/uaccess.h>
#include <linux/platform_data/atmel.h>
#include <linux/timer.h>
+#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/ioctls.h>
-#ifdef CONFIG_ARM
-#include <mach/cpu.h>
-#include <asm/gpio.h>
-#endif
-
#define PDC_BUFFER_SIZE 512
/* Revisit: We should calculate this based on the actual port settings */
#define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */
@@ -165,6 +162,7 @@ struct atmel_uart_port {
struct circ_buf rx_ring;
struct serial_rs485 rs485; /* rs485 settings */
+ int rts_gpio; /* optional RTS GPIO */
unsigned int tx_done_mask;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
@@ -298,20 +296,16 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
unsigned int mode;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
-#ifdef CONFIG_ARCH_AT91RM9200
- if (cpu_is_at91rm9200()) {
- /*
- * AT91RM9200 Errata #39: RTS0 is not internally connected
- * to PA21. We need to drive the pin manually.
- */
- if (port->mapbase == AT91RM9200_BASE_US0) {
- if (mctrl & TIOCM_RTS)
- at91_set_gpio_value(AT91_PIN_PA21, 0);
- else
- at91_set_gpio_value(AT91_PIN_PA21, 1);
- }
+ /*
+ * AT91RM9200 Errata #39: RTS0 is not internally connected
+ * to PA21. We need to drive the pin as a GPIO.
+ */
+ if (gpio_is_valid(atmel_port->rts_gpio)) {
+ if (mctrl & TIOCM_RTS)
+ gpio_set_value(atmel_port->rts_gpio, 0);
+ else
+ gpio_set_value(atmel_port->rts_gpio, 1);
}
-#endif
if (mctrl & TIOCM_RTS)
control |= ATMEL_US_RTSEN;
@@ -2365,6 +2359,25 @@ static int atmel_serial_probe(struct platform_device *pdev)
port = &atmel_ports[ret];
port->backup_imr = 0;
port->uart.line = ret;
+ port->rts_gpio = -EINVAL; /* Invalid, zero could be valid */
+ if (pdata)
+ port->rts_gpio = pdata->rts_gpio;
+ else if (np)
+ port->rts_gpio = of_get_named_gpio(np, "rts-gpios", 0);
+
+ if (gpio_is_valid(port->rts_gpio)) {
+ ret = devm_gpio_request(&pdev->dev, port->rts_gpio, "RTS");
+ if (ret) {
+ dev_err(&pdev->dev, "error requesting RTS GPIO\n");
+ goto err;
+ }
+ /* Default to 1 as RTS is active low */
+ ret = gpio_direction_output(port->rts_gpio, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "error setting up RTS GPIO\n");
+ goto err;
+ }
+ }
ret = atmel_init_port(port, pdev);
if (ret)
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c
index a139894c600f..e471580a2a3b 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/lpc32xx_udc.c
@@ -55,7 +55,6 @@
#include <mach/hardware.h>
#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <mach/platform.h>
#include <mach/irqs.h>
diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig
index eb6f2b059821..fcf2d48ac6d1 100644
--- a/drivers/video/exynos/Kconfig
+++ b/drivers/video/exynos/Kconfig
@@ -29,11 +29,4 @@ config EXYNOS_LCD_S6E8AX0
If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its
LCD control driver.
-config EXYNOS_DP
- bool "EXYNOS DP driver support"
- depends on ARCH_EXYNOS
- default n
- help
- This enables support for DP device.
-
endif # EXYNOS_VIDEO
diff --git a/drivers/video/exynos/Makefile b/drivers/video/exynos/Makefile
index ec7772e452a9..b5b1bd228abb 100644
--- a/drivers/video/exynos/Makefile
+++ b/drivers/video/exynos/Makefile
@@ -5,4 +5,3 @@
obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
exynos_mipi_dsi_lowlevel.o
obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o
-obj-$(CONFIG_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
diff --git a/drivers/video/omap2/displays-new/connector-analog-tv.c b/drivers/video/omap2/displays-new/connector-analog-tv.c
index 27f33ef8fca1..5ee3b5505f7f 100644
--- a/drivers/video/omap2/displays-new/connector-analog-tv.c
+++ b/drivers/video/omap2/displays-new/connector-analog-tv.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -42,6 +43,12 @@ static const struct omap_video_timings tvc_pal_timings = {
.interlace = true,
};
+static const struct of_device_id tvc_of_match[];
+
+struct tvc_of_data {
+ enum omap_dss_venc_type connector_type;
+};
+
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tvc_connect(struct omap_dss_device *dssdev)
@@ -91,8 +98,12 @@ static int tvc_enable(struct omap_dss_device *dssdev)
in->ops.atv->set_timings(in, &ddata->timings);
- in->ops.atv->set_type(in, ddata->connector_type);
- in->ops.atv->invert_vid_out_polarity(in, ddata->invert_polarity);
+ if (!ddata->dev->of_node) {
+ in->ops.atv->set_type(in, ddata->connector_type);
+
+ in->ops.atv->invert_vid_out_polarity(in,
+ ddata->invert_polarity);
+ }
r = in->ops.atv->enable(in);
if (r)
@@ -205,6 +216,23 @@ static int tvc_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int tvc_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int tvc_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -222,6 +250,10 @@ static int tvc_probe(struct platform_device *pdev)
r = tvc_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = tvc_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -263,12 +295,19 @@ static int __exit tvc_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id tvc_of_match[] = {
+ { .compatible = "omapdss,svideo-connector", },
+ { .compatible = "omapdss,composite-video-connector", },
+ {},
+};
+
static struct platform_driver tvc_connector_driver = {
.probe = tvc_probe,
.remove = __exit_p(tvc_remove),
.driver = {
.name = "connector-analog-tv",
.owner = THIS_MODULE,
+ .of_match_table = tvc_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/connector-dvi.c b/drivers/video/omap2/displays-new/connector-dvi.c
index d18e4b8c0731..74de2bc50c4f 100644
--- a/drivers/video/omap2/displays-new/connector-dvi.c
+++ b/drivers/video/omap2/displays-new/connector-dvi.c
@@ -277,6 +277,37 @@ static int dvic_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int dvic_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+ struct device_node *adapter_node;
+ struct i2c_adapter *adapter;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
+ if (adapter_node) {
+ adapter = of_find_i2c_adapter_by_node(adapter_node);
+ if (adapter == NULL) {
+ dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
+ omap_dss_put_device(ddata->in);
+ return -EPROBE_DEFER;
+ }
+
+ ddata->i2c_adapter = adapter;
+ }
+
+ return 0;
+}
+
static int dvic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -293,6 +324,10 @@ static int dvic_probe(struct platform_device *pdev)
r = dvic_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = dvic_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -342,12 +377,20 @@ static int __exit dvic_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id dvic_of_match[] = {
+ { .compatible = "omapdss,dvi-connector", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dvic_of_match);
+
static struct platform_driver dvi_connector_driver = {
.probe = dvic_probe,
.remove = __exit_p(dvic_remove),
.driver = {
.name = "connector-dvi",
.owner = THIS_MODULE,
+ .of_match_table = dvic_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/connector-hdmi.c b/drivers/video/omap2/displays-new/connector-hdmi.c
index 9393e2d6473d..29ed21b9dce5 100644
--- a/drivers/video/omap2/displays-new/connector-hdmi.c
+++ b/drivers/video/omap2/displays-new/connector-hdmi.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <drm/drm_edid.h>
@@ -301,6 +302,23 @@ static int hdmic_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int hdmic_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int hdmic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -318,6 +336,10 @@ static int hdmic_probe(struct platform_device *pdev)
r = hdmic_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = hdmic_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -359,12 +381,20 @@ static int __exit hdmic_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id hdmic_of_match[] = {
+ { .compatible = "omapdss,hdmi-connector", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, hdmic_of_match);
+
static struct platform_driver hdmi_connector_driver = {
.probe = hdmic_probe,
.remove = __exit_p(hdmic_remove),
.driver = {
.name = "connector-hdmi",
.owner = THIS_MODULE,
+ .of_match_table = hdmic_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/encoder-tfp410.c b/drivers/video/omap2/displays-new/encoder-tfp410.c
index 4a291e756be9..b4e9a42a79e6 100644
--- a/drivers/video/omap2/displays-new/encoder-tfp410.c
+++ b/drivers/video/omap2/displays-new/encoder-tfp410.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -82,7 +83,8 @@ static int tfp410_enable(struct omap_dss_device *dssdev)
return 0;
in->ops.dpi->set_timings(in, &ddata->timings);
- in->ops.dpi->set_data_lines(in, ddata->data_lines);
+ if (ddata->data_lines)
+ in->ops.dpi->set_data_lines(in, ddata->data_lines);
r = in->ops.dpi->enable(in);
if (r)
@@ -179,6 +181,33 @@ static int tfp410_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int tfp410_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+ int gpio;
+
+ gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
+
+ if (gpio_is_valid(gpio) || gpio == -ENOENT) {
+ ddata->pd_gpio = gpio;
+ } else {
+ dev_err(&pdev->dev, "failed to parse PD gpio\n");
+ return gpio;
+ }
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int tfp410_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
@@ -195,6 +224,10 @@ static int tfp410_probe(struct platform_device *pdev)
r = tfp410_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = tfp410_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -251,12 +284,20 @@ static int __exit tfp410_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id tfp410_of_match[] = {
+ { .compatible = "omapdss,ti,tfp410", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tfp410_of_match);
+
static struct platform_driver tfp410_driver = {
.probe = tfp410_probe,
.remove = __exit_p(tfp410_remove),
.driver = {
.name = "tfp410",
.owner = THIS_MODULE,
+ .of_match_table = tfp410_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/omap2/displays-new/encoder-tpd12s015.c
index d5c936cb217f..7e33686171e3 100644
--- a/drivers/video/omap2/displays-new/encoder-tpd12s015.c
+++ b/drivers/video/omap2/displays-new/encoder-tpd12s015.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -289,6 +290,49 @@ static int tpd_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int tpd_probe_of(struct platform_device *pdev)
+{
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_dss_device *in;
+ int gpio;
+
+ /* CT CP HPD GPIO */
+ gpio = of_get_gpio(node, 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(&pdev->dev, "failed to parse CT CP HPD gpio\n");
+ return gpio;
+ }
+ ddata->ct_cp_hpd_gpio = gpio;
+
+ /* LS OE GPIO */
+ gpio = of_get_gpio(node, 1);
+ if (gpio_is_valid(gpio) || gpio == -ENOENT) {
+ ddata->ls_oe_gpio = gpio;
+ } else {
+ dev_err(&pdev->dev, "failed to parse LS OE gpio\n");
+ return gpio;
+ }
+
+ /* HPD GPIO */
+ gpio = of_get_gpio(node, 2);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(&pdev->dev, "failed to parse HPD gpio\n");
+ return gpio;
+ }
+ ddata->hpd_gpio = gpio;
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ return 0;
+}
+
static int tpd_probe(struct platform_device *pdev)
{
struct omap_dss_device *in, *dssdev;
@@ -307,6 +351,10 @@ static int tpd_probe(struct platform_device *pdev)
r = tpd_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = tpd_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -379,12 +427,20 @@ static int __exit tpd_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id tpd_of_match[] = {
+ { .compatible = "omapdss,ti,tpd12s015", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tpd_of_match);
+
static struct platform_driver tpd_driver = {
.probe = tpd_probe,
.remove = __exit_p(tpd_remove),
.driver = {
.name = "tpd12s015",
.owner = THIS_MODULE,
+ .of_match_table = tpd_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/panel-dsi-cm.c b/drivers/video/omap2/displays-new/panel-dsi-cm.c
index f317c878a259..d6f14e8717e8 100644
--- a/drivers/video/omap2/displays-new/panel-dsi-cm.c
+++ b/drivers/video/omap2/displays-new/panel-dsi-cm.c
@@ -22,6 +22,8 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -595,10 +597,13 @@ static int dsicm_power_on(struct panel_drv_data *ddata)
.lp_clk_max = 10000000,
};
- r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
- if (r) {
- dev_err(&ddata->pdev->dev, "failed to configure DSI pins\n");
- goto err0;
+ if (ddata->pin_config.num_pins > 0) {
+ r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
+ if (r) {
+ dev_err(&ddata->pdev->dev,
+ "failed to configure DSI pins\n");
+ goto err0;
+ }
}
r = in->ops.dsi->set_config(in, &dsi_config);
@@ -1156,6 +1161,41 @@ static int dsicm_probe_pdata(struct platform_device *pdev)
return 0;
}
+static int dsicm_probe_of(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+ struct omap_dss_device *in;
+ int gpio;
+
+ gpio = of_get_named_gpio(node, "reset-gpios", 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(&pdev->dev, "failed to parse reset gpio\n");
+ return gpio;
+ }
+ ddata->reset_gpio = gpio;
+
+ gpio = of_get_named_gpio(node, "te-gpios", 0);
+ if (gpio_is_valid(gpio) || gpio == -ENOENT) {
+ ddata->ext_te_gpio = gpio;
+ } else {
+ dev_err(&pdev->dev, "failed to parse TE gpio\n");
+ return gpio;
+ }
+
+ in = omapdss_of_find_source_for_first_ep(node);
+ if (IS_ERR(in)) {
+ dev_err(&pdev->dev, "failed to find video source\n");
+ return PTR_ERR(in);
+ }
+
+ ddata->in = in;
+
+ /* TODO: ulps, backlight */
+
+ return 0;
+}
+
static int dsicm_probe(struct platform_device *pdev)
{
struct backlight_properties props;
@@ -1178,6 +1218,10 @@ static int dsicm_probe(struct platform_device *pdev)
r = dsicm_probe_pdata(pdev);
if (r)
return r;
+ } else if (pdev->dev.of_node) {
+ r = dsicm_probe_of(pdev);
+ if (r)
+ return r;
} else {
return -ENODEV;
}
@@ -1320,12 +1364,20 @@ static int __exit dsicm_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id dsicm_of_match[] = {
+ { .compatible = "omapdss,panel-dsi-cm", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dsicm_of_match);
+
static struct platform_driver dsicm_driver = {
.probe = dsicm_probe,
.remove = __exit_p(dsicm_remove),
.driver = {
.name = "panel-dsi-cm",
.owner = THIS_MODULE,
+ .of_match_table = dsicm_of_match,
},
};
diff --git a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
index 27f60ad6b2ab..c7ba4d8b928a 100644
--- a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
+++ b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
@@ -30,6 +30,8 @@
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
@@ -547,7 +549,9 @@ static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
in->ops.sdi->set_timings(in, &ddata->videomode);
- in->ops.sdi->set_datapairs(in, ddata->datapairs);
+
+ if (ddata->datapairs > 0)
+ in->ops.sdi->set_datapairs(in, ddata->datapairs);
r = in->ops.sdi->enable(in);
if (r) {
@@ -726,6 +730,22 @@ static int acx565akm_probe_pdata(struct spi_device *spi)
return 0;
}
+static int acx565akm_probe_of(struct spi_device *spi)
+{
+ struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+ struct device_node *np = spi->dev.of_node;
+
+ ddata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+
+ ddata->in = omapdss_of_find_source_for_first_ep(np);
+ if (IS_ERR(ddata->in)) {
+ dev_err(&spi->dev, "failed to find video source\n");
+ return PTR_ERR(ddata->in);
+ }
+
+ return 0;
+}
+
static int acx565akm_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
@@ -753,7 +773,12 @@ static int acx565akm_probe(struct spi_device *spi)
r = acx565akm_probe_pdata(spi);
if (r)
return r;
+ } else if (spi->dev.of_node) {
+ r = acx565akm_probe_of(spi);
+ if (r)
+ return r;
} else {
+ dev_err(&spi->dev, "platform data missing!\n");
return -ENODEV;
}
@@ -864,10 +889,16 @@ static int acx565akm_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id acx565akm_of_match[] = {
+ { .compatible = "omapdss,sony,acx565akm", },
+ {},
+};
+
static struct spi_driver acx565akm_driver = {
.driver = {
.name = "acx565akm",
.owner = THIS_MODULE,
+ .of_match_table = acx565akm_of_match,
},
.probe = acx565akm_probe,
.remove = acx565akm_remove,
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index d3aa91bdd6a8..8aec8bda27cc 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -1,7 +1,7 @@
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
# Core DSS files
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
- output.o
+ output.o dss-of.o
# DSS compat layer files
omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
dispc-compat.o display-sysfs.o
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index aaecbf347748..2bbdb7ff7daf 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -3778,12 +3778,20 @@ static const struct dev_pm_ops dispc_pm_ops = {
.runtime_resume = dispc_runtime_resume,
};
+static const struct of_device_id dispc_of_match[] = {
+ { .compatible = "ti,omap2-dispc", },
+ { .compatible = "ti,omap3-dispc", },
+ { .compatible = "ti,omap4-dispc", },
+ {},
+};
+
static struct platform_driver omap_dispchw_driver = {
.remove = __exit_p(omap_dispchw_remove),
.driver = {
.name = "omapdss_dispc",
.owner = THIS_MODULE,
.pm = &dispc_pm_ops,
+ .of_match_table = dispc_of_match,
},
};
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 9f19ae22944c..2412a0dd0c13 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -133,9 +134,32 @@ static int disp_num_counter;
int omapdss_register_display(struct omap_dss_device *dssdev)
{
struct omap_dss_driver *drv = dssdev->driver;
+ int id;
- snprintf(dssdev->alias, sizeof(dssdev->alias),
- "display%d", disp_num_counter++);
+ /*
+ * Note: this presumes all the displays are either using DT or non-DT,
+ * which normally should be the case. This also presumes that all
+ * displays either have an DT alias, or none has.
+ */
+
+ if (dssdev->dev->of_node) {
+ id = of_alias_get_id(dssdev->dev->of_node, "display");
+
+ if (id < 0)
+ id = disp_num_counter++;
+ } else {
+ id = disp_num_counter++;
+ }
+
+ snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);
+
+ /* Use 'label' property for name, if it exists */
+ if (dssdev->dev->of_node)
+ of_property_read_string(dssdev->dev->of_node, "label",
+ &dssdev->name);
+
+ if (dssdev->name == NULL)
+ dssdev->name = dssdev->alias;
if (drv && drv->get_resolution == NULL)
drv->get_resolution = omapdss_default_get_resolution;
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index 6c0bb099b7bf..157921db447a 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
+#include <linux/of.h>
#include <video/omapdss.h>
@@ -49,6 +50,8 @@ static struct {
int data_lines;
struct omap_dss_device output;
+
+ bool port_initialized;
} dpi;
static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
@@ -725,3 +728,47 @@ void __exit dpi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_dpi_driver);
}
+
+int __init dpi_init_port(struct platform_device *pdev, struct device_node *port)
+{
+ struct device_node *ep;
+ u32 datalines;
+ int r;
+
+ ep = omapdss_of_get_next_endpoint(port, NULL);
+ if (!ep)
+ return 0;
+
+ r = of_property_read_u32(ep, "data-lines", &datalines);
+ if (r) {
+ DSSERR("failed to parse datalines\n");
+ goto err_datalines;
+ }
+
+ dpi.data_lines = datalines;
+
+ of_node_put(ep);
+
+ dpi.pdev = pdev;
+
+ mutex_init(&dpi.lock);
+
+ dpi_init_output(pdev);
+
+ dpi.port_initialized = true;
+
+ return 0;
+
+err_datalines:
+ of_node_put(ep);
+
+ return r;
+}
+
+void __exit dpi_uninit_port(void)
+{
+ if (!dpi.port_initialized)
+ return;
+
+ dpi_uninit_output(dpi.pdev);
+}
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 0d82f731d2f0..121d1049d0bc 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -38,6 +38,8 @@
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <video/omapdss.h>
#include <video/mipi_display.h>
@@ -386,6 +388,13 @@ struct dsi_packet_sent_handler_data {
struct completion *completion;
};
+struct dsi_module_id_data {
+ u32 address;
+ int id;
+};
+
+static const struct of_device_id dsi_of_match[];
+
#ifdef DSI_PERF_MEASURE
static bool dsi_perf;
module_param(dsi_perf, bool, 0644);
@@ -1151,15 +1160,11 @@ static int dsi_regulator_init(struct platform_device *dsidev)
if (dsi->vdds_dsi_reg != NULL)
return 0;
- vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdds_dsi");
-
- /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
- if (IS_ERR(vdds_dsi))
- vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "VCXIO");
+ vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdd");
if (IS_ERR(vdds_dsi)) {
if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
- DSSERR("can't get VDDS_DSI regulator\n");
+ DSSERR("can't get DSI VDD regulator\n");
return PTR_ERR(vdds_dsi);
}
@@ -5370,12 +5375,69 @@ static void dsi_uninit_output(struct platform_device *dsidev)
omapdss_unregister_output(out);
}
+static int dsi_probe_of(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
+ struct property *prop;
+ u32 lane_arr[10];
+ int len, num_pins;
+ int r, i;
+ struct device_node *ep;
+ struct omap_dsi_pin_config pin_cfg;
+
+ ep = omapdss_of_get_first_endpoint(node);
+ if (!ep)
+ return 0;
+
+ prop = of_find_property(ep, "lanes", &len);
+ if (prop == NULL) {
+ dev_err(&pdev->dev, "failed to find lane data\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ num_pins = len / sizeof(u32);
+
+ if (num_pins < 4 || num_pins % 2 != 0 ||
+ num_pins > dsi->num_lanes_supported * 2) {
+ dev_err(&pdev->dev, "bad number of lanes\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins);
+ if (r) {
+ dev_err(&pdev->dev, "failed to read lane data\n");
+ goto err;
+ }
+
+ pin_cfg.num_pins = num_pins;
+ for (i = 0; i < num_pins; ++i)
+ pin_cfg.pins[i] = (int)lane_arr[i];
+
+ r = dsi_configure_pins(&dsi->output, &pin_cfg);
+ if (r) {
+ dev_err(&pdev->dev, "failed to configure pins");
+ goto err;
+ }
+
+ of_node_put(ep);
+
+ return 0;
+
+err:
+ of_node_put(ep);
+ return r;
+}
+
/* DSI1 HW IP initialisation */
static int omap_dsihw_probe(struct platform_device *dsidev)
{
u32 rev;
int r, i;
struct dsi_data *dsi;
+ struct resource *dsi_mem;
struct resource *res;
struct resource temp_res;
@@ -5383,7 +5445,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
if (!dsi)
return -ENOMEM;
- dsi->module_id = dsidev->id;
dsi->pdev = dsidev;
dev_set_drvdata(&dsidev->dev, dsi);
@@ -5421,6 +5482,8 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
res = &temp_res;
}
+ dsi_mem = res;
+
dsi->proto_base = devm_ioremap(&dsidev->dev, res->start,
resource_size(res));
if (!dsi->proto_base) {
@@ -5481,6 +5544,31 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
return r;
}
+ if (dsidev->dev.of_node) {
+ const struct of_device_id *match;
+ const struct dsi_module_id_data *d;
+
+ match = of_match_node(dsi_of_match, dsidev->dev.of_node);
+ if (!match) {
+ DSSERR("unsupported DSI module\n");
+ return -ENODEV;
+ }
+
+ d = match->data;
+
+ while (d->address != 0 && d->address != dsi_mem->start)
+ d++;
+
+ if (d->address == 0) {
+ DSSERR("unsupported DSI module\n");
+ return -ENODEV;
+ }
+
+ dsi->module_id = d->id;
+ } else {
+ dsi->module_id = dsidev->id;
+ }
+
/* DSI VCs initialization */
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
dsi->vc[i].source = DSI_VC_SOURCE_L4;
@@ -5516,6 +5604,19 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
dsi_init_output(dsidev);
+ if (dsidev->dev.of_node) {
+ r = dsi_probe_of(dsidev);
+ if (r) {
+ DSSERR("Invalid DSI DT data\n");
+ goto err_probe_of;
+ }
+
+ r = of_platform_populate(dsidev->dev.of_node, NULL, NULL,
+ &dsidev->dev);
+ if (r)
+ DSSERR("Failed to populate DSI child devices: %d\n", r);
+ }
+
dsi_runtime_put(dsidev);
if (dsi->module_id == 0)
@@ -5529,17 +5630,31 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
else if (dsi->module_id == 1)
dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs);
#endif
+
return 0;
+err_probe_of:
+ dsi_uninit_output(dsidev);
+ dsi_runtime_put(dsidev);
+
err_runtime_get:
pm_runtime_disable(&dsidev->dev);
return r;
}
+static int dsi_unregister_child(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ platform_device_unregister(pdev);
+ return 0;
+}
+
static int __exit omap_dsihw_remove(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ device_for_each_child(&dsidev->dev, NULL, dsi_unregister_child);
+
WARN_ON(dsi->scp_clk_refcount > 0);
dsi_uninit_output(dsidev);
@@ -5577,6 +5692,23 @@ static const struct dev_pm_ops dsi_pm_ops = {
.runtime_resume = dsi_runtime_resume,
};
+static const struct dsi_module_id_data dsi_of_data_omap3[] = {
+ { .address = 0x4804fc00, .id = 0, },
+ { },
+};
+
+static const struct dsi_module_id_data dsi_of_data_omap4[] = {
+ { .address = 0x58004000, .id = 0, },
+ { .address = 0x58005000, .id = 1, },
+ { },
+};
+
+static const struct of_device_id dsi_of_match[] = {
+ { .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, },
+ { .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, },
+ {},
+};
+
static struct platform_driver omap_dsihw_driver = {
.probe = omap_dsihw_probe,
.remove = __exit_p(omap_dsihw_remove),
@@ -5584,6 +5716,7 @@ static struct platform_driver omap_dsihw_driver = {
.name = "omapdss_dsi",
.owner = THIS_MODULE,
.pm = &dsi_pm_ops,
+ .of_match_table = dsi_of_match,
},
};
diff --git a/drivers/video/omap2/dss/dss-of.c b/drivers/video/omap2/dss/dss-of.c
new file mode 100644
index 000000000000..a4b20aaf6142
--- /dev/null
+++ b/drivers/video/omap2/dss/dss-of.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/seq_file.h>
+
+#include <video/omapdss.h>
+
+struct device_node *
+omapdss_of_get_next_port(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *port = NULL;
+
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *ports;
+ /*
+ * It's the first call, we have to find a port subnode
+ * within this node or within an optional 'ports' node.
+ */
+ ports = of_get_child_by_name(parent, "ports");
+ if (ports)
+ parent = ports;
+
+ port = of_get_child_by_name(parent, "port");
+
+ /* release the 'ports' node */
+ of_node_put(ports);
+ } else {
+ struct device_node *ports;
+
+ ports = of_get_parent(prev);
+ if (!ports)
+ return NULL;
+
+ do {
+ port = of_get_next_child(ports, prev);
+ if (!port) {
+ of_node_put(ports);
+ return NULL;
+ }
+ prev = port;
+ } while (of_node_cmp(port->name, "port") != 0);
+ }
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_next_port);
+
+struct device_node *
+omapdss_of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *ep = NULL;
+
+ if (!parent)
+ return NULL;
+
+ do {
+ ep = of_get_next_child(parent, prev);
+ if (!ep)
+ return NULL;
+ prev = ep;
+ } while (of_node_cmp(ep->name, "endpoint") != 0);
+
+ return ep;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint);
+
+static struct device_node *
+omapdss_of_get_remote_device_node(const struct device_node *node)
+{
+ struct device_node *np;
+ int i;
+
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+
+ if (!np)
+ return NULL;
+
+ np = of_get_next_parent(np);
+
+ for (i = 0; i < 3 && np; ++i) {
+ struct property *prop;
+
+ prop = of_find_property(np, "compatible", NULL);
+
+ if (prop)
+ return np;
+
+ np = of_get_next_parent(np);
+ }
+
+ return NULL;
+}
+
+struct device_node *
+omapdss_of_get_first_endpoint(const struct device_node *parent)
+{
+ struct device_node *port, *ep;
+
+ port = omapdss_of_get_next_port(parent, NULL);
+
+ if (!port)
+ return NULL;
+
+ ep = omapdss_of_get_next_endpoint(port, NULL);
+
+ of_node_put(port);
+
+ return ep;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint);
+
+struct omap_dss_device *
+omapdss_of_find_source_for_first_ep(struct device_node *node)
+{
+ struct device_node *ep;
+ struct device_node *src_node;
+ struct omap_dss_device *src;
+
+ ep = omapdss_of_get_first_endpoint(node);
+ if (!ep)
+ return ERR_PTR(-EINVAL);
+
+ src_node = omapdss_of_get_remote_device_node(ep);
+
+ of_node_put(ep);
+
+ if (!src_node)
+ return ERR_PTR(-EINVAL);
+
+ src = omap_dss_find_output_by_node(src_node);
+
+ of_node_put(src_node);
+
+ if (!src)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return src;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep);
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 96e400c51001..825c019ddee7 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -23,6 +23,7 @@
#define DSS_SUBSYS_NAME "DSS"
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/err.h>
@@ -33,6 +34,7 @@
#include <linux/pm_runtime.h>
#include <linux/gfp.h>
#include <linux/sizes.h>
+#include <linux/of.h>
#include <video/omapdss.h>
@@ -772,6 +774,56 @@ static int __init dss_init_features(struct platform_device *pdev)
return 0;
}
+static int __init dss_init_ports(struct platform_device *pdev)
+{
+ struct device_node *parent = pdev->dev.of_node;
+ struct device_node *port;
+ int r;
+
+ if (parent == NULL)
+ return 0;
+
+ port = omapdss_of_get_next_port(parent, NULL);
+ if (!port) {
+#ifdef CONFIG_OMAP2_DSS_DPI
+ dpi_init_port(pdev, parent);
+#endif
+ return 0;
+ }
+
+ do {
+ u32 reg;
+
+ r = of_property_read_u32(port, "reg", &reg);
+ if (r)
+ reg = 0;
+
+#ifdef CONFIG_OMAP2_DSS_DPI
+ if (reg == 0)
+ dpi_init_port(pdev, port);
+#endif
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+ if (reg == 1)
+ sdi_init_port(pdev, port);
+#endif
+
+ } while ((port = omapdss_of_get_next_port(parent, port)) != NULL);
+
+ return 0;
+}
+
+static void dss_uninit_ports(void)
+{
+#ifdef CONFIG_OMAP2_DSS_DPI
+ dpi_uninit_port();
+#endif
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+ sdi_uninit_port();
+#endif
+}
+
/* DSS HW IP initialisation */
static int __init omap_dsshw_probe(struct platform_device *pdev)
{
@@ -830,6 +882,8 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
+ dss_init_ports(pdev);
+
rev = dss_read_reg(DSS_REVISION);
printk(KERN_INFO "OMAP DSS rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
@@ -849,6 +903,8 @@ err_setup_clocks:
static int __exit omap_dsshw_remove(struct platform_device *pdev)
{
+ dss_uninit_ports();
+
pm_runtime_disable(&pdev->dev);
dss_put_clocks();
@@ -886,12 +942,22 @@ static const struct dev_pm_ops dss_pm_ops = {
.runtime_resume = dss_runtime_resume,
};
+static const struct of_device_id dss_of_match[] = {
+ { .compatible = "ti,omap2-dss", },
+ { .compatible = "ti,omap3-dss", },
+ { .compatible = "ti,omap4-dss", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dss_of_match);
+
static struct platform_driver omap_dsshw_driver = {
.remove = __exit_p(omap_dsshw_remove),
.driver = {
.name = "omapdss_dss",
.owner = THIS_MODULE,
.pm = &dss_pm_ops,
+ .of_match_table = dss_of_match,
},
};
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 570f7ed2bcbc..918fec182424 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -250,6 +250,9 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min,
int sdi_init_platform_driver(void) __init;
void sdi_uninit_platform_driver(void) __exit;
+int sdi_init_port(struct platform_device *pdev, struct device_node *port) __init;
+void sdi_uninit_port(void) __exit;
+
/* DSI */
typedef bool (*dsi_pll_calc_func)(int regn, int regm, unsigned long fint,
@@ -361,6 +364,9 @@ static inline bool dsi_pll_calc(struct platform_device *dsidev,
int dpi_init_platform_driver(void) __init;
void dpi_uninit_platform_driver(void) __exit;
+int dpi_init_port(struct platform_device *pdev, struct device_node *port) __init;
+void dpi_uninit_port(void) __exit;
+
/* DISPC */
int dispc_init_platform_driver(void) __init;
void dispc_uninit_platform_driver(void) __exit;
diff --git a/drivers/video/omap2/dss/hdmi4.c b/drivers/video/omap2/dss/hdmi4.c
index 895c252ae0a8..f5f7944a1fd1 100644
--- a/drivers/video/omap2/dss/hdmi4.c
+++ b/drivers/video/omap2/dss/hdmi4.c
@@ -88,15 +88,11 @@ static int hdmi_init_regulator(void)
if (hdmi.vdda_hdmi_dac_reg != NULL)
return 0;
- reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac");
-
- /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */
- if (IS_ERR(reg))
- reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC");
+ reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
if (IS_ERR(reg)) {
if (PTR_ERR(reg) != -EPROBE_DEFER)
- DSSERR("can't get VDDA_HDMI_DAC regulator\n");
+ DSSERR("can't get VDDA regulator\n");
return PTR_ERR(reg);
}
@@ -680,6 +676,11 @@ static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_resume = hdmi_runtime_resume,
};
+static const struct of_device_id hdmi_of_match[] = {
+ { .compatible = "ti,omap4-hdmi", },
+ {},
+};
+
static struct platform_driver omapdss_hdmihw_driver = {
.probe = omapdss_hdmihw_probe,
.remove = __exit_p(omapdss_hdmihw_remove),
@@ -687,6 +688,7 @@ static struct platform_driver omapdss_hdmihw_driver = {
.name = "omapdss_hdmi",
.owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
+ .of_match_table = hdmi_of_match,
},
};
diff --git a/drivers/video/omap2/dss/hdmi_wp.c b/drivers/video/omap2/dss/hdmi_wp.c
index cd620c6e43a0..f5f4ccf50d90 100644
--- a/drivers/video/omap2/dss/hdmi_wp.c
+++ b/drivers/video/omap2/dss/hdmi_wp.c
@@ -171,6 +171,8 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
video_fmt->y_res = param->timings.y_res;
video_fmt->x_res = param->timings.x_res;
+ if (param->timings.interlace)
+ video_fmt->y_res /= 2;
timings->hbp = param->timings.hbp;
timings->hfp = param->timings.hfp;
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index b679e33adf2d..911dcc9173a6 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -26,6 +26,7 @@
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/string.h>
+#include <linux/of.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -41,6 +42,8 @@ static struct {
int datapairs;
struct omap_dss_device output;
+
+ bool port_initialized;
} sdi;
struct sdi_clk_calc_ctx {
@@ -386,3 +389,45 @@ void __exit sdi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_sdi_driver);
}
+
+int __init sdi_init_port(struct platform_device *pdev, struct device_node *port)
+{
+ struct device_node *ep;
+ u32 datapairs;
+ int r;
+
+ ep = omapdss_of_get_next_endpoint(port, NULL);
+ if (!ep)
+ return 0;
+
+ r = of_property_read_u32(ep, "datapairs", &datapairs);
+ if (r) {
+ DSSERR("failed to parse datapairs\n");
+ goto err_datapairs;
+ }
+
+ sdi.datapairs = datapairs;
+
+ of_node_put(ep);
+
+ sdi.pdev = pdev;
+
+ sdi_init_output(pdev);
+
+ sdi.port_initialized = true;
+
+ return 0;
+
+err_datapairs:
+ of_node_put(ep);
+
+ return r;
+}
+
+void __exit sdi_uninit_port(void)
+{
+ if (!sdi.port_initialized)
+ return;
+
+ sdi_uninit_output(sdi.pdev);
+}
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 59ade34bd536..21d81113962b 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -34,6 +34,7 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
#include <video/omapdss.h>
@@ -636,7 +637,10 @@ static int venc_init_regulator(void)
if (venc.vdda_dac_reg != NULL)
return 0;
- vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac");
+ if (venc.pdev->dev.of_node)
+ vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda");
+ else
+ vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac");
if (IS_ERR(vdda_dac)) {
if (PTR_ERR(vdda_dac) != -EPROBE_DEFER)
@@ -805,6 +809,48 @@ static void __exit venc_uninit_output(struct platform_device *pdev)
omapdss_unregister_output(out);
}
+static int venc_probe_of(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *ep;
+ u32 channels;
+ int r;
+
+ ep = omapdss_of_get_first_endpoint(node);
+ if (!ep)
+ return 0;
+
+ venc.invert_polarity = of_property_read_bool(ep, "ti,invert-polarity");
+
+ r = of_property_read_u32(ep, "ti,channels", &channels);
+ if (r) {
+ dev_err(&pdev->dev,
+ "failed to read property 'ti,channels': %d\n", r);
+ goto err;
+ }
+
+ switch (channels) {
+ case 1:
+ venc.type = OMAP_DSS_VENC_TYPE_COMPOSITE;
+ break;
+ case 2:
+ venc.type = OMAP_DSS_VENC_TYPE_SVIDEO;
+ break;
+ default:
+ dev_err(&pdev->dev, "bad channel propert '%d'\n", channels);
+ r = -EINVAL;
+ goto err;
+ }
+
+ of_node_put(ep);
+
+ return 0;
+err:
+ of_node_put(ep);
+
+ return 0;
+}
+
/* VENC HW IP initialisation */
static int omap_venchw_probe(struct platform_device *pdev)
{
@@ -846,12 +892,21 @@ static int omap_venchw_probe(struct platform_device *pdev)
venc_runtime_put();
+ if (pdev->dev.of_node) {
+ r = venc_probe_of(pdev);
+ if (r) {
+ DSSERR("Invalid DT data\n");
+ goto err_probe_of;
+ }
+ }
+
dss_debugfs_create_file("venc", venc_dump_regs);
venc_init_output(pdev);
return 0;
+err_probe_of:
err_runtime_get:
pm_runtime_disable(&pdev->dev);
return r;
@@ -895,6 +950,14 @@ static const struct dev_pm_ops venc_pm_ops = {
.runtime_resume = venc_runtime_resume,
};
+
+static const struct of_device_id venc_of_match[] = {
+ { .compatible = "ti,omap2-venc", },
+ { .compatible = "ti,omap3-venc", },
+ { .compatible = "ti,omap4-venc", },
+ {},
+};
+
static struct platform_driver omap_venchw_driver = {
.probe = omap_venchw_probe,
.remove = __exit_p(omap_venchw_remove),
@@ -902,6 +965,7 @@ static struct platform_driver omap_venchw_driver = {
.name = "omapdss_venc",
.owner = THIS_MODULE,
.pm = &venc_pm_ops,
+ .of_match_table = venc_of_match,
},
};
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 8d02f164c8c6..ec2d132c782d 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -2417,6 +2417,55 @@ static int omapfb_init_connections(struct omapfb2_device *fbdev,
return 0;
}
+static struct omap_dss_device *
+omapfb_find_default_display(struct omapfb2_device *fbdev)
+{
+ const char *def_name;
+ int i;
+
+ /*
+ * Search with the display name from the user or the board file,
+ * comparing to display names and aliases
+ */
+
+ def_name = omapdss_get_default_display_name();
+
+ if (def_name) {
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ struct omap_dss_device *dssdev;
+
+ dssdev = fbdev->displays[i].dssdev;
+
+ if (dssdev->name && strcmp(def_name, dssdev->name) == 0)
+ return dssdev;
+
+ if (strcmp(def_name, dssdev->alias) == 0)
+ return dssdev;
+ }
+
+ /* def_name given but not found */
+ return NULL;
+ }
+
+ /* then look for DT alias display0 */
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ struct omap_dss_device *dssdev;
+ int id;
+
+ dssdev = fbdev->displays[i].dssdev;
+
+ if (dssdev->dev->of_node == NULL)
+ continue;
+
+ id = of_alias_get_id(dssdev->dev->of_node, "display");
+ if (id == 0)
+ return dssdev;
+ }
+
+ /* return the first display we have in the list */
+ return fbdev->displays[0].dssdev;
+}
+
static int omapfb_probe(struct platform_device *pdev)
{
struct omapfb2_device *fbdev = NULL;
@@ -2494,23 +2543,7 @@ static int omapfb_probe(struct platform_device *pdev)
for (i = 0; i < fbdev->num_managers; i++)
fbdev->managers[i] = omap_dss_get_overlay_manager(i);
- def_display = NULL;
-
- for (i = 0; i < fbdev->num_displays; ++i) {
- struct omap_dss_device *dssdev;
- const char *def_name;
-
- def_name = omapdss_get_default_display_name();
-
- dssdev = fbdev->displays[i].dssdev;
-
- if (def_name == NULL ||
- (dssdev->name && strcmp(def_name, dssdev->name) == 0)) {
- def_display = dssdev;
- break;
- }
- }
-
+ def_display = omapfb_find_default_display(fbdev);
if (def_display == NULL) {
dev_err(fbdev->dev, "failed to find default display\n");
r = -EPROBE_DEFER;
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0c6048d5c9a3..74ec8fc5cc03 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -301,7 +301,7 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG
tristate "Orion watchdog"
- depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 0e6c0333f775..0ba1b7c99760 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -48,7 +48,7 @@
/* Module and version information */
#define DRV_NAME "iTCO_wdt"
-#define DRV_VERSION "1.10"
+#define DRV_VERSION "1.11"
/* Includes */
#include <linux/module.h> /* For module specific items */
@@ -92,9 +92,12 @@ static struct { /* this is private data for the iTCO_wdt device */
unsigned int iTCO_version;
struct resource *tco_res;
struct resource *smi_res;
- struct resource *gcs_res;
- /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
- unsigned long __iomem *gcs;
+ /*
+ * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2),
+ * or memory-mapped PMC register bit 4 (TCO version 3).
+ */
+ struct resource *gcs_pmc_res;
+ unsigned long __iomem *gcs_pmc;
/* the lock for io operations */
spinlock_t io_lock;
struct platform_device *dev;
@@ -125,11 +128,19 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
* Some TCO specific functions
*/
-static inline unsigned int seconds_to_ticks(int seconds)
+/*
+ * The iTCO v1 and v2's internal timer is stored as ticks which decrement
+ * every 0.6 seconds. v3's internal timer is stored as seconds (some
+ * datasheets incorrectly state 0.6 seconds).
+ */
+static inline unsigned int seconds_to_ticks(int secs)
{
- /* the internal timer is stored as ticks which decrement
- * every 0.6 seconds */
- return (seconds * 10) / 6;
+ return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6;
+}
+
+static inline unsigned int ticks_to_seconds(int ticks)
+{
+ return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
}
static void iTCO_wdt_set_NO_REBOOT_bit(void)
@@ -137,10 +148,14 @@ static void iTCO_wdt_set_NO_REBOOT_bit(void)
u32 val32;
/* Set the NO_REBOOT bit: this disables reboots */
- if (iTCO_wdt_private.iTCO_version == 2) {
- val32 = readl(iTCO_wdt_private.gcs);
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ val32 |= 0x00000010;
+ writel(val32, iTCO_wdt_private.gcs_pmc);
+ } else if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 |= 0x00000020;
- writel(val32, iTCO_wdt_private.gcs);
+ writel(val32, iTCO_wdt_private.gcs_pmc);
} else if (iTCO_wdt_private.iTCO_version == 1) {
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
val32 |= 0x00000002;
@@ -154,12 +169,20 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
u32 val32;
/* Unset the NO_REBOOT bit: this enables reboots */
- if (iTCO_wdt_private.iTCO_version == 2) {
- val32 = readl(iTCO_wdt_private.gcs);
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ val32 &= 0xffffffef;
+ writel(val32, iTCO_wdt_private.gcs_pmc);
+
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
+ if (val32 & 0x00000010)
+ ret = -EIO;
+ } else if (iTCO_wdt_private.iTCO_version == 2) {
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
val32 &= 0xffffffdf;
- writel(val32, iTCO_wdt_private.gcs);
+ writel(val32, iTCO_wdt_private.gcs_pmc);
- val32 = readl(iTCO_wdt_private.gcs);
+ val32 = readl(iTCO_wdt_private.gcs_pmc);
if (val32 & 0x00000020)
ret = -EIO;
} else if (iTCO_wdt_private.iTCO_version == 1) {
@@ -192,7 +215,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
/* Force the timer to its reload value by writing to the TCO_RLD
register */
- if (iTCO_wdt_private.iTCO_version == 2)
+ if (iTCO_wdt_private.iTCO_version >= 2)
outw(0x01, TCO_RLD);
else if (iTCO_wdt_private.iTCO_version == 1)
outb(0x01, TCO_RLD);
@@ -240,9 +263,9 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
/* Reload the timer by writing to the TCO Timer Counter register */
- if (iTCO_wdt_private.iTCO_version == 2)
+ if (iTCO_wdt_private.iTCO_version >= 2) {
outw(0x01, TCO_RLD);
- else if (iTCO_wdt_private.iTCO_version == 1) {
+ } else if (iTCO_wdt_private.iTCO_version == 1) {
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw(0x0008, TCO1_STS); /* write 1 to clear bit */
@@ -270,14 +293,14 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
/* "Values of 0h-3h are ignored and should not be attempted" */
if (tmrval < 0x04)
return -EINVAL;
- if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) ||
+ if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) ||
((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
return -EINVAL;
iTCO_vendor_pre_set_heartbeat(tmrval);
/* Write new heartbeat to watchdog */
- if (iTCO_wdt_private.iTCO_version == 2) {
+ if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCOv2_TMR);
val16 &= 0xfc00;
@@ -312,13 +335,13 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
unsigned int time_left = 0;
/* read the TCO Timer */
- if (iTCO_wdt_private.iTCO_version == 2) {
+ if (iTCO_wdt_private.iTCO_version >= 2) {
spin_lock(&iTCO_wdt_private.io_lock);
val16 = inw(TCO_RLD);
val16 &= 0x3ff;
spin_unlock(&iTCO_wdt_private.io_lock);
- time_left = (val16 * 6) / 10;
+ time_left = ticks_to_seconds(val16);
} else if (iTCO_wdt_private.iTCO_version == 1) {
spin_lock(&iTCO_wdt_private.io_lock);
val8 = inb(TCO_RLD);
@@ -327,7 +350,7 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
val8 += (inb(TCOv1_TMR) & 0x3f);
spin_unlock(&iTCO_wdt_private.io_lock);
- time_left = (val8 * 6) / 10;
+ time_left = ticks_to_seconds(val8);
}
return time_left;
}
@@ -376,16 +399,16 @@ static void iTCO_wdt_cleanup(void)
resource_size(iTCO_wdt_private.tco_res));
release_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res));
- if (iTCO_wdt_private.iTCO_version == 2) {
- iounmap(iTCO_wdt_private.gcs);
- release_mem_region(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res));
+ if (iTCO_wdt_private.iTCO_version >= 2) {
+ iounmap(iTCO_wdt_private.gcs_pmc);
+ release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res));
}
iTCO_wdt_private.tco_res = NULL;
iTCO_wdt_private.smi_res = NULL;
- iTCO_wdt_private.gcs_res = NULL;
- iTCO_wdt_private.gcs = NULL;
+ iTCO_wdt_private.gcs_pmc_res = NULL;
+ iTCO_wdt_private.gcs_pmc = NULL;
}
static int iTCO_wdt_probe(struct platform_device *dev)
@@ -414,27 +437,27 @@ static int iTCO_wdt_probe(struct platform_device *dev)
iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
/*
- * Get the Memory-Mapped GCS register, we need it for the
- * NO_REBOOT flag (TCO v2).
+ * Get the Memory-Mapped GCS or PMC register, we need it for the
+ * NO_REBOOT flag (TCO v2 and v3).
*/
- if (iTCO_wdt_private.iTCO_version == 2) {
- iTCO_wdt_private.gcs_res = platform_get_resource(dev,
+ if (iTCO_wdt_private.iTCO_version >= 2) {
+ iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev,
IORESOURCE_MEM,
- ICH_RES_MEM_GCS);
+ ICH_RES_MEM_GCS_PMC);
- if (!iTCO_wdt_private.gcs_res)
+ if (!iTCO_wdt_private.gcs_pmc_res)
goto out;
- if (!request_mem_region(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res), dev->name)) {
+ if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) {
ret = -EBUSY;
goto out;
}
- iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res));
- if (!iTCO_wdt_private.gcs) {
+ iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res));
+ if (!iTCO_wdt_private.gcs_pmc) {
ret = -EIO;
- goto unreg_gcs;
+ goto unreg_gcs_pmc;
}
}
@@ -442,7 +465,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
- goto unmap_gcs;
+ goto unmap_gcs_pmc;
}
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
@@ -454,7 +477,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
pr_err("I/O address 0x%04llx already in use, device disabled\n",
(u64)SMI_EN);
ret = -EBUSY;
- goto unmap_gcs;
+ goto unmap_gcs_pmc;
}
if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
/*
@@ -478,9 +501,13 @@ static int iTCO_wdt_probe(struct platform_device *dev)
ich_info->name, ich_info->iTCO_version, (u64)TCOBASE);
/* Clear out the (probably old) status */
- outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
- outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
- outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
+ if (iTCO_wdt_private.iTCO_version == 3) {
+ outl(0x20008, TCO1_STS);
+ } else {
+ outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
+ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
+ outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
+ }
iTCO_wdt_watchdog_dev.bootstatus = 0;
iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
@@ -515,18 +542,18 @@ unreg_tco:
unreg_smi:
release_region(iTCO_wdt_private.smi_res->start,
resource_size(iTCO_wdt_private.smi_res));
-unmap_gcs:
- if (iTCO_wdt_private.iTCO_version == 2)
- iounmap(iTCO_wdt_private.gcs);
-unreg_gcs:
- if (iTCO_wdt_private.iTCO_version == 2)
- release_mem_region(iTCO_wdt_private.gcs_res->start,
- resource_size(iTCO_wdt_private.gcs_res));
+unmap_gcs_pmc:
+ if (iTCO_wdt_private.iTCO_version >= 2)
+ iounmap(iTCO_wdt_private.gcs_pmc);
+unreg_gcs_pmc:
+ if (iTCO_wdt_private.iTCO_version >= 2)
+ release_mem_region(iTCO_wdt_private.gcs_pmc_res->start,
+ resource_size(iTCO_wdt_private.gcs_pmc_res));
out:
iTCO_wdt_private.tco_res = NULL;
iTCO_wdt_private.smi_res = NULL;
- iTCO_wdt_private.gcs_res = NULL;
- iTCO_wdt_private.gcs = NULL;
+ iTCO_wdt_private.gcs_pmc_res = NULL;
+ iTCO_wdt_private.gcs_pmc = NULL;
return ret;
}
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
index 461208831428..4baf2d788920 100644
--- a/drivers/watchdog/octeon-wdt-main.c
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -708,10 +708,13 @@ static int __init octeon_wdt_init(void)
cpumask_clear(&irq_enabled_cpus);
+ cpu_notifier_register_begin();
for_each_online_cpu(cpu)
octeon_wdt_setup_interrupt(cpu);
- register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ __register_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ cpu_notifier_register_done();
+
out:
return ret;
}
@@ -725,7 +728,8 @@ static void __exit octeon_wdt_cleanup(void)
misc_deregister(&octeon_wdt_miscdev);
- unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
+ cpu_notifier_register_begin();
+ __unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier);
for_each_online_cpu(cpu) {
int core = cpu2core(cpu);
@@ -734,6 +738,9 @@ static void __exit octeon_wdt_cleanup(void)
/* Free the interrupt handler */
free_irq(OCTEON_IRQ_WDOG0 + core, octeon_wdt_poke_irq);
}
+
+ cpu_notifier_register_done();
+
/*
* Disable the boot-bus memory, the code it points to is soon
* to go missing.
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 498163497c1c..9b3c41d18703 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -18,101 +18,204 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <mach/bridge-regs.h>
+#include <linux/of_device.h>
+
+/* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */
+#define ORION_RSTOUT_MASK_OFFSET 0x20108
+
+/* Internal registers can be configured at any 1 MiB aligned address */
+#define INTERNAL_REGS_MASK ~(SZ_1M - 1)
/*
* Watchdog timer block registers.
*/
#define TIMER_CTRL 0x0000
-#define WDT_EN 0x0010
-#define WDT_VAL 0x0024
+#define TIMER_A370_STATUS 0x04
#define WDT_MAX_CYCLE_COUNT 0xffffffff
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-#define WDT_RESET_OUT_EN BIT(1)
-#define WDT_INT_REQ BIT(3)
+#define WDT_A370_RATIO_MASK(v) ((v) << 16)
+#define WDT_A370_RATIO_SHIFT 5
+#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
+
+#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
+#define WDT_A370_EXPIRED BIT(31)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
-static unsigned int wdt_max_duration; /* (seconds) */
-static struct clk *clk;
-static unsigned int wdt_tclk;
-static void __iomem *wdt_reg;
-static DEFINE_SPINLOCK(wdt_lock);
-static int orion_wdt_ping(struct watchdog_device *wdt_dev)
+struct orion_watchdog;
+
+struct orion_watchdog_data {
+ int wdt_counter_offset;
+ int wdt_enable_bit;
+ int rstout_enable_bit;
+ int (*clock_init)(struct platform_device *,
+ struct orion_watchdog *);
+ int (*start)(struct watchdog_device *);
+};
+
+struct orion_watchdog {
+ struct watchdog_device wdt;
+ void __iomem *reg;
+ void __iomem *rstout;
+ unsigned long clk_rate;
+ struct clk *clk;
+ const struct orion_watchdog_data *data;
+};
+
+static int orion_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- spin_lock(&wdt_lock);
+ int ret;
- /* Reload watchdog duration */
- writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
- spin_unlock(&wdt_lock);
+ dev->clk_rate = clk_get_rate(dev->clk);
return 0;
}
-static int orion_wdt_start(struct watchdog_device *wdt_dev)
+static int armada370_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
{
- u32 reg;
+ int ret;
- spin_lock(&wdt_lock);
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
+
+ /* Setup watchdog input clock */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT),
+ WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT));
+
+ dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO;
+ return 0;
+}
+
+static int armadaxp_wdt_clock_init(struct platform_device *pdev,
+ struct orion_watchdog *dev)
+{
+ int ret;
+
+ dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ clk_put(dev->clk);
+ return ret;
+ }
+
+ /* Enable the fixed watchdog clock input */
+ atomic_io_modify(dev->reg + TIMER_CTRL,
+ WDT_AXP_FIXED_ENABLE_BIT,
+ WDT_AXP_FIXED_ENABLE_BIT);
+
+ dev->clk_rate = clk_get_rate(dev->clk);
+ return 0;
+}
+
+static int orion_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ /* Reload watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+ return 0;
+}
+
+static int armada370_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
/* Set watchdog duration */
- writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
- /* Clear watchdog timer interrupt */
- writel(~WDT_INT_REQ, BRIDGE_CAUSE);
+ /* Clear the watchdog expiration bit */
+ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
/* Enable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg |= WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
+
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
+ return 0;
+}
+
+static int orion_start(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ /* Set watchdog duration */
+ writel(dev->clk_rate * wdt_dev->timeout,
+ dev->reg + dev->data->wdt_counter_offset);
+
+ /* Enable watchdog timer */
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
+ dev->data->wdt_enable_bit);
/* Enable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg |= WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
+ dev->data->rstout_enable_bit);
- spin_unlock(&wdt_lock);
return 0;
}
-static int orion_wdt_stop(struct watchdog_device *wdt_dev)
+static int orion_wdt_start(struct watchdog_device *wdt_dev)
{
- u32 reg;
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
- spin_lock(&wdt_lock);
+ /* There are some per-SoC quirks to handle */
+ return dev->data->start(wdt_dev);
+}
+
+static int orion_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
/* Disable reset on watchdog */
- reg = readl(RSTOUTn_MASK);
- reg &= ~WDT_RESET_OUT_EN;
- writel(reg, RSTOUTn_MASK);
+ atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
/* Disable watchdog timer */
- reg = readl(wdt_reg + TIMER_CTRL);
- reg &= ~WDT_EN;
- writel(reg, wdt_reg + TIMER_CTRL);
+ atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
- spin_unlock(&wdt_lock);
return 0;
}
-static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+static int orion_wdt_enabled(struct orion_watchdog *dev)
{
- unsigned int time_left;
+ bool enabled, running;
+
+ enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
+ running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
- spin_lock(&wdt_lock);
- time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk;
- spin_unlock(&wdt_lock);
+ return enabled && running;
+}
- return time_left;
+static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+{
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+ return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
}
static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
@@ -136,68 +239,188 @@ static const struct watchdog_ops orion_wdt_ops = {
.get_timeleft = orion_wdt_get_timeleft,
};
-static struct watchdog_device orion_wdt = {
- .info = &orion_wdt_info,
- .ops = &orion_wdt_ops,
- .min_timeout = 1,
+static irqreturn_t orion_wdt_irq(int irq, void *devid)
+{
+ panic("Watchdog Timeout");
+ return IRQ_HANDLED;
+}
+
+/*
+ * The original devicetree binding for this driver specified only
+ * one memory resource, so in order to keep DT backwards compatibility
+ * we try to fallback to a hardcoded register address, if the resource
+ * is missing from the devicetree.
+ */
+static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
+ phys_addr_t internal_regs)
+{
+ struct resource *res;
+ phys_addr_t rstout;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res)
+ return devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ /* This workaround works only for "orion-wdt", DT-enabled */
+ if (!of_device_is_compatible(pdev->dev.of_node, "marvell,orion-wdt"))
+ return NULL;
+
+ rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
+
+ WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
+ return devm_ioremap(&pdev->dev, rstout, 0x4);
+}
+
+static const struct orion_watchdog_data orion_data = {
+ .rstout_enable_bit = BIT(1),
+ .wdt_enable_bit = BIT(4),
+ .wdt_counter_offset = 0x24,
+ .clock_init = orion_wdt_clock_init,
+ .start = orion_start,
+};
+
+static const struct orion_watchdog_data armada370_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armada370_wdt_clock_init,
+ .start = armada370_start,
};
+static const struct orion_watchdog_data armadaxp_data = {
+ .rstout_enable_bit = BIT(8),
+ .wdt_enable_bit = BIT(8),
+ .wdt_counter_offset = 0x34,
+ .clock_init = armadaxp_wdt_clock_init,
+ .start = armada370_start,
+};
+
+static const struct of_device_id orion_wdt_of_match_table[] = {
+ {
+ .compatible = "marvell,orion-wdt",
+ .data = &orion_data,
+ },
+ {
+ .compatible = "marvell,armada-370-wdt",
+ .data = &armada370_data,
+ },
+ {
+ .compatible = "marvell,armada-xp-wdt",
+ .data = &armadaxp_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
+
static int orion_wdt_probe(struct platform_device *pdev)
{
+ struct orion_watchdog *dev;
+ const struct of_device_id *match;
+ unsigned int wdt_max_duration; /* (seconds) */
struct resource *res;
- int ret;
+ int ret, irq;
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "Orion Watchdog missing clock\n");
- return -ENODEV;
- }
- clk_prepare_enable(clk);
- wdt_tclk = clk_get_rate(clk);
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
+ if (!match)
+ /* Default legacy match */
+ match = &orion_wdt_of_match_table[0];
+
+ dev->wdt.info = &orion_wdt_info;
+ dev->wdt.ops = &orion_wdt_ops;
+ dev->wdt.min_timeout = 1;
+ dev->data = match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!wdt_reg)
- return -ENOMEM;
- wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
+ dev->reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->reg)
+ return -ENOMEM;
- orion_wdt.timeout = wdt_max_duration;
- orion_wdt.max_timeout = wdt_max_duration;
- watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
+ dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
+ INTERNAL_REGS_MASK);
+ if (!dev->rstout)
+ return -ENODEV;
- watchdog_set_nowayout(&orion_wdt, nowayout);
- ret = watchdog_register_device(&orion_wdt);
+ ret = dev->data->clock_init(pdev, dev);
if (ret) {
- clk_disable_unprepare(clk);
+ dev_err(&pdev->dev, "cannot initialize clock\n");
return ret;
}
+ wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
+
+ dev->wdt.timeout = wdt_max_duration;
+ dev->wdt.max_timeout = wdt_max_duration;
+ watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
+
+ platform_set_drvdata(pdev, &dev->wdt);
+ watchdog_set_drvdata(&dev->wdt, dev);
+
+ /*
+ * Let's make sure the watchdog is fully stopped, unless it's
+ * explicitly enabled. This may be the case if the module was
+ * removed and re-insterted, or if the bootloader explicitly
+ * set a running watchdog before booting the kernel.
+ */
+ if (!orion_wdt_enabled(dev))
+ orion_wdt_stop(&dev->wdt);
+
+ /* Request the IRQ only after the watchdog is disabled */
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ /*
+ * Not all supported platforms specify an interrupt for the
+ * watchdog, so let's make it optional.
+ */
+ ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
+ pdev->name, dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto disable_clk;
+ }
+ }
+
+ watchdog_set_nowayout(&dev->wdt, nowayout);
+ ret = watchdog_register_device(&dev->wdt);
+ if (ret)
+ goto disable_clk;
+
pr_info("Initial timeout %d sec%s\n",
- orion_wdt.timeout, nowayout ? ", nowayout" : "");
+ dev->wdt.timeout, nowayout ? ", nowayout" : "");
return 0;
+
+disable_clk:
+ clk_disable_unprepare(dev->clk);
+ clk_put(dev->clk);
+ return ret;
}
static int orion_wdt_remove(struct platform_device *pdev)
{
- watchdog_unregister_device(&orion_wdt);
- clk_disable_unprepare(clk);
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
+
+ watchdog_unregister_device(wdt_dev);
+ clk_disable_unprepare(dev->clk);
+ clk_put(dev->clk);
return 0;
}
static void orion_wdt_shutdown(struct platform_device *pdev)
{
- orion_wdt_stop(&orion_wdt);
+ struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
+ orion_wdt_stop(wdt_dev);
}
-static const struct of_device_id orion_wdt_of_match_table[] = {
- { .compatible = "marvell,orion-wdt", },
- {},
-};
-MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
-
static struct platform_driver orion_wdt_driver = {
.probe = orion_wdt_probe,
.remove = orion_wdt_remove,
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 61a6ac8fa8fc..b7a506f2bb14 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -604,19 +604,29 @@ static void __init balloon_add_region(unsigned long start_pfn,
}
}
+static int alloc_balloon_scratch_page(int cpu)
+{
+ if (per_cpu(balloon_scratch_page, cpu) != NULL)
+ return 0;
+
+ per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
+ if (per_cpu(balloon_scratch_page, cpu) == NULL) {
+ pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+
static int balloon_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
int cpu = (long)hcpu;
switch (action) {
case CPU_UP_PREPARE:
- if (per_cpu(balloon_scratch_page, cpu) != NULL)
- break;
- per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
- if (per_cpu(balloon_scratch_page, cpu) == NULL) {
- pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
+ if (alloc_balloon_scratch_page(cpu))
return NOTIFY_BAD;
- }
break;
default:
break;
@@ -636,15 +646,17 @@ static int __init balloon_init(void)
return -ENODEV;
if (!xen_feature(XENFEAT_auto_translated_physmap)) {
- for_each_online_cpu(cpu)
- {
- per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
- if (per_cpu(balloon_scratch_page, cpu) == NULL) {
- pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
+ register_cpu_notifier(&balloon_cpu_notifier);
+
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
+ if (alloc_balloon_scratch_page(cpu)) {
+ put_online_cpus();
+ unregister_cpu_notifier(&balloon_cpu_notifier);
return -ENOMEM;
}
}
- register_cpu_notifier(&balloon_cpu_notifier);
+ put_online_cpus();
}
pr_info("Initialising balloon driver\n");
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index d5a3de88ac59..dfa12a4a0a48 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -1248,8 +1248,8 @@ void xen_evtchn_do_upcall(struct pt_regs *regs)
irq_enter();
#ifdef CONFIG_X86
exit_idle();
-#endif
inc_irq_stat(irq_hv_callback_count);
+#endif
__xen_evtchn_do_upcall();