summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt36
-rw-r--r--Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt5
-rw-r--r--Documentation/devicetree/bindings/display/exynos/exynos_hdmi.txt27
-rw-r--r--Documentation/devicetree/bindings/display/fsl,dcu.txt15
-rw-r--r--Documentation/devicetree/bindings/display/fsl,tcon.txt18
-rw-r--r--Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt72
-rw-r--r--Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt64
-rw-r--r--Documentation/devicetree/bindings/display/snps,arcpgu.txt35
-rw-r--r--Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt258
-rw-r--r--MAINTAINERS24
-rw-r--r--arch/arc/boot/dts/axs10x_mb.dtsi61
-rw-r--r--drivers/gpu/drm/Kconfig6
-rw-r--r--drivers/gpu/drm/Makefile5
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c5
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c10
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c12
-rw-r--r--drivers/gpu/drm/arc/Kconfig10
-rw-r--r--drivers/gpu/drm/arc/Makefile2
-rw-r--r--drivers/gpu/drm/arc/arcpgu.h50
-rw-r--r--drivers/gpu/drm/arc/arcpgu_crtc.c257
-rw-r--r--drivers/gpu/drm/arc/arcpgu_drv.c282
-rw-r--r--drivers/gpu/drm/arc/arcpgu_hdmi.c201
-rw-r--r--drivers/gpu/drm/arc/arcpgu_regs.h40
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c17
-rw-r--r--drivers/gpu/drm/drm_crtc.c202
-rw-r--r--drivers/gpu/drm/exynos/Kconfig2
-rw-r--r--drivers/gpu/drm/exynos/Makefile6
-rw-r--r--drivers/gpu/drm/exynos/exynos5433_drm_decon.c86
-rw-r--r--drivers/gpu/drm/exynos/exynos7_drm_decon.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp.c9
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h28
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c27
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.h23
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c99
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_mic.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c12
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_rotator.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c738
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c69
-rw-r--r--drivers/gpu/drm/exynos/regs-hdmi.h9
-rw-r--r--drivers/gpu/drm/fsl-dcu/Makefile3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c7
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c127
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h2
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c38
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.c111
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.h33
-rw-r--r--drivers/gpu/drm/hisilicon/Kconfig5
-rw-r--r--drivers/gpu/drm/hisilicon/Makefile5
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/Kconfig18
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/Makefile6
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c857
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h103
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h230
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c1057
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c367
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h31
-rw-r--r--drivers/gpu/drm/i915/Kconfig.debug29
-rw-r--r--drivers/gpu/drm/i915/Makefile2
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c104
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c32
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c193
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h126
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c197
-rw-r--r--drivers/gpu/drm/i915/i915_gem.h34
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c228
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c15
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c10
-rw-r--r--drivers/gpu/drm/i915/i915_gem_shrinker.c33
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c14
-rw-r--r--drivers/gpu/drm/i915/i915_gem_userptr.c90
-rw-r--r--drivers/gpu/drm/i915/i915_guc_submission.c120
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c523
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h74
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c32
-rw-r--r--drivers/gpu/drm/i915/intel_csr.c64
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c234
-rw-r--r--drivers/gpu/drm/i915/intel_display.c309
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c86
-rw-r--r--drivers/gpu/drm/i915/intel_dpll_mgr.c17
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h25
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.c135
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.h14
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_pll.c156
-rw-r--r--drivers/gpu/drm/i915/intel_guc.h29
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c12
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c19
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.c176
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.h10
-rw-r--r--drivers/gpu/drm/i915/intel_mocs.c155
-rw-r--r--drivers/gpu/drm/i915/intel_mocs.h2
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c58
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c8
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c4
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c51
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c51
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h1
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.c370
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c500
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig4
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c48
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi.c38
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c17
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi.c20
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c66
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h10
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c95
-rw-r--r--drivers/gpu/drm/sun4i/Kconfig14
-rw-r--r--drivers/gpu/drm/sun4i/Makefile13
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.c364
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.h165
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.c120
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.h30
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_dotclock.c160
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_dotclock.h21
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c358
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.h30
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_framebuffer.c54
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_framebuffer.h19
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_layer.c161
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_layer.h30
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_rgb.c250
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_rgb.h18
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c561
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.h186
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tv.c708
-rw-r--r--drivers/gpu/drm/vc4/Kconfig1
-rw-r--r--drivers/gpu/drm/vc4/Makefile1
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c58
-rw-r--r--drivers/gpu/drm/vc4/vc4_debugfs.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_dpi.c520
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h5
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_regs.h6
-rw-r--r--include/drm/drm_crtc.h1
-rw-r--r--include/drm/drm_fb_cma_helper.h2
-rw-r--r--include/uapi/drm/drm_mode.h2
-rw-r--r--include/uapi/drm/qxl_drm.h1
-rw-r--r--include/uapi/drm/sis_drm.h2
-rw-r--r--include/video/exynos5433_decon.h6
147 files changed, 11795 insertions, 2594 deletions
diff --git a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
index 9f97df4d5152..a5ea451e67fc 100644
--- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
+++ b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
@@ -35,12 +35,22 @@ Optional properties for HDMI:
as an interrupt/status bit in the HDMI controller
itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt
+Required properties for DPI:
+- compatible: Should be "brcm,bcm2835-dpi"
+- reg: Physical base address and length of the registers
+- clocks: a) core: The core clock the unit runs on
+ b) pixel: The pixel clock that feeds the pixelvalve
+- port: Port node with a single endpoint connecting to the panel
+ device, as defined in [1]
+
Required properties for V3D:
- compatible: Should be "brcm,bcm2835-v3d"
- reg: Physical base address and length of the V3D's registers
- interrupts: The interrupt number
See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+[1] Documentation/devicetree/bindings/media/video-interfaces.txt
+
Example:
pixelvalve@7e807000 {
compatible = "brcm,bcm2835-pixelvalve2";
@@ -66,6 +76,22 @@ hdmi: hdmi@7e902000 {
clock-names = "pixel", "hdmi";
};
+dpi: dpi@7e208000 {
+ compatible = "brcm,bcm2835-dpi";
+ reg = <0x7e208000 0x8c>;
+ clocks = <&clocks BCM2835_CLOCK_VPU>,
+ <&clocks BCM2835_CLOCK_DPI>;
+ clock-names = "core", "pixel";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port {
+ dpi_out: endpoint@0 {
+ remote-endpoint = <&panel_in>;
+ };
+ };
+};
+
v3d: v3d@7ec00000 {
compatible = "brcm,bcm2835-v3d";
reg = <0x7ec00000 0x1000>;
@@ -75,3 +101,13 @@ v3d: v3d@7ec00000 {
vc4: gpu {
compatible = "brcm,bcm2835-vc4";
};
+
+panel: panel {
+ compatible = "ontat,yx700wv03", "simple-panel";
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt b/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt
index 377afbf5122a..c9fd7b3807e7 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos5433-decon.txt
@@ -5,7 +5,8 @@ Exynos series of SoCs which transfers the image data from a video memory
buffer to an external LCD interface.
Required properties:
-- compatible: value should be "samsung,exynos5433-decon";
+- compatible: value should be one of:
+ "samsung,exynos5433-decon", "samsung,exynos5433-decon-tv";
- reg: physical base address and length of the DECON registers set.
- interrupts: should contain a list of all DECON IP block interrupts in the
order: VSYNC, LCD_SYSTEM. The interrupt specifier format
@@ -16,7 +17,7 @@ Required properties:
- clocks: must include clock specifiers corresponding to entries in the
clock-names property.
- clock-names: list of clock names sorted in the same order as the clocks
- property. Must contain "aclk_decon", "aclk_smmu_decon0x",
+ property. Must contain "pclk", "aclk_decon", "aclk_smmu_decon0x",
"aclk_xiu_decon0x", "pclk_smmu_decon0x", clk_decon_vclk",
"sclk_decon_eclk"
- ports: contains a port which is connected to mic node. address-cells and
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_hdmi.txt b/Documentation/devicetree/bindings/display/exynos/exynos_hdmi.txt
index d474f59be6d6..a2ec4c1c9382 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos_hdmi.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos_hdmi.txt
@@ -5,6 +5,7 @@ Required properties:
1) "samsung,exynos4210-hdmi"
2) "samsung,exynos4212-hdmi"
3) "samsung,exynos5420-hdmi"
+ 4) "samsung,exynos5433-hdmi"
- reg: physical base address of the hdmi and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
@@ -12,6 +13,11 @@ Required properties:
a) phandle of the gpio controller node.
b) pin number within the gpio controller.
c) optional flags and pull up/down.
+- ddc: phandle to the hdmi ddc node
+- phy: phandle to the hdmi phy node
+- samsung,syscon-phandle: phandle for system controller node for PMU.
+
+Required properties for Exynos 4210, 4212, 5420 and 5433:
- clocks: list of clock IDs from SoC clock driver.
a) hdmi: Gate of HDMI IP bus clock.
b) sclk_hdmi: Gate of HDMI special clock.
@@ -25,9 +31,24 @@ Required properties:
sclk_pixel.
- clock-names: aliases as per driver requirements for above clock IDs:
"hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
-- ddc: phandle to the hdmi ddc node
-- phy: phandle to the hdmi phy node
-- samsung,syscon-phandle: phandle for system controller node for PMU.
+
+Required properties for Exynos 5433:
+- clocks: list of clock specifiers according to common clock bindings.
+ a) hdmi_pclk: Gate of HDMI IP APB bus.
+ b) hdmi_i_pclk: Gate of HDMI-PHY IP APB bus.
+ d) i_tmds_clk: Gate of HDMI TMDS clock.
+ e) i_pixel_clk: Gate of HDMI pixel clock.
+ f) i_spdif_clk: Gate of HDMI SPDIF clock.
+ g) oscclk: Oscillator clock, used as parent of following *_user clocks
+ in case HDMI-PHY is not operational.
+ h) tmds_clko: TMDS clock generated by HDMI-PHY.
+ i) tmds_clko_user: MUX used to switch between oscclk and tmds_clko,
+ respectively if HDMI-PHY is off and operational.
+ j) pixel_clko: Pixel clock generated by HDMI-PHY.
+ k) pixel_clko_user: MUX used to switch between oscclk and pixel_clko,
+ respectively if HDMI-PHY is off and operational.
+- clock-names: aliases for above clock specfiers.
+- samsung,sysreg: handle to syscon used to control the system registers.
Example:
diff --git a/Documentation/devicetree/bindings/display/fsl,dcu.txt b/Documentation/devicetree/bindings/display/fsl,dcu.txt
index ebf1be9ae393..ae55cde1b69e 100644
--- a/Documentation/devicetree/bindings/display/fsl,dcu.txt
+++ b/Documentation/devicetree/bindings/display/fsl,dcu.txt
@@ -6,17 +6,24 @@ Required properties:
* "fsl,vf610-dcu".
- reg: Address and length of the register set for dcu.
-- clocks: From common clock binding: handle to dcu clock.
-- clock-names: From common clock binding: Shall be "dcu".
+- clocks: Handle to "dcu" and "pix" clock (in the order below)
+ This can be the same clock (e.g. LS1021a)
+ See ../clocks/clock-bindings.txt for details.
+- clock-names: Should be "dcu" and "pix"
+ See ../clocks/clock-bindings.txt for details.
- big-endian Boolean property, LS1021A DCU registers are big-endian.
- fsl,panel: The phandle to panel node.
+Optional properties:
+- fsl,tcon: The phandle to the timing controller node.
+
Examples:
dcu: dcu@2ce0000 {
compatible = "fsl,ls1021a-dcu";
reg = <0x0 0x2ce0000 0x0 0x10000>;
- clocks = <&platform_clk 0>;
- clock-names = "dcu";
+ clocks = <&platform_clk 0>, <&platform_clk 0>;
+ clock-names = "dcu", "pix";
big-endian;
fsl,panel = <&panel>;
+ fsl,tcon = <&tcon>;
};
diff --git a/Documentation/devicetree/bindings/display/fsl,tcon.txt b/Documentation/devicetree/bindings/display/fsl,tcon.txt
new file mode 100644
index 000000000000..6fa4ab668db5
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/fsl,tcon.txt
@@ -0,0 +1,18 @@
+Device Tree bindings for Freescale TCON Driver
+
+Required properties:
+- compatible: Should be one of
+ * "fsl,vf610-tcon".
+
+- reg: Address and length of the register set for tcon.
+- clocks: From common clock binding: handle to tcon ipg clock.
+- clock-names: From common clock binding: Shall be "ipg".
+
+Examples:
+timing-controller@4003d000 {
+ compatible = "fsl,vf610-tcon";
+ reg = <0x4003d000 0x1000>;
+ clocks = <&clks VF610_CLK_TCON0>;
+ clock-names = "ipg";
+ status = "okay";
+};
diff --git a/Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt b/Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt
new file mode 100644
index 000000000000..d270bfe4e4e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt
@@ -0,0 +1,72 @@
+Device-Tree bindings for DesignWare DSI Host Controller v1.20a driver
+
+A DSI Host Controller resides in the middle of display controller and external
+HDMI converter or panel.
+
+Required properties:
+- compatible: value should be "hisilicon,hi6220-dsi".
+- reg: physical base address and length of dsi controller's registers.
+- clocks: contains APB clock phandle + clock-specifier pair.
+- clock-names: should be "pclk".
+- ports: contains DSI controller input and output sub port.
+ The input port connects to ADE output port with the reg value "0".
+ The output port with the reg value "1", it could connect to panel or
+ any other bridge endpoints.
+ See Documentation/devicetree/bindings/graph.txt for more device graph info.
+
+A example of HiKey board hi6220 SoC and board specific DT entry:
+Example:
+
+SoC specific:
+ dsi: dsi@f4107800 {
+ compatible = "hisilicon,hi6220-dsi";
+ reg = <0x0 0xf4107800 0x0 0x100>;
+ clocks = <&media_ctrl HI6220_DSI_PCLK>;
+ clock-names = "pclk";
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* 0 for input port */
+ port@0 {
+ reg = <0>;
+ dsi_in: endpoint {
+ remote-endpoint = <&ade_out>;
+ };
+ };
+ };
+ };
+
+
+Board specific:
+ &dsi {
+ status = "ok";
+
+ ports {
+ /* 1 for output port */
+ port@1 {
+ reg = <1>;
+
+ dsi_out0: endpoint@0 {
+ remote-endpoint = <&adv7533_in>;
+ };
+ };
+ };
+ };
+
+ &i2c2 {
+ ...
+
+ adv7533: adv7533@39 {
+ ...
+
+ port {
+ adv7533_in: endpoint {
+ remote-endpoint = <&dsi_out0>;
+ };
+ };
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt b/Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt
new file mode 100644
index 000000000000..38dc9d60eef8
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt
@@ -0,0 +1,64 @@
+Device-Tree bindings for hisilicon ADE display controller driver
+
+ADE (Advanced Display Engine) is the display controller which grab image
+data from memory, do composition, do post image processing, generate RGB
+timing stream and transfer to DSI.
+
+Required properties:
+- compatible: value should be "hisilicon,hi6220-ade".
+- reg: physical base address and length of the ADE controller's registers.
+- hisilicon,noc-syscon: ADE NOC QoS syscon.
+- resets: The ADE reset controller node.
+- interrupt: the ldi vblank interrupt number used.
+- clocks: a list of phandle + clock-specifier pairs, one for each entry
+ in clock-names.
+- clock-names: should contain:
+ "clk_ade_core" for the ADE core clock.
+ "clk_codec_jpeg" for the media NOC QoS clock, which use the same clock with
+ jpeg codec.
+ "clk_ade_pix" for the ADE pixel clok.
+- assigned-clocks: Should contain "clk_ade_core" and "clk_codec_jpeg" clocks'
+ phandle + clock-specifier pairs.
+- assigned-clock-rates: clock rates, one for each entry in assigned-clocks.
+ The rate of "clk_ade_core" could be "360000000" or "180000000";
+ The rate of "clk_codec_jpeg" could be or less than "1440000000".
+ These rate values could be configured according to performance and power
+ consumption.
+- port: the output port. This contains one endpoint subnode, with its
+ remote-endpoint set to the phandle of the connected DSI input endpoint.
+ See Documentation/devicetree/bindings/graph.txt for more device graph info.
+
+Optional properties:
+- dma-coherent: Present if dma operations are coherent.
+
+
+A example of HiKey board hi6220 SoC specific DT entry:
+Example:
+
+ ade: ade@f4100000 {
+ compatible = "hisilicon,hi6220-ade";
+ reg = <0x0 0xf4100000 0x0 0x7800>;
+ reg-names = "ade_base";
+ hisilicon,noc-syscon = <&medianoc_ade>;
+ resets = <&media_ctrl MEDIA_ADE>;
+ interrupts = <0 115 4>; /* ldi interrupt */
+
+ clocks = <&media_ctrl HI6220_ADE_CORE>,
+ <&media_ctrl HI6220_CODEC_JPEG>,
+ <&media_ctrl HI6220_ADE_PIX_SRC>;
+ /*clock name*/
+ clock-names = "clk_ade_core",
+ "clk_codec_jpeg",
+ "clk_ade_pix";
+
+ assigned-clocks = <&media_ctrl HI6220_ADE_CORE>,
+ <&media_ctrl HI6220_CODEC_JPEG>;
+ assigned-clock-rates = <360000000>, <288000000>;
+ dma-coherent;
+
+ port {
+ ade_out: endpoint {
+ remote-endpoint = <&dsi_in>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/display/snps,arcpgu.txt b/Documentation/devicetree/bindings/display/snps,arcpgu.txt
new file mode 100644
index 000000000000..c5c7dfd37df2
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/snps,arcpgu.txt
@@ -0,0 +1,35 @@
+ARC PGU
+
+This is a display controller found on several development boards produced
+by Synopsys. The ARC PGU is an RGB streamer that reads the data from a
+framebuffer and sends it to a single digital encoder (usually HDMI).
+
+Required properties:
+ - compatible: "snps,arcpgu"
+ - reg: Physical base address and length of the controller's registers.
+ - clocks: A list of phandle + clock-specifier pairs, one for each
+ entry in 'clock-names'.
+ - clock-names: A list of clock names. For ARC PGU it should contain:
+ - "pxlclk" for the clock feeding the output PLL of the controller.
+
+Required sub-nodes:
+ - port: The PGU connection to an encoder chip.
+
+Example:
+
+/ {
+ ...
+
+ pgu@XXXXXXXX {
+ compatible = "snps,arcpgu";
+ reg = <0xXXXXXXXX 0x400>;
+ clocks = <&clock_node>;
+ clock-names = "pxlclk";
+
+ port {
+ pgu_output: endpoint {
+ remote-endpoint = <&hdmi_enc_input>;
+ };
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
new file mode 100644
index 000000000000..df8f4aeefe4c
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -0,0 +1,258 @@
+Allwinner A10 Display Pipeline
+==============================
+
+The Allwinner A10 Display pipeline is composed of several components
+that are going to be documented below:
+
+TV Encoder
+----------
+
+The TV Encoder supports the composite and VGA output. It is one end of
+the pipeline.
+
+Required properties:
+ - compatible: value should be "allwinner,sun4i-a10-tv-encoder".
+ - reg: base address and size of memory-mapped region
+ - clocks: the clocks driving the TV encoder
+ - resets: phandle to the reset controller driving the encoder
+
+- ports: A ports node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt. The
+ first port should be the input endpoint.
+
+TCON
+----
+
+The TCON acts as a timing controller for RGB, LVDS and TV interfaces.
+
+Required properties:
+ - compatible: value should be "allwinner,sun5i-a13-tcon".
+ - reg: base address and size of memory-mapped region
+ - interrupts: interrupt associated to this IP
+ - clocks: phandles to the clocks feeding the TCON. Three are needed:
+ - 'ahb': the interface clocks
+ - 'tcon-ch0': The clock driving the TCON channel 0
+ - 'tcon-ch1': The clock driving the TCON channel 1
+ - resets: phandles to the reset controllers driving the encoder
+ - "lcd": the reset line for the TCON channel 0
+
+ - clock-names: the clock names mentioned above
+ - reset-names: the reset names mentioned above
+ - clock-output-names: Name of the pixel clock created
+
+- ports: A ports node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt. The
+ first port should be the input endpoint, the second one the output
+
+ The output should have two endpoints. The first is the block
+ connected to the TCON channel 0 (usually a panel or a bridge), the
+ second the block connected to the TCON channel 1 (usually the TV
+ encoder)
+
+
+Display Engine Backend
+----------------------
+
+The display engine backend exposes layers and sprites to the
+system.
+
+Required properties:
+ - compatible: value must be one of:
+ * allwinner,sun5i-a13-display-backend
+ - reg: base address and size of the memory-mapped region.
+ - clocks: phandles to the clocks feeding the frontend and backend
+ * ahb: the backend interface clock
+ * mod: the backend module clock
+ * ram: the backend DRAM clock
+ - clock-names: the clock names mentioned above
+ - resets: phandles to the reset controllers driving the backend
+
+- ports: A ports node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt. The
+ first port should be the input endpoints, the second one the output
+
+Display Engine Frontend
+-----------------------
+
+The display engine frontend does formats conversion, scaling,
+deinterlacing and color space conversion.
+
+Required properties:
+ - compatible: value must be one of:
+ * allwinner,sun5i-a13-display-frontend
+ - reg: base address and size of the memory-mapped region.
+ - interrupts: interrupt associated to this IP
+ - clocks: phandles to the clocks feeding the frontend and backend
+ * ahb: the backend interface clock
+ * mod: the backend module clock
+ * ram: the backend DRAM clock
+ - clock-names: the clock names mentioned above
+ - resets: phandles to the reset controllers driving the backend
+
+- ports: A ports node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt. The
+ first port should be the input endpoints, the second one the outputs
+
+
+Display Engine Pipeline
+-----------------------
+
+The display engine pipeline (and its entry point, since it can be
+either directly the backend or the frontend) is represented as an
+extra node.
+
+Required properties:
+ - compatible: value must be one of:
+ * allwinner,sun5i-a13-display-engine
+
+ - allwinner,pipelines: list of phandle to the display engine
+ frontends available.
+
+Example:
+
+panel: panel {
+ compatible = "olimex,lcd-olinuxino-43-ts";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel_input: endpoint {
+ remote-endpoint = <&tcon0_out_panel>;
+ };
+ };
+};
+
+tve0: tv-encoder@01c0a000 {
+ compatible = "allwinner,sun4i-a10-tv-encoder";
+ reg = <0x01c0a000 0x1000>;
+ clocks = <&ahb_gates 34>;
+ resets = <&tcon_ch0_clk 0>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tve0_in_tcon0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_out_tve0>;
+ };
+ };
+};
+
+tcon0: lcd-controller@1c0c000 {
+ compatible = "allwinner,sun5i-a13-tcon";
+ reg = <0x01c0c000 0x1000>;
+ interrupts = <44>;
+ resets = <&tcon_ch0_clk 1>;
+ reset-names = "lcd";
+ clocks = <&ahb_gates 36>,
+ <&tcon_ch0_clk>,
+ <&tcon_ch1_clk>;
+ clock-names = "ahb",
+ "tcon-ch0",
+ "tcon-ch1";
+ clock-output-names = "tcon-pixel-clock";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tcon0_in: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ tcon0_in_be0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&be0_out_tcon0>;
+ };
+ };
+
+ tcon0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ tcon0_out_panel: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&panel_input>;
+ };
+
+ tcon0_out_tve0: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&tve0_in_tcon0>;
+ };
+ };
+ };
+};
+
+fe0: display-frontend@1e00000 {
+ compatible = "allwinner,sun5i-a13-display-frontend";
+ reg = <0x01e00000 0x20000>;
+ interrupts = <47>;
+ clocks = <&ahb_gates 46>, <&de_fe_clk>,
+ <&dram_gates 25>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&de_fe_clk>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fe0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ fe0_out_be0: endpoint {
+ remote-endpoint = <&be0_in_fe0>;
+ };
+ };
+ };
+};
+
+be0: display-backend@1e60000 {
+ compatible = "allwinner,sun5i-a13-display-backend";
+ reg = <0x01e60000 0x10000>;
+ clocks = <&ahb_gates 44>, <&de_be_clk>,
+ <&dram_gates 26>;
+ clock-names = "ahb", "mod",
+ "ram";
+ resets = <&de_be_clk>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ be0_in: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ be0_in_fe0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&fe0_out_be0>;
+ };
+ };
+
+ be0_out: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ be0_out_tcon0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&tcon0_in_be0>;
+ };
+ };
+ };
+};
+
+display-engine {
+ compatible = "allwinner,sun5i-a13-display-engine";
+ allwinner,pipelines = <&fe0>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 1bbf5ece1dd4..8c10b4cc4da7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -847,6 +847,12 @@ S: Maintained
F: drivers/net/arcnet/
F: include/uapi/linux/if_arcnet.h
+ARC PGU DRM DRIVER
+M: Alexey Brodkin <abrodkin@synopsys.com>
+S: Supported
+F: drivers/gpu/drm/arc/
+F: Documentation/devicetree/bindings/display/snps,arcpgu.txt
+
ARM HDLCD DRM DRIVER
M: Liviu Dudau <liviu.dudau@arm.com>
S: Supported
@@ -3818,6 +3824,13 @@ S: Supported
F: drivers/gpu/drm/atmel-hlcdc/
F: Documentation/devicetree/bindings/drm/atmel/
+DRM DRIVERS FOR ALLWINNER A10
+M: Maxime Ripard <maxime.ripard@free-electrons.com>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+F: drivers/gpu/drm/sun4i/
+F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+
DRM DRIVERS FOR EXYNOS
M: Inki Dae <inki.dae@samsung.com>
M: Joonyoung Shim <jy0922.shim@samsung.com>
@@ -3837,6 +3850,7 @@ L: dri-devel@lists.freedesktop.org
S: Supported
F: drivers/gpu/drm/fsl-dcu/
F: Documentation/devicetree/bindings/display/fsl,dcu.txt
+F: Documentation/devicetree/bindings/display/fsl,tcon.txt
F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt
DRM DRIVERS FOR FREESCALE IMX
@@ -3854,6 +3868,16 @@ T: git git://github.com/patjak/drm-gma500
S: Maintained
F: drivers/gpu/drm/gma500/
+DRM DRIVERS FOR HISILICON
+M: Xinliang Liu <z.liuxinliang@hisilicon.com>
+R: Xinwei Kong <kong.kongxinwei@hisilicon.com>
+R: Chen Feng <puck.chen@hisilicon.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://github.com/xin3liang/linux.git
+S: Maintained
+F: drivers/gpu/drm/hisilicon/
+F: Documentation/devicetree/bindings/display/hisilicon/
+
DRM DRIVER FOR INTEL I810 VIDEO CARDS
S: Orphan / Obsolete
F: drivers/gpu/drm/i810/
diff --git a/arch/arc/boot/dts/axs10x_mb.dtsi b/arch/arc/boot/dts/axs10x_mb.dtsi
index ab5d5701e11d..823f15ca68df 100644
--- a/arch/arc/boot/dts/axs10x_mb.dtsi
+++ b/arch/arc/boot/dts/axs10x_mb.dtsi
@@ -34,6 +34,12 @@
clock-frequency = <50000000>;
#clock-cells = <0>;
};
+
+ pguclk: pguclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <74440000>;
+ };
};
ethernet@0x18000 {
@@ -155,6 +161,37 @@
clocks = <&i2cclk>;
interrupts = <16>;
+ adv7511:adv7511@39{
+ compatible="adi,adv7511";
+ reg = <0x39>;
+ interrupts = <23>;
+ adi,input-depth = <8>;
+ adi,input-colorspace = "rgb";
+ adi,input-clock = "1x";
+ adi,clock-delay = <0x03>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* RGB/YUV input */
+ port@0 {
+ reg = <0>;
+ adv7511_input:endpoint {
+ remote-endpoint = <&pgu_output>;
+ };
+ };
+
+ /* HDMI output */
+ port@1 {
+ reg = <1>;
+ adv7511_output: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+ };
+ };
+
eeprom@0x54{
compatible = "24c01";
reg = <0x54>;
@@ -168,6 +205,16 @@
};
};
+ hdmi0: connector {
+ compatible = "hdmi-connector";
+ type = "a";
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&adv7511_output>;
+ };
+ };
+ };
+
gpio0:gpio@13000 {
compatible = "snps,dw-apb-gpio";
reg = <0x13000 0x1000>;
@@ -229,5 +276,19 @@
reg = <2>;
};
};
+
+ pgu@17000 {
+ compatible = "snps,arcpgu";
+ reg = <0x17000 0x400>;
+ encoder-slave = <&adv7511>;
+ clocks = <&pguclk>;
+ clock-names = "pxlclk";
+
+ port {
+ pgu_output: endpoint {
+ remote-endpoint = <&adv7511_input>;
+ };
+ };
+ };
};
};
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 414c79bf63a7..16e4c2135aad 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -253,6 +253,8 @@ source "drivers/gpu/drm/rcar-du/Kconfig"
source "drivers/gpu/drm/shmobile/Kconfig"
+source "drivers/gpu/drm/sun4i/Kconfig"
+
source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
@@ -282,3 +284,7 @@ source "drivers/gpu/drm/imx/Kconfig"
source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig"
+
+source "drivers/gpu/drm/arc/Kconfig"
+
+source "drivers/gpu/drm/hisilicon/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 6eb94fc561dc..43c2abf425ee 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -1,4 +1,4 @@
-#
+
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
@@ -65,6 +65,7 @@ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-y += omapdrm/
+obj-$(CONFIG_DRM_SUN4I) += sun4i/
obj-y += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
@@ -78,3 +79,5 @@ obj-y += panel/
obj-y += bridge/
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
+obj-$(CONFIG_DRM_ARCPGU)+= arc/
+obj-y += hisilicon/
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 07ac724e3ec9..ee3e04e10dae 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -109,7 +109,7 @@ static int kfd_open(struct inode *inode, struct file *filep)
is_32bit_user_mode = in_compat_syscall();
- if (is_32bit_user_mode == true) {
+ if (is_32bit_user_mode) {
dev_warn(kfd_device,
"Process %d (32-bit) failed to open /dev/kfd\n"
"32-bit processes are not supported by amdkfd\n",
@@ -131,12 +131,11 @@ static int kfd_ioctl_get_version(struct file *filep, struct kfd_process *p,
void *data)
{
struct kfd_ioctl_get_version_args *args = data;
- int err = 0;
args->major_version = KFD_IOCTL_MAJOR_VERSION;
args->minor_version = KFD_IOCTL_MINOR_VERSION;
- return err;
+ return 0;
}
static int set_queue_properties_from_user(struct queue_properties *q_properties,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 4bb7f4223762..f49c551195b3 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -216,7 +216,7 @@ static int allocate_hqd(struct device_queue_manager *dqm, struct queue *q)
}
}
- if (set == false)
+ if (!set)
return -EBUSY;
pr_debug("kfd: DQM %s hqd slot - pipe (%d) queue(%d)\n",
@@ -354,7 +354,7 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q)
return -ENOMEM;
}
- if (q->properties.is_active == true)
+ if (q->properties.is_active)
prev_active = true;
/*
@@ -363,9 +363,9 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q)
* and modify counter accordingly
*/
retval = mqd->update_mqd(mqd, q->mqd, &q->properties);
- if ((q->properties.is_active == true) && (prev_active == false))
+ if ((q->properties.is_active) && (!prev_active))
dqm->queue_count++;
- else if ((q->properties.is_active == false) && (prev_active == true))
+ else if ((!q->properties.is_active) && (prev_active))
dqm->queue_count--;
if (sched_policy != KFD_SCHED_POLICY_NO_HWS)
@@ -954,7 +954,7 @@ static int destroy_queues_cpsch(struct device_queue_manager *dqm,
if (lock)
mutex_lock(&dqm->lock);
- if (dqm->active_runlist == false)
+ if (!dqm->active_runlist)
goto out;
pr_debug("kfd: Before destroying queues, sdma queue count is : %u\n",
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index b6e28dcaea1d..a6a4b2b1c0d9 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -177,9 +177,9 @@ static bool allocate_event_notification_slot(struct file *devkfd,
bool ret;
ret = allocate_free_slot(p, page, signal_slot_index);
- if (ret == false) {
+ if (!ret) {
ret = allocate_signal_page(devkfd, p);
- if (ret == true)
+ if (ret)
ret = allocate_free_slot(p, page, signal_slot_index);
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
index 8fa894100290..9beae87aadd5 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
@@ -300,7 +300,7 @@ struct kernel_queue *kernel_queue_init(struct kfd_dev *dev,
break;
}
- if (kq->ops.initialize(kq, dev, type, KFD_KERNEL_QUEUE_SIZE) == false) {
+ if (!kq->ops.initialize(kq, dev, type, KFD_KERNEL_QUEUE_SIZE)) {
pr_err("amdkfd: failed to init kernel queue\n");
kfree(kq);
return NULL;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c
index 90f391434fa3..ca8c09326b31 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c
@@ -98,7 +98,7 @@ static int pm_allocate_runlist_ib(struct packet_manager *pm,
int retval;
BUG_ON(!pm);
- BUG_ON(pm->allocated == true);
+ BUG_ON(pm->allocated);
BUG_ON(is_over_subscription == NULL);
pm_calc_rlib_size(pm, rl_buffer_size, is_over_subscription);
@@ -292,7 +292,7 @@ static int pm_create_map_queue(struct packet_manager *pm, uint32_t *buffer,
q->properties.doorbell_off;
packet->mes_map_queues_ordinals[0].bitfields3.is_static =
- (use_static == true) ? 1 : 0;
+ (use_static) ? 1 : 0;
packet->mes_map_queues_ordinals[0].mqd_addr_lo =
lower_32_bits(q->gart_mqd_addr);
@@ -357,7 +357,7 @@ static int pm_create_runlist_ib(struct packet_manager *pm,
alloc_size_bytes);
list_for_each_entry(kq, &qpd->priv_queue_list, list) {
- if (kq->queue->properties.is_active != true)
+ if (!kq->queue->properties.is_active)
continue;
pr_debug("kfd: static_queue, mapping kernel q %d, is debug status %d\n",
@@ -383,7 +383,7 @@ static int pm_create_runlist_ib(struct packet_manager *pm,
}
list_for_each_entry(q, &qpd->queues_list, list) {
- if (q->properties.is_active != true)
+ if (!q->properties.is_active)
continue;
pr_debug("kfd: static_queue, mapping user queue %d, is debug status %d\n",
@@ -531,7 +531,7 @@ fail_create_runlist:
fail_acquire_packet_buffer:
mutex_unlock(&pm->lock);
fail_create_runlist_ib:
- if (pm->allocated == true)
+ if (pm->allocated)
pm_release_ib(pm);
return retval;
}
@@ -647,7 +647,7 @@ int pm_send_unmap_queue(struct packet_manager *pm, enum kfd_queue_type type,
default:
BUG();
break;
- };
+ }
pm->priv_queue->ops.submit_packet(pm->priv_queue);
diff --git a/drivers/gpu/drm/arc/Kconfig b/drivers/gpu/drm/arc/Kconfig
new file mode 100644
index 000000000000..f9a13b658fea
--- /dev/null
+++ b/drivers/gpu/drm/arc/Kconfig
@@ -0,0 +1,10 @@
+config DRM_ARCPGU
+ tristate "ARC PGU"
+ depends on DRM && OF
+ select DRM_KMS_CMA_HELPER
+ select DRM_KMS_FB_HELPER
+ select DRM_KMS_HELPER
+ help
+ Choose this option if you have an ARC PGU controller.
+
+ If M is selected the module will be called arcpgu.
diff --git a/drivers/gpu/drm/arc/Makefile b/drivers/gpu/drm/arc/Makefile
new file mode 100644
index 000000000000..d48fda70f857
--- /dev/null
+++ b/drivers/gpu/drm/arc/Makefile
@@ -0,0 +1,2 @@
+arcpgu-y := arcpgu_crtc.o arcpgu_hdmi.o arcpgu_drv.o
+obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
diff --git a/drivers/gpu/drm/arc/arcpgu.h b/drivers/gpu/drm/arc/arcpgu.h
new file mode 100644
index 000000000000..86574b698a78
--- /dev/null
+++ b/drivers/gpu/drm/arc/arcpgu.h
@@ -0,0 +1,50 @@
+/*
+ * ARC PGU DRM driver.
+ *
+ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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.
+ *
+ */
+
+#ifndef _ARCPGU_H_
+#define _ARCPGU_H_
+
+struct arcpgu_drm_private {
+ void __iomem *regs;
+ struct clk *clk;
+ struct drm_fbdev_cma *fbdev;
+ struct drm_framebuffer *fb;
+ struct list_head event_list;
+ struct drm_crtc crtc;
+ struct drm_plane *plane;
+};
+
+#define crtc_to_arcpgu_priv(x) container_of(x, struct arcpgu_drm_private, crtc)
+
+static inline void arc_pgu_write(struct arcpgu_drm_private *arcpgu,
+ unsigned int reg, u32 value)
+{
+ iowrite32(value, arcpgu->regs + reg);
+}
+
+static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu,
+ unsigned int reg)
+{
+ return ioread32(arcpgu->regs + reg);
+}
+
+int arc_pgu_setup_crtc(struct drm_device *dev);
+int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np);
+struct drm_fbdev_cma *arcpgu_fbdev_cma_init(struct drm_device *dev,
+ unsigned int preferred_bpp, unsigned int num_crtc,
+ unsigned int max_conn_count);
+
+#endif
diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c
new file mode 100644
index 000000000000..92f8beff8e60
--- /dev/null
+++ b/drivers/gpu/drm/arc/arcpgu_crtc.c
@@ -0,0 +1,257 @@
+/*
+ * ARC PGU DRM driver.
+ *
+ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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 <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <linux/clk.h>
+#include <linux/platform_data/simplefb.h>
+
+#include "arcpgu.h"
+#include "arcpgu_regs.h"
+
+#define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1))
+
+static struct simplefb_format supported_formats[] = {
+ { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 },
+ { "r8g8b8", 24, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_RGB888 },
+};
+
+static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc)
+{
+ struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
+ uint32_t pixel_format = crtc->primary->state->fb->pixel_format;
+ struct simplefb_format *format = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
+ if (supported_formats[i].fourcc == pixel_format)
+ format = &supported_formats[i];
+ }
+
+ if (WARN_ON(!format))
+ return;
+
+ if (format->fourcc == DRM_FORMAT_RGB888)
+ arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
+ arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
+ ARCPGU_MODE_RGB888_MASK);
+
+}
+
+static const struct drm_crtc_funcs arc_pgu_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
+ struct drm_display_mode *m = &crtc->state->adjusted_mode;
+ u32 val;
+
+ arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
+ ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal));
+
+ arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC,
+ ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay,
+ m->crtc_hsync_end - m->crtc_hdisplay));
+
+ arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC,
+ ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay,
+ m->crtc_vsync_end - m->crtc_vdisplay));
+
+ arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE,
+ ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start,
+ m->crtc_vblank_end - m->crtc_vblank_start));
+
+ val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
+
+ if (m->flags & DRM_MODE_FLAG_PVSYNC)
+ val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST;
+ else
+ val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST);
+
+ if (m->flags & DRM_MODE_FLAG_PHSYNC)
+ val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST;
+ else
+ val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST);
+
+ arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val);
+ arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0);
+ arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1);
+
+ arc_pgu_set_pxl_fmt(crtc);
+
+ clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
+}
+
+static void arc_pgu_crtc_enable(struct drm_crtc *crtc)
+{
+ struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
+
+ clk_prepare_enable(arcpgu->clk);
+ arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
+ arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
+ ARCPGU_CTRL_ENABLE_MASK);
+}
+
+static void arc_pgu_crtc_disable(struct drm_crtc *crtc)
+{
+ struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
+
+ if (!crtc->primary->fb)
+ return;
+
+ clk_disable_unprepare(arcpgu->clk);
+ arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
+ arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) &
+ ~ARCPGU_CTRL_ENABLE_MASK);
+}
+
+static int arc_pgu_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
+ struct drm_display_mode *mode = &state->adjusted_mode;
+ long rate, clk_rate = mode->clock * 1000;
+
+ rate = clk_round_rate(arcpgu->clk, clk_rate);
+ if (rate != clk_rate)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
+ unsigned long flags;
+
+ if (crtc->state->event) {
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ crtc->state->event = NULL;
+ event->pipe = drm_crtc_index(crtc);
+
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ list_add_tail(&event->base.link, &arcpgu->event_list);
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+ }
+}
+
+static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = {
+ .mode_set = drm_helper_crtc_mode_set,
+ .mode_set_base = drm_helper_crtc_mode_set_base,
+ .mode_set_nofb = arc_pgu_crtc_mode_set_nofb,
+ .enable = arc_pgu_crtc_enable,
+ .disable = arc_pgu_crtc_disable,
+ .prepare = arc_pgu_crtc_disable,
+ .commit = arc_pgu_crtc_enable,
+ .atomic_check = arc_pgu_crtc_atomic_check,
+ .atomic_begin = arc_pgu_crtc_atomic_begin,
+};
+
+static void arc_pgu_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct arcpgu_drm_private *arcpgu;
+ struct drm_gem_cma_object *gem;
+
+ if (!plane->state->crtc || !plane->state->fb)
+ return;
+
+ arcpgu = crtc_to_arcpgu_priv(plane->state->crtc);
+ gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
+ arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr);
+}
+
+static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = {
+ .prepare_fb = NULL,
+ .cleanup_fb = NULL,
+ .atomic_update = arc_pgu_plane_atomic_update,
+};
+
+static void arc_pgu_plane_destroy(struct drm_plane *plane)
+{
+ drm_plane_helper_disable(plane);
+ drm_plane_cleanup(plane);
+}
+
+static const struct drm_plane_funcs arc_pgu_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = arc_pgu_plane_destroy,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm)
+{
+ struct arcpgu_drm_private *arcpgu = drm->dev_private;
+ struct drm_plane *plane = NULL;
+ u32 formats[ARRAY_SIZE(supported_formats)], i;
+ int ret;
+
+ plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < ARRAY_SIZE(supported_formats); i++)
+ formats[i] = supported_formats[i].fourcc;
+
+ ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs,
+ formats, ARRAY_SIZE(formats),
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs);
+ arcpgu->plane = plane;
+
+ return plane;
+}
+
+int arc_pgu_setup_crtc(struct drm_device *drm)
+{
+ struct arcpgu_drm_private *arcpgu = drm->dev_private;
+ struct drm_plane *primary;
+ int ret;
+
+ primary = arc_pgu_plane_init(drm);
+ if (IS_ERR(primary))
+ return PTR_ERR(primary);
+
+ ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL,
+ &arc_pgu_crtc_funcs, NULL);
+ if (ret) {
+ arc_pgu_plane_destroy(primary);
+ return ret;
+ }
+
+ drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs);
+ return 0;
+}
diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c
new file mode 100644
index 000000000000..5b35e5dbae38
--- /dev/null
+++ b/drivers/gpu/drm/arc/arcpgu_drv.c
@@ -0,0 +1,282 @@
+/*
+ * ARC PGU DRM driver.
+ *
+ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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/clk.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "arcpgu.h"
+#include "arcpgu_regs.h"
+
+static void arcpgu_fb_output_poll_changed(struct drm_device *dev)
+{
+ struct arcpgu_drm_private *arcpgu = dev->dev_private;
+
+ if (arcpgu->fbdev)
+ drm_fbdev_cma_hotplug_event(arcpgu->fbdev);
+}
+
+static int arcpgu_atomic_commit(struct drm_device *dev,
+ struct drm_atomic_state *state, bool async)
+{
+ return drm_atomic_helper_commit(dev, state, false);
+}
+
+static struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .output_poll_changed = arcpgu_fb_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = arcpgu_atomic_commit,
+};
+
+static void arcpgu_setup_mode_config(struct drm_device *drm)
+{
+ drm_mode_config_init(drm);
+ drm->mode_config.min_width = 0;
+ drm->mode_config.min_height = 0;
+ drm->mode_config.max_width = 1920;
+ drm->mode_config.max_height = 1080;
+ drm->mode_config.funcs = &arcpgu_drm_modecfg_funcs;
+}
+
+int arcpgu_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
+ return 0;
+}
+
+static const struct file_operations arcpgu_drm_ops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = arcpgu_gem_mmap,
+};
+
+static void arcpgu_preclose(struct drm_device *drm, struct drm_file *file)
+{
+ struct arcpgu_drm_private *arcpgu = drm->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drm->event_lock, flags);
+ list_for_each_entry_safe(e, t, &arcpgu->event_list, base.link) {
+ if (e->base.file_priv != file)
+ continue;
+ list_del(&e->base.link);
+ e->base.destroy(&e->base);
+ }
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+}
+
+static void arcpgu_lastclose(struct drm_device *drm)
+{
+ struct arcpgu_drm_private *arcpgu = drm->dev_private;
+
+ drm_fbdev_cma_restore_mode(arcpgu->fbdev);
+}
+
+static int arcpgu_load(struct drm_device *drm)
+{
+ struct platform_device *pdev = to_platform_device(drm->dev);
+ struct arcpgu_drm_private *arcpgu;
+ struct device_node *encoder_node;
+ struct resource *res;
+ int ret;
+
+ arcpgu = devm_kzalloc(&pdev->dev, sizeof(*arcpgu), GFP_KERNEL);
+ if (arcpgu == NULL)
+ return -ENOMEM;
+
+ drm->dev_private = arcpgu;
+
+ arcpgu->clk = devm_clk_get(drm->dev, "pxlclk");
+ if (IS_ERR(arcpgu->clk))
+ return PTR_ERR(arcpgu->clk);
+
+ INIT_LIST_HEAD(&arcpgu->event_list);
+
+ arcpgu_setup_mode_config(drm);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ arcpgu->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(arcpgu->regs)) {
+ dev_err(drm->dev, "Could not remap IO mem\n");
+ return PTR_ERR(arcpgu->regs);
+ }
+
+ dev_info(drm->dev, "arc_pgu ID: 0x%x\n",
+ arc_pgu_read(arcpgu, ARCPGU_REG_ID));
+
+ if (dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)))
+ return -ENODEV;
+
+ if (arc_pgu_setup_crtc(drm) < 0)
+ return -ENODEV;
+
+ /* find the encoder node and initialize it */
+ encoder_node = of_parse_phandle(drm->dev->of_node, "encoder-slave", 0);
+ if (!encoder_node) {
+ dev_err(drm->dev, "failed to get an encoder slave node\n");
+ return -ENODEV;
+ }
+
+ ret = arcpgu_drm_hdmi_init(drm, encoder_node);
+ if (ret < 0)
+ return ret;
+
+ drm_mode_config_reset(drm);
+ drm_kms_helper_poll_init(drm);
+
+ arcpgu->fbdev = drm_fbdev_cma_init(drm, 16,
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+ if (IS_ERR(arcpgu->fbdev)) {
+ ret = PTR_ERR(arcpgu->fbdev);
+ arcpgu->fbdev = NULL;
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, arcpgu);
+ return 0;
+}
+
+int arcpgu_unload(struct drm_device *drm)
+{
+ struct arcpgu_drm_private *arcpgu = drm->dev_private;
+
+ if (arcpgu->fbdev) {
+ drm_fbdev_cma_fini(arcpgu->fbdev);
+ arcpgu->fbdev = NULL;
+ }
+ drm_kms_helper_poll_fini(drm);
+ drm_vblank_cleanup(drm);
+ drm_mode_config_cleanup(drm);
+
+ return 0;
+}
+
+static struct drm_driver arcpgu_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
+ DRIVER_ATOMIC,
+ .preclose = arcpgu_preclose,
+ .lastclose = arcpgu_lastclose,
+ .name = "drm-arcpgu",
+ .desc = "ARC PGU Controller",
+ .date = "20160219",
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+ .fops = &arcpgu_drm_ops,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+};
+
+static int arcpgu_probe(struct platform_device *pdev)
+{
+ struct drm_device *drm;
+ int ret;
+
+ drm = drm_dev_alloc(&arcpgu_drm_driver, &pdev->dev);
+ if (!drm)
+ return -ENOMEM;
+
+ ret = arcpgu_load(drm);
+ if (ret)
+ goto err_unref;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto err_unload;
+
+ ret = drm_connector_register_all(drm);
+ if (ret)
+ goto err_unregister;
+
+ return 0;
+
+err_unregister:
+ drm_dev_unregister(drm);
+
+err_unload:
+ arcpgu_unload(drm);
+
+err_unref:
+ drm_dev_unref(drm);
+
+ return ret;
+}
+
+static int arcpgu_remove(struct platform_device *pdev)
+{
+ struct drm_device *drm = platform_get_drvdata(pdev);
+
+ drm_connector_unregister_all(drm);
+ drm_dev_unregister(drm);
+ arcpgu_unload(drm);
+ drm_dev_unref(drm);
+
+ return 0;
+}
+
+static const struct of_device_id arcpgu_of_table[] = {
+ {.compatible = "snps,arcpgu"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, arcpgu_of_table);
+
+static struct platform_driver arcpgu_platform_driver = {
+ .probe = arcpgu_probe,
+ .remove = arcpgu_remove,
+ .driver = {
+ .name = "arcpgu",
+ .of_match_table = arcpgu_of_table,
+ },
+};
+
+module_platform_driver(arcpgu_platform_driver);
+
+MODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>");
+MODULE_DESCRIPTION("ARC PGU DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
new file mode 100644
index 000000000000..08b6baeb320d
--- /dev/null
+++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
@@ -0,0 +1,201 @@
+/*
+ * ARC PGU DRM driver.
+ *
+ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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 <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "arcpgu.h"
+
+struct arcpgu_drm_connector {
+ struct drm_connector connector;
+ struct drm_encoder_slave *encoder_slave;
+};
+
+static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
+{
+ const struct drm_encoder_slave_funcs *sfuncs;
+ struct drm_encoder_slave *slave;
+ struct arcpgu_drm_connector *con =
+ container_of(connector, struct arcpgu_drm_connector, connector);
+
+ slave = con->encoder_slave;
+ if (slave == NULL) {
+ dev_err(connector->dev->dev,
+ "connector_get_modes: cannot find slave encoder for connector\n");
+ return 0;
+ }
+
+ sfuncs = slave->slave_funcs;
+ if (sfuncs->get_modes == NULL)
+ return 0;
+
+ return sfuncs->get_modes(&slave->base, connector);
+}
+
+struct drm_encoder *
+arcpgu_drm_connector_best_encoder(struct drm_connector *connector)
+{
+ struct drm_encoder_slave *slave;
+ struct arcpgu_drm_connector *con =
+ container_of(connector, struct arcpgu_drm_connector, connector);
+
+ slave = con->encoder_slave;
+ if (slave == NULL) {
+ dev_err(connector->dev->dev,
+ "connector_best_encoder: cannot find slave encoder for connector\n");
+ return NULL;
+ }
+
+ return &slave->base;
+}
+
+static enum drm_connector_status
+arcpgu_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+ enum drm_connector_status status = connector_status_unknown;
+ const struct drm_encoder_slave_funcs *sfuncs;
+ struct drm_encoder_slave *slave;
+
+ struct arcpgu_drm_connector *con =
+ container_of(connector, struct arcpgu_drm_connector, connector);
+
+ slave = con->encoder_slave;
+ if (slave == NULL) {
+ dev_err(connector->dev->dev,
+ "connector_detect: cannot find slave encoder for connector\n");
+ return status;
+ }
+
+ sfuncs = slave->slave_funcs;
+ if (sfuncs && sfuncs->detect)
+ return sfuncs->detect(&slave->base, connector);
+
+ dev_err(connector->dev->dev, "connector_detect: could not detect slave funcs\n");
+ return status;
+}
+
+static void arcpgu_drm_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_helper_funcs
+arcpgu_drm_connector_helper_funcs = {
+ .get_modes = arcpgu_drm_connector_get_modes,
+ .best_encoder = arcpgu_drm_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .detect = arcpgu_drm_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = arcpgu_drm_connector_destroy,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_encoder_helper_funcs arcpgu_drm_encoder_helper_funcs = {
+ .dpms = drm_i2c_encoder_dpms,
+ .mode_fixup = drm_i2c_encoder_mode_fixup,
+ .mode_set = drm_i2c_encoder_mode_set,
+ .prepare = drm_i2c_encoder_prepare,
+ .commit = drm_i2c_encoder_commit,
+ .detect = drm_i2c_encoder_detect,
+};
+
+static struct drm_encoder_funcs arcpgu_drm_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
+{
+ struct arcpgu_drm_connector *arcpgu_connector;
+ struct drm_i2c_encoder_driver *driver;
+ struct drm_encoder_slave *encoder;
+ struct drm_connector *connector;
+ struct i2c_client *i2c_slave;
+ int ret;
+
+ encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+
+ i2c_slave = of_find_i2c_device_by_node(np);
+ if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) {
+ dev_err(drm->dev, "failed to find i2c slave encoder\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (i2c_slave->dev.driver == NULL) {
+ dev_err(drm->dev, "failed to find i2c slave driver\n");
+ return -EPROBE_DEFER;
+ }
+
+ driver =
+ to_drm_i2c_encoder_driver(to_i2c_driver(i2c_slave->dev.driver));
+ ret = driver->encoder_init(i2c_slave, drm, encoder);
+ if (ret) {
+ dev_err(drm->dev, "failed to initialize i2c encoder slave\n");
+ return ret;
+ }
+
+ encoder->base.possible_crtcs = 1;
+ encoder->base.possible_clones = 0;
+ ret = drm_encoder_init(drm, &encoder->base, &arcpgu_drm_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ret;
+
+ drm_encoder_helper_add(&encoder->base,
+ &arcpgu_drm_encoder_helper_funcs);
+
+ arcpgu_connector = devm_kzalloc(drm->dev, sizeof(*arcpgu_connector),
+ GFP_KERNEL);
+ if (!arcpgu_connector) {
+ ret = -ENOMEM;
+ goto error_encoder_cleanup;
+ }
+
+ connector = &arcpgu_connector->connector;
+ drm_connector_helper_add(connector, &arcpgu_drm_connector_helper_funcs);
+ ret = drm_connector_init(drm, connector, &arcpgu_drm_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret < 0) {
+ dev_err(drm->dev, "failed to initialize drm connector\n");
+ goto error_encoder_cleanup;
+ }
+
+ ret = drm_mode_connector_attach_encoder(connector, &encoder->base);
+ if (ret < 0) {
+ dev_err(drm->dev, "could not attach connector to encoder\n");
+ drm_connector_unregister(connector);
+ goto error_connector_cleanup;
+ }
+
+ arcpgu_connector->encoder_slave = encoder;
+
+ return 0;
+
+error_connector_cleanup:
+ drm_connector_cleanup(connector);
+
+error_encoder_cleanup:
+ drm_encoder_cleanup(&encoder->base);
+ return ret;
+}
diff --git a/drivers/gpu/drm/arc/arcpgu_regs.h b/drivers/gpu/drm/arc/arcpgu_regs.h
new file mode 100644
index 000000000000..95a13a84c373
--- /dev/null
+++ b/drivers/gpu/drm/arc/arcpgu_regs.h
@@ -0,0 +1,40 @@
+/*
+ * ARC PGU DRM driver.
+ *
+ * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.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.
+ *
+ */
+
+#ifndef _ARC_PGU_REGS_H_
+#define _ARC_PGU_REGS_H_
+
+#define ARCPGU_REG_CTRL 0x00
+#define ARCPGU_REG_STAT 0x04
+#define ARCPGU_REG_FMT 0x10
+#define ARCPGU_REG_HSYNC 0x14
+#define ARCPGU_REG_VSYNC 0x18
+#define ARCPGU_REG_ACTIVE 0x1c
+#define ARCPGU_REG_BUF0_ADDR 0x40
+#define ARCPGU_REG_STRIDE 0x50
+#define ARCPGU_REG_START_SET 0x84
+
+#define ARCPGU_REG_ID 0x3FC
+
+#define ARCPGU_CTRL_ENABLE_MASK 0x02
+#define ARCPGU_CTRL_VS_POL_MASK 0x1
+#define ARCPGU_CTRL_VS_POL_OFST 0x3
+#define ARCPGU_CTRL_HS_POL_MASK 0x1
+#define ARCPGU_CTRL_HS_POL_OFST 0x4
+#define ARCPGU_MODE_RGB888_MASK 0x04
+#define ARCPGU_STAT_BUSY_MASK 0x02
+
+#endif
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index b04662e0e608..92e11a2b83c8 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -2510,12 +2510,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
*/
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
{
- if (crtc->state) {
- drm_property_unreference_blob(crtc->state->mode_blob);
- drm_property_unreference_blob(crtc->state->degamma_lut);
- drm_property_unreference_blob(crtc->state->ctm);
- drm_property_unreference_blob(crtc->state->gamma_lut);
- }
+ if (crtc->state)
+ __drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);
+
kfree(crtc->state);
crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
@@ -2621,8 +2618,8 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
*/
void drm_atomic_helper_plane_reset(struct drm_plane *plane)
{
- if (plane->state && plane->state->fb)
- drm_framebuffer_unreference(plane->state->fb);
+ if (plane->state)
+ __drm_atomic_helper_plane_destroy_state(plane, plane->state);
kfree(plane->state);
plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
@@ -2743,6 +2740,10 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
struct drm_connector_state *conn_state =
kzalloc(sizeof(*conn_state), GFP_KERNEL);
+ if (connector->state)
+ __drm_atomic_helper_connector_destroy_state(connector,
+ connector->state);
+
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, conn_state);
}
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 7d1960e4b23f..c8253ebad680 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -168,6 +168,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
{ DRM_MODE_CONNECTOR_eDP, "eDP" },
{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
{ DRM_MODE_CONNECTOR_DSI, "DSI" },
+ { DRM_MODE_CONNECTOR_DPI, "DPI" },
};
static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
@@ -179,6 +180,7 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
{ DRM_MODE_ENCODER_DSI, "DSI" },
{ DRM_MODE_ENCODER_DPMST, "DP MST" },
+ { DRM_MODE_ENCODER_DPI, "DPI" },
};
static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
@@ -360,10 +362,6 @@ static struct drm_mode_object *_object_find(struct drm_device *dev,
obj = NULL;
if (obj && obj->id != id)
obj = NULL;
- /* don't leak out unref'd fb's */
- if (obj &&
- obj->type == DRM_MODE_OBJECT_BLOB)
- obj = NULL;
if (obj && obj->free_cb) {
if (!kref_get_unless_zero(&obj->refcount))
@@ -380,24 +378,28 @@ static struct drm_mode_object *_object_find(struct drm_device *dev,
* @id: id of the mode object
* @type: type of the mode object
*
- * Note that framebuffers cannot be looked up with this functions - since those
- * are reference counted, they need special treatment. Even with
- * DRM_MODE_OBJECT_ANY (although that will simply return NULL
- * rather than WARN_ON()).
+ * This function is used to look up a modeset object. It will acquire a
+ * reference for reference counted objects. This reference must be dropped again
+ * by callind drm_mode_object_unreference().
*/
struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
uint32_t id, uint32_t type)
{
struct drm_mode_object *obj = NULL;
- /* Framebuffers are reference counted and need their own lookup
- * function.*/
- WARN_ON(type == DRM_MODE_OBJECT_FB || type == DRM_MODE_OBJECT_BLOB);
obj = _object_find(dev, id, type);
return obj;
}
EXPORT_SYMBOL(drm_mode_object_find);
+/**
+ * drm_mode_object_unreference - decr the object refcnt
+ * @obj: mode_object
+ *
+ * This functions decrements the object's refcount if it is a refcounted modeset
+ * object. It is a no-op on any other object. This is used to drop references
+ * acquired with drm_mode_object_reference().
+ */
void drm_mode_object_unreference(struct drm_mode_object *obj)
{
if (obj->free_cb) {
@@ -408,11 +410,12 @@ void drm_mode_object_unreference(struct drm_mode_object *obj)
EXPORT_SYMBOL(drm_mode_object_unreference);
/**
- * drm_mode_object_reference - incr the fb refcnt
+ * drm_mode_object_reference - incr the object refcnt
* @obj: mode_object
*
- * This function operates only on refcounted objects.
- * This functions increments the object's refcount.
+ * This functions increments the object's refcount if it is a refcounted modeset
+ * object. It is a no-op on any other object. References should be dropped again
+ * by calling drm_mode_object_unreference().
*/
void drm_mode_object_reference(struct drm_mode_object *obj)
{
@@ -4241,6 +4244,20 @@ done:
return ret;
}
+static void drm_property_free_blob(struct kref *kref)
+{
+ struct drm_property_blob *blob =
+ container_of(kref, struct drm_property_blob, base.refcount);
+
+ mutex_lock(&blob->dev->mode_config.blob_lock);
+ list_del(&blob->head_global);
+ mutex_unlock(&blob->dev->mode_config.blob_lock);
+
+ drm_mode_object_unregister(blob->dev, &blob->base);
+
+ kfree(blob);
+}
+
/**
* drm_property_create_blob - Create new blob property
*
@@ -4278,20 +4295,16 @@ drm_property_create_blob(struct drm_device *dev, size_t length,
if (data)
memcpy(blob->data, data, length);
- mutex_lock(&dev->mode_config.blob_lock);
-
- ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+ ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB,
+ true, drm_property_free_blob);
if (ret) {
kfree(blob);
- mutex_unlock(&dev->mode_config.blob_lock);
return ERR_PTR(-EINVAL);
}
- kref_init(&blob->refcount);
-
+ mutex_lock(&dev->mode_config.blob_lock);
list_add_tail(&blob->head_global,
&dev->mode_config.property_blob_list);
-
mutex_unlock(&dev->mode_config.blob_lock);
return blob;
@@ -4299,27 +4312,6 @@ drm_property_create_blob(struct drm_device *dev, size_t length,
EXPORT_SYMBOL(drm_property_create_blob);
/**
- * drm_property_free_blob - Blob property destructor
- *
- * Internal free function for blob properties; must not be used directly.
- *
- * @kref: Reference
- */
-static void drm_property_free_blob(struct kref *kref)
-{
- struct drm_property_blob *blob =
- container_of(kref, struct drm_property_blob, refcount);
-
- WARN_ON(!mutex_is_locked(&blob->dev->mode_config.blob_lock));
-
- list_del(&blob->head_global);
- list_del(&blob->head_file);
- drm_mode_object_unregister(blob->dev, &blob->base);
-
- kfree(blob);
-}
-
-/**
* drm_property_unreference_blob - Unreference a blob property
*
* Drop a reference on a blob property. May free the object.
@@ -4328,42 +4320,14 @@ static void drm_property_free_blob(struct kref *kref)
*/
void drm_property_unreference_blob(struct drm_property_blob *blob)
{
- struct drm_device *dev;
-
if (!blob)
return;
- dev = blob->dev;
-
- DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount));
-
- if (kref_put_mutex(&blob->refcount, drm_property_free_blob,
- &dev->mode_config.blob_lock))
- mutex_unlock(&dev->mode_config.blob_lock);
- else
- might_lock(&dev->mode_config.blob_lock);
+ drm_mode_object_unreference(&blob->base);
}
EXPORT_SYMBOL(drm_property_unreference_blob);
/**
- * drm_property_unreference_blob_locked - Unreference a blob property with blob_lock held
- *
- * Drop a reference on a blob property. May free the object. This must be
- * called with blob_lock held.
- *
- * @blob: Pointer to blob property
- */
-static void drm_property_unreference_blob_locked(struct drm_property_blob *blob)
-{
- if (!blob)
- return;
-
- DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount));
-
- kref_put(&blob->refcount, drm_property_free_blob);
-}
-
-/**
* drm_property_destroy_user_blobs - destroy all blobs created by this client
* @dev: DRM device
* @file_priv: destroy all blobs owned by this file handle
@@ -4373,14 +4337,14 @@ void drm_property_destroy_user_blobs(struct drm_device *dev,
{
struct drm_property_blob *blob, *bt;
- mutex_lock(&dev->mode_config.blob_lock);
-
+ /*
+ * When the file gets released that means no one else can access the
+ * blob list any more, so no need to grab dev->blob_lock.
+ */
list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) {
list_del_init(&blob->head_file);
- drm_property_unreference_blob_locked(blob);
+ drm_property_unreference_blob(blob);
}
-
- mutex_unlock(&dev->mode_config.blob_lock);
}
/**
@@ -4392,35 +4356,11 @@ void drm_property_destroy_user_blobs(struct drm_device *dev,
*/
struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob)
{
- DRM_DEBUG("%p: blob ID: %d (%d)\n", blob, blob->base.id, atomic_read(&blob->refcount.refcount));
- kref_get(&blob->refcount);
+ drm_mode_object_reference(&blob->base);
return blob;
}
EXPORT_SYMBOL(drm_property_reference_blob);
-/*
- * Like drm_property_lookup_blob, but does not return an additional reference.
- * Must be called with blob_lock held.
- */
-static struct drm_property_blob *__drm_property_lookup_blob(struct drm_device *dev,
- uint32_t id)
-{
- struct drm_mode_object *obj = NULL;
- struct drm_property_blob *blob;
-
- WARN_ON(!mutex_is_locked(&dev->mode_config.blob_lock));
-
- mutex_lock(&dev->mode_config.idr_mutex);
- obj = idr_find(&dev->mode_config.crtc_idr, id);
- if (!obj || (obj->type != DRM_MODE_OBJECT_BLOB) || (obj->id != id))
- blob = NULL;
- else
- blob = obj_to_blob(obj);
- mutex_unlock(&dev->mode_config.idr_mutex);
-
- return blob;
-}
-
/**
* drm_property_lookup_blob - look up a blob property and take a reference
* @dev: drm device
@@ -4433,16 +4373,12 @@ static struct drm_property_blob *__drm_property_lookup_blob(struct drm_device *d
struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
uint32_t id)
{
- struct drm_property_blob *blob;
-
- mutex_lock(&dev->mode_config.blob_lock);
- blob = __drm_property_lookup_blob(dev, id);
- if (blob) {
- if (!kref_get_unless_zero(&blob->refcount))
- blob = NULL;
- }
- mutex_unlock(&dev->mode_config.blob_lock);
+ struct drm_mode_object *obj;
+ struct drm_property_blob *blob = NULL;
+ obj = _object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+ if (obj)
+ blob = obj_to_blob(obj);
return blob;
}
EXPORT_SYMBOL(drm_property_lookup_blob);
@@ -4547,26 +4483,21 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
- drm_modeset_lock_all(dev);
- mutex_lock(&dev->mode_config.blob_lock);
- blob = __drm_property_lookup_blob(dev, out_resp->blob_id);
- if (!blob) {
- ret = -ENOENT;
- goto done;
- }
+ blob = drm_property_lookup_blob(dev, out_resp->blob_id);
+ if (!blob)
+ return -ENOENT;
if (out_resp->length == blob->length) {
blob_ptr = (void __user *)(unsigned long)out_resp->data;
if (copy_to_user(blob_ptr, blob->data, blob->length)) {
ret = -EFAULT;
- goto done;
+ goto unref;
}
}
out_resp->length = blob->length;
+unref:
+ drm_property_unreference_blob(blob);
-done:
- mutex_unlock(&dev->mode_config.blob_lock);
- drm_modeset_unlock_all(dev);
return ret;
}
@@ -4645,13 +4576,11 @@ int drm_mode_destroyblob_ioctl(struct drm_device *dev,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
- mutex_lock(&dev->mode_config.blob_lock);
- blob = __drm_property_lookup_blob(dev, out_resp->blob_id);
- if (!blob) {
- ret = -ENOENT;
- goto err;
- }
+ blob = drm_property_lookup_blob(dev, out_resp->blob_id);
+ if (!blob)
+ return -ENOENT;
+ mutex_lock(&dev->mode_config.blob_lock);
/* Ensure the property was actually created by this user. */
list_for_each_entry(bt, &file_priv->blobs, head_file) {
if (bt == blob) {
@@ -4668,13 +4597,18 @@ int drm_mode_destroyblob_ioctl(struct drm_device *dev,
/* We must drop head_file here, because we may not be the last
* reference on the blob. */
list_del_init(&blob->head_file);
- drm_property_unreference_blob_locked(blob);
mutex_unlock(&dev->mode_config.blob_lock);
+ /* One reference from lookup, and one from the filp. */
+ drm_property_unreference_blob(blob);
+ drm_property_unreference_blob(blob);
+
return 0;
err:
mutex_unlock(&dev->mode_config.blob_lock);
+ drm_property_unreference_blob(blob);
+
return ret;
}
@@ -4985,7 +4919,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
}
if (!obj->properties) {
ret = -EINVAL;
- goto out;
+ goto out_unref;
}
ret = get_properties(obj, file_priv->atomic,
@@ -4993,6 +4927,8 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
(uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
&arg->count_props);
+out_unref:
+ drm_mode_object_unreference(obj);
out:
drm_modeset_unlock_all(dev);
return ret;
@@ -5035,20 +4971,20 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
goto out;
}
if (!arg_obj->properties)
- goto out;
+ goto out_unref;
for (i = 0; i < arg_obj->properties->count; i++)
if (arg_obj->properties->properties[i]->base.id == arg->prop_id)
break;
if (i == arg_obj->properties->count)
- goto out;
+ goto out_unref;
prop_obj = drm_mode_object_find(dev, arg->prop_id,
DRM_MODE_OBJECT_PROPERTY);
if (!prop_obj) {
ret = -ENOENT;
- goto out;
+ goto out_unref;
}
property = obj_to_property(prop_obj);
@@ -5071,6 +5007,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
drm_property_change_valid_put(property, ref);
+out_unref:
+ drm_mode_object_unreference(arg_obj);
out:
drm_modeset_unlock_all(dev);
return ret;
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 2fadd8275fa5..d814b3048ee5 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -95,7 +95,7 @@ comment "Sub-drivers"
config DRM_EXYNOS_G2D
bool "G2D"
- depends on !VIDEO_SAMSUNG_S5P_G2D
+ depends on VIDEO_SAMSUNG_S5P_G2D=n
select FRAME_VECTOR
help
Choose this option if you want to use Exynos G2D for DRM.
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 126b0a1915db..f663490e949d 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -2,10 +2,10 @@
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
-exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fbdev.o \
- exynos_drm_fb.o exynos_drm_gem.o exynos_drm_core.o \
- exynos_drm_plane.o
+exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fb.o \
+ exynos_drm_gem.o exynos_drm_core.o exynos_drm_plane.o
+exynosdrm-$(CONFIG_DRM_FBDEV_EMULATION) += exynos_drm_fbdev.o
exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON) += exynos5433_drm_decon.o
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
index 5245bc5e82e9..4ab5bfc23647 100644
--- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
@@ -28,6 +28,10 @@
#define WINDOWS_NR 3
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
+#define IFTYPE_I80 (1 << 0)
+#define I80_HW_TRG (1 << 1)
+#define IFTYPE_HDMI (1 << 2)
+
static const char * const decon_clks_name[] = {
"pclk",
"aclk_decon",
@@ -38,12 +42,6 @@ static const char * const decon_clks_name[] = {
"sclk_decon_eclk",
};
-enum decon_iftype {
- IFTYPE_RGB,
- IFTYPE_I80,
- IFTYPE_HDMI
-};
-
enum decon_flag_bits {
BIT_CLKS_ENABLED,
BIT_IRQS_ENABLED,
@@ -61,7 +59,7 @@ struct decon_context {
struct clk *clks[ARRAY_SIZE(decon_clks_name)];
int pipe;
unsigned long flags;
- enum decon_iftype out_type;
+ unsigned long out_type;
int first_win;
};
@@ -95,7 +93,7 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
if (!test_and_set_bit(BIT_IRQS_ENABLED, &ctx->flags)) {
val = VIDINTCON0_INTEN;
- if (ctx->out_type == IFTYPE_I80)
+ if (ctx->out_type & IFTYPE_I80)
val |= VIDINTCON0_FRAMEDONE;
else
val |= VIDINTCON0_INTFRMEN;
@@ -119,11 +117,11 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
static void decon_setup_trigger(struct decon_context *ctx)
{
- u32 val = (ctx->out_type != IFTYPE_HDMI)
+ u32 val = !(ctx->out_type & I80_HW_TRG)
? TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN
: TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
- TRIGCON_HWTRIGMASK_I80_RGB | TRIGCON_HWTRIGEN_I80_RGB;
+ TRIGCON_HWTRIGMASK | TRIGCON_HWTRIGEN;
writel(val, ctx->addr + DECON_TRIGCON);
}
@@ -136,7 +134,7 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
- if (ctx->out_type == IFTYPE_HDMI) {
+ if (ctx->out_type & IFTYPE_HDMI) {
m->crtc_hsync_start = m->crtc_hdisplay + 10;
m->crtc_hsync_end = m->crtc_htotal - 92;
m->crtc_vsync_start = m->crtc_vdisplay + 1;
@@ -151,17 +149,20 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
/* lcd on and use command if */
val = VIDOUT_LCD_ON;
- if (ctx->out_type == IFTYPE_I80)
+ if (ctx->out_type & IFTYPE_I80) {
val |= VIDOUT_COMMAND_IF;
- else
+ decon_setup_trigger(ctx);
+ } else {
val |= VIDOUT_RGB_IF;
+ }
+
writel(val, ctx->addr + DECON_VIDOUTCON0);
val = VIDTCON2_LINEVAL(m->vdisplay - 1) |
VIDTCON2_HOZVAL(m->hdisplay - 1);
writel(val, ctx->addr + DECON_VIDTCON2);
- if (ctx->out_type != IFTYPE_I80) {
+ if (!(ctx->out_type & IFTYPE_I80)) {
val = VIDTCON00_VBPD_F(
m->crtc_vtotal - m->crtc_vsync_end - 1) |
VIDTCON00_VFPD_F(
@@ -183,10 +184,10 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
writel(val, ctx->addr + DECON_VIDTCON11);
}
- decon_setup_trigger(ctx);
-
/* enable output and display signal */
decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID | VIDCON0_ENVID_F, ~0);
+
+ decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
}
static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
@@ -300,7 +301,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
val = dma_addr + pitch * state->src.h;
writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
- if (ctx->out_type != IFTYPE_HDMI)
+ if (!(ctx->out_type & IFTYPE_HDMI))
val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14)
| BIT_VAL(state->crtc.w * bpp, 13, 0);
else
@@ -312,9 +313,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
/* window enable */
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0);
-
- /* standalone update */
- decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
}
static void decon_disable_plane(struct exynos_drm_crtc *crtc,
@@ -326,15 +324,7 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
- decon_shadow_protect_win(ctx, win, true);
-
- /* window disable */
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0);
-
- decon_shadow_protect_win(ctx, win, false);
-
- /* standalone update */
- decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
}
static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
@@ -348,7 +338,10 @@ static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
for (i = ctx->first_win; i < WINDOWS_NR; i++)
decon_shadow_protect_win(ctx, i, false);
- if (ctx->out_type == IFTYPE_I80)
+ /* standalone update */
+ decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
+
+ if (ctx->out_type & IFTYPE_I80)
set_bit(BIT_WIN_UPDATED, &ctx->flags);
}
@@ -374,7 +367,7 @@ static void decon_swreset(struct decon_context *ctx)
WARN(tries == 0, "failed to software reset DECON\n");
- if (ctx->out_type != IFTYPE_HDMI)
+ if (!(ctx->out_type & IFTYPE_HDMI))
return;
writel(VIDCON0_CLKVALUP | VIDCON0_VLCKFREE, ctx->addr + DECON_VIDCON0);
@@ -383,7 +376,9 @@ static void decon_swreset(struct decon_context *ctx)
writel(VIDCON1_VCLK_RUN_VDEN_DISABLE, ctx->addr + DECON_VIDCON1);
writel(CRCCTRL_CRCEN | CRCCTRL_CRCSTART_F | CRCCTRL_CRCCLKEN,
ctx->addr + DECON_CRCCTRL);
- decon_setup_trigger(ctx);
+
+ if (ctx->out_type & IFTYPE_I80)
+ decon_setup_trigger(ctx);
}
static void decon_enable(struct exynos_drm_crtc *crtc)
@@ -395,8 +390,12 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
pm_runtime_get_sync(ctx->dev);
+ exynos_drm_pipe_clk_enable(crtc, true);
+
set_bit(BIT_CLKS_ENABLED, &ctx->flags);
+ decon_swreset(ctx);
+
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(BIT_IRQS_ENABLED, &ctx->flags))
decon_enable_vblank(ctx->crtc);
@@ -424,6 +423,8 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
clear_bit(BIT_CLKS_ENABLED, &ctx->flags);
+ exynos_drm_pipe_clk_enable(crtc, false);
+
pm_runtime_put_sync(ctx->dev);
set_bit(BIT_SUSPENDED, &ctx->flags);
@@ -459,8 +460,10 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
decon_shadow_protect_win(ctx, win, true);
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0);
decon_shadow_protect_win(ctx, win, false);
- decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
}
+
+ decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
+
/* TODO: wait for possible vsync */
msleep(50);
@@ -509,7 +512,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
}
exynos_plane = &ctx->planes[ctx->first_win];
- out_type = (ctx->out_type == IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI
+ out_type = (ctx->out_type & IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI
: EXYNOS_DISPLAY_TYPE_LCD;
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, out_type,
@@ -617,11 +620,11 @@ static const struct dev_pm_ops exynos5433_decon_pm_ops = {
static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
{
.compatible = "samsung,exynos5433-decon",
- .data = (void *)IFTYPE_RGB
+ .data = (void *)I80_HW_TRG
},
{
.compatible = "samsung,exynos5433-decon-tv",
- .data = (void *)IFTYPE_HDMI
+ .data = (void *)(I80_HW_TRG | IFTYPE_HDMI)
},
{},
};
@@ -629,7 +632,6 @@ MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match);
static int exynos5433_decon_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct decon_context *ctx;
struct resource *res;
@@ -642,14 +644,14 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
__set_bit(BIT_SUSPENDED, &ctx->flags);
ctx->dev = dev;
+ ctx->out_type = (unsigned long)of_device_get_match_data(dev);
- of_id = of_match_device(exynos5433_decon_driver_dt_match, &pdev->dev);
- ctx->out_type = (enum decon_iftype)of_id->data;
-
- if (ctx->out_type == IFTYPE_HDMI)
+ if (ctx->out_type & IFTYPE_HDMI) {
ctx->first_win = 1;
- else if (of_get_child_by_name(dev->of_node, "i80-if-timings"))
ctx->out_type = IFTYPE_I80;
+ } else if (of_get_child_by_name(dev->of_node, "i80-if-timings")) {
+ ctx->out_type = IFTYPE_I80;
+ }
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
struct clk *clk;
@@ -674,7 +676,7 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- (ctx->out_type == IFTYPE_I80) ? "lcd_sys" : "vsync");
+ (ctx->out_type & IFTYPE_I80) ? "lcd_sys" : "vsync");
if (!res) {
dev_err(dev, "cannot find IRQ resource\n");
return -ENXIO;
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 93361073af9a..f6223f907c15 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -593,7 +593,6 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank,
- .wait_for_vblank = decon_wait_for_vblank,
.atomic_begin = decon_atomic_begin,
.update_plane = decon_update_plane,
.disable_plane = decon_disable_plane,
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
index 8ae3d51b5b33..468498e3fec1 100644
--- a/drivers/gpu/drm/exynos/exynos_dp.c
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -48,14 +48,11 @@ int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
{
struct exynos_dp_device *dp = to_dp(plat_data);
struct drm_encoder *encoder = &dp->encoder;
- struct exynos_drm_crtc *crtc;
- if (!encoder)
- return -1;
+ if (!encoder->crtc)
+ return -EPERM;
- crtc = to_exynos_crtc(encoder->crtc);
- if (crtc && crtc->ops && crtc->ops->clock_enable)
- crtc->ops->clock_enable(crtc, enable);
+ exynos_drm_pipe_clk_enable(to_exynos_crtc(encoder->crtc), enable);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 7f55ba6771c6..011211e4167d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -101,7 +101,7 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
return 0;
err:
- list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ list_for_each_entry_continue_reverse(subdrv, &exynos_drm_subdrv_list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index e36579c1c025..50dd33d5045d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -157,9 +157,8 @@ err_crtc:
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
{
- struct exynos_drm_private *private = dev->dev_private;
- struct exynos_drm_crtc *exynos_crtc =
- to_exynos_crtc(private->crtc[pipe]);
+ struct exynos_drm_crtc *exynos_crtc = exynos_drm_crtc_from_pipe(dev,
+ pipe);
if (exynos_crtc->ops->enable_vblank)
return exynos_crtc->ops->enable_vblank(exynos_crtc);
@@ -169,9 +168,8 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
{
- struct exynos_drm_private *private = dev->dev_private;
- struct exynos_drm_crtc *exynos_crtc =
- to_exynos_crtc(private->crtc[pipe]);
+ struct exynos_drm_crtc *exynos_crtc = exynos_drm_crtc_from_pipe(dev,
+ pipe);
if (exynos_crtc->ops->disable_vblank)
exynos_crtc->ops->disable_vblank(exynos_crtc);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index e50c09b4dce1..cc33ec9296e7 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -120,8 +120,6 @@ struct exynos_drm_plane_config {
* @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.
* @atomic_check: validate state
* @atomic_begin: prepare device to receive an update
* @atomic_flush: mark the end of device update
@@ -129,10 +127,6 @@ struct exynos_drm_plane_config {
* @disable_plane: disable hardware specific overlay.
* @te_handler: trigger to transfer video image at the tearing effect
* synchronization signal if there is a page flip request.
- * @clock_enable: optional function enabling/disabling display domain clock,
- * called from exynos-dp driver before powering up (with
- * 'enable' argument as true) and after powering down (with
- * 'enable' as false).
*/
struct exynos_drm_crtc;
struct exynos_drm_crtc_ops {
@@ -141,7 +135,6 @@ struct exynos_drm_crtc_ops {
void (*commit)(struct exynos_drm_crtc *crtc);
int (*enable_vblank)(struct exynos_drm_crtc *crtc);
void (*disable_vblank)(struct exynos_drm_crtc *crtc);
- void (*wait_for_vblank)(struct exynos_drm_crtc *crtc);
int (*atomic_check)(struct exynos_drm_crtc *crtc,
struct drm_crtc_state *state);
void (*atomic_begin)(struct exynos_drm_crtc *crtc);
@@ -151,7 +144,10 @@ struct exynos_drm_crtc_ops {
struct exynos_drm_plane *plane);
void (*atomic_flush)(struct exynos_drm_crtc *crtc);
void (*te_handler)(struct exynos_drm_crtc *crtc);
- void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
+};
+
+struct exynos_drm_clk {
+ void (*enable)(struct exynos_drm_clk *clk, bool enable);
};
/*
@@ -182,8 +178,16 @@ struct exynos_drm_crtc {
atomic_t pending_update;
const struct exynos_drm_crtc_ops *ops;
void *ctx;
+ struct exynos_drm_clk *pipe_clk;
};
+static inline void exynos_drm_pipe_clk_enable(struct exynos_drm_crtc *crtc,
+ bool enable)
+{
+ if (crtc->pipe_clk)
+ crtc->pipe_clk->enable(crtc->pipe_clk, enable);
+}
+
struct exynos_drm_g2d_private {
struct device *dev;
struct list_head inuse_cmdlist;
@@ -232,6 +236,14 @@ struct exynos_drm_private {
wait_queue_head_t wait;
};
+static inline struct exynos_drm_crtc *
+exynos_drm_crtc_from_pipe(struct drm_device *dev, int pipe)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+
+ return to_exynos_crtc(private->crtc[pipe]);
+}
+
static inline struct device *to_dma_dev(struct drm_device *dev)
{
struct exynos_drm_private *priv = dev->dev_private;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 63c84a106c0b..72c3565d22ee 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -280,7 +280,7 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */
struct list_head transfer_list;
- struct exynos_dsi_driver_data *driver_data;
+ const struct exynos_dsi_driver_data *driver_data;
struct device_node *bridge_node;
};
@@ -532,15 +532,6 @@ static const struct of_device_id exynos_dsi_of_match[] = {
{ }
};
-static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data(
- struct platform_device *pdev)
-{
- const struct of_device_id *of_id =
- of_match_device(exynos_dsi_of_match, &pdev->dev);
-
- return (struct exynos_dsi_driver_data *)of_id->data;
-}
-
static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
{
if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
@@ -564,7 +555,7 @@ static void exynos_dsi_reset(struct exynos_dsi *dsi)
static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
unsigned long best_freq = 0;
u32 min_delta = 0xffffffff;
u8 p_min, p_max;
@@ -618,7 +609,7 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
unsigned long freq)
{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
unsigned long fin, fout;
int timeout;
u8 p, s;
@@ -712,7 +703,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
const unsigned int *reg_values = driver_data->reg_values;
u32 reg;
@@ -790,7 +781,7 @@ static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
static int exynos_dsi_init_link(struct exynos_dsi *dsi)
{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int timeout;
u32 reg;
u32 lanes_mask;
@@ -1334,7 +1325,7 @@ static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
static int exynos_dsi_init(struct exynos_dsi *dsi)
{
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
exynos_dsi_reset(dsi);
exynos_dsi_enable_irq(dsi);
@@ -1833,7 +1824,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->dsi_host.dev = dev;
dsi->dev = dev;
- dsi->driver_data = exynos_dsi_get_driver_data(pdev);
+ dsi->driver_data = of_device_get_match_data(dev);
ret = exynos_dsi_parse_dt(dsi);
if (ret)
@@ -1917,7 +1908,7 @@ static int __maybe_unused exynos_dsi_suspend(struct device *dev)
{
struct drm_encoder *encoder = dev_get_drvdata(dev);
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int ret, i;
usleep_range(10000, 20000);
@@ -1948,7 +1939,7 @@ static int __maybe_unused exynos_dsi_resume(struct device *dev)
{
struct drm_encoder *encoder = dev_get_drvdata(dev);
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int ret, i;
ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index d614194644c8..81cc5537cf25 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -199,17 +199,6 @@ dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index)
return exynos_fb->dma_addr[index];
}
-static void exynos_drm_output_poll_changed(struct drm_device *dev)
-{
- struct exynos_drm_private *private = dev->dev_private;
- struct drm_fb_helper *fb_helper = private->fb_helper;
-
- 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 = {
.fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 4656cd6e7083..67dcd6831291 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -311,3 +311,14 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper);
}
+
+void exynos_drm_output_poll_changed(struct drm_device *dev)
+{
+ struct exynos_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *fb_helper = private->fb_helper;
+
+ if (fb_helper)
+ drm_fb_helper_hotplug_event(fb_helper);
+ else
+ exynos_drm_fbdev_init(dev);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.h b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h
index e16d7f0ae192..330eef87f718 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.h
@@ -15,9 +15,30 @@
#ifndef _EXYNOS_DRM_FBDEV_H_
#define _EXYNOS_DRM_FBDEV_H_
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+
int exynos_drm_fbdev_init(struct drm_device *dev);
-int exynos_drm_fbdev_reinit(struct drm_device *dev);
void exynos_drm_fbdev_fini(struct drm_device *dev);
void exynos_drm_fbdev_restore_mode(struct drm_device *dev);
+void exynos_drm_output_poll_changed(struct drm_device *dev);
+
+#else
+
+static inline int exynos_drm_fbdev_init(struct drm_device *dev)
+{
+ return 0;
+}
+
+static inline void exynos_drm_fbdev_fini(struct drm_device *dev)
+{
+}
+
+static inline void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
+{
+}
+
+#define exynos_drm_output_poll_changed (NULL)
+
+#endif
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 51d484ae9f49..547d759a0a6f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -68,10 +68,15 @@
/* color key value register for hardware window 1 ~ 4. */
#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
-/* I80 / RGB trigger control register */
+/* I80 trigger control register */
#define TRIGCON 0x1A4
-#define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0)
-#define SWTRGCMD_I80_RGB_ENABLE (1 << 1)
+#define TRGMODE_ENABLE (1 << 0)
+#define SWTRGCMD_ENABLE (1 << 1)
+/* Exynos3250, 3472, 4415, 5260 5410, 5420 and 5422 only supported. */
+#define HWTRGEN_ENABLE (1 << 3)
+#define HWTRGMASK_ENABLE (1 << 4)
+/* Exynos3250, 3472, 4415, 5260, 5420 and 5422 only supported. */
+#define HWTRIGEN_PER_ENABLE (1 << 31)
/* display mode change control register except exynos4 */
#define VIDOUT_CON 0x000
@@ -89,12 +94,16 @@
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
+/* HW trigger flag on i80 panel. */
+#define I80_HW_TRG (1 << 1)
+
struct fimd_driver_data {
unsigned int timing_base;
unsigned int lcdblk_offset;
unsigned int lcdblk_vt_shift;
unsigned int lcdblk_bypass_shift;
unsigned int lcdblk_mic_bypass_shift;
+ unsigned int trg_type;
unsigned int has_shadowcon:1;
unsigned int has_clksel:1;
@@ -102,20 +111,26 @@ struct fimd_driver_data {
unsigned int has_vidoutcon:1;
unsigned int has_vtsel:1;
unsigned int has_mic_bypass:1;
+ unsigned int has_dp_clk:1;
+ unsigned int has_hw_trigger:1;
+ unsigned int has_trigger_per_te:1;
};
static struct fimd_driver_data s3c64xx_fimd_driver_data = {
.timing_base = 0x0,
.has_clksel = 1,
.has_limited_fmt = 1,
+ .has_hw_trigger = 1,
};
static struct fimd_driver_data exynos3_fimd_driver_data = {
.timing_base = 0x20000,
.lcdblk_offset = 0x210,
.lcdblk_bypass_shift = 1,
+ .trg_type = I80_HW_TRG,
.has_shadowcon = 1,
.has_vidoutcon = 1,
+ .has_trigger_per_te = 1,
};
static struct fimd_driver_data exynos4_fimd_driver_data = {
@@ -132,9 +147,11 @@ static struct fimd_driver_data exynos4415_fimd_driver_data = {
.lcdblk_offset = 0x210,
.lcdblk_vt_shift = 10,
.lcdblk_bypass_shift = 1,
+ .trg_type = I80_HW_TRG,
.has_shadowcon = 1,
.has_vidoutcon = 1,
.has_vtsel = 1,
+ .has_trigger_per_te = 1,
};
static struct fimd_driver_data exynos5_fimd_driver_data = {
@@ -145,6 +162,7 @@ static struct fimd_driver_data exynos5_fimd_driver_data = {
.has_shadowcon = 1,
.has_vidoutcon = 1,
.has_vtsel = 1,
+ .has_dp_clk = 1,
};
static struct fimd_driver_data exynos5420_fimd_driver_data = {
@@ -153,10 +171,14 @@ static struct fimd_driver_data exynos5420_fimd_driver_data = {
.lcdblk_vt_shift = 24,
.lcdblk_bypass_shift = 15,
.lcdblk_mic_bypass_shift = 11,
+ .trg_type = I80_HW_TRG,
.has_shadowcon = 1,
.has_vidoutcon = 1,
.has_vtsel = 1,
.has_mic_bypass = 1,
+ .has_dp_clk = 1,
+ .has_hw_trigger = 1,
+ .has_trigger_per_te = 1,
};
struct fimd_context {
@@ -182,8 +204,9 @@ struct fimd_context {
atomic_t win_updated;
atomic_t triggering;
- struct fimd_driver_data *driver_data;
+ const struct fimd_driver_data *driver_data;
struct drm_encoder *encoder;
+ struct exynos_drm_clk dp_clk;
};
static const struct of_device_id fimd_driver_dt_match[] = {
@@ -219,15 +242,6 @@ static const uint32_t fimd_formats[] = {
DRM_FORMAT_ARGB8888,
};
-static inline struct fimd_driver_data *drm_fimd_get_driver_data(
- struct platform_device *pdev)
-{
- const struct of_device_id *of_id =
- of_match_device(fimd_driver_dt_match, &pdev->dev);
-
- return (struct fimd_driver_data *)of_id->data;
-}
-
static int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
@@ -400,11 +414,31 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
return (clkdiv < 0x100) ? clkdiv : 0xff;
}
+static void fimd_setup_trigger(struct fimd_context *ctx)
+{
+ void __iomem *timing_base = ctx->regs + ctx->driver_data->timing_base;
+ u32 trg_type = ctx->driver_data->trg_type;
+ u32 val = readl(timing_base + TRIGCON);
+
+ val &= ~(TRGMODE_ENABLE);
+
+ if (trg_type == I80_HW_TRG) {
+ if (ctx->driver_data->has_hw_trigger)
+ val |= HWTRGEN_ENABLE | HWTRGMASK_ENABLE;
+ if (ctx->driver_data->has_trigger_per_te)
+ val |= HWTRIGEN_PER_ENABLE;
+ } else {
+ val |= TRGMODE_ENABLE;
+ }
+
+ writel(val, timing_base + TRIGCON);
+}
+
static void fimd_commit(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
- struct fimd_driver_data *driver_data = ctx->driver_data;
+ const struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
u32 val, clkdiv;
@@ -495,6 +529,8 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
+ fimd_setup_trigger(ctx);
+
/*
* fields of register with prefix '_F' would be updated
* at vsync(same as dma start)
@@ -827,7 +863,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc)
static void fimd_trigger(struct device *dev)
{
struct fimd_context *ctx = dev_get_drvdata(dev);
- struct fimd_driver_data *driver_data = ctx->driver_data;
+ const struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
u32 reg;
@@ -842,7 +878,7 @@ static void fimd_trigger(struct device *dev)
atomic_set(&ctx->triggering, 1);
reg = readl(timing_base + TRIGCON);
- reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
+ reg |= (TRGMODE_ENABLE | SWTRGCMD_ENABLE);
writel(reg, timing_base + TRIGCON);
/*
@@ -856,11 +892,15 @@ static void fimd_trigger(struct device *dev)
static void fimd_te_handler(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
+ u32 trg_type = ctx->driver_data->trg_type;
/* Checks the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
return;
+ if (trg_type == I80_HW_TRG)
+ goto out;
+
/*
* If there is a page flip request, triggers and handles the page flip
* event so that current fb can be updated into panel GRAM.
@@ -868,6 +908,7 @@ static void fimd_te_handler(struct exynos_drm_crtc *crtc)
if (atomic_add_unless(&ctx->win_updated, -1, 0))
fimd_trigger(ctx->dev);
+out:
/* Wakes up vsync event queue */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
@@ -878,21 +919,12 @@ static void fimd_te_handler(struct exynos_drm_crtc *crtc)
drm_crtc_handle_vblank(&ctx->crtc->base);
}
-static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
+static void fimd_dp_clock_enable(struct exynos_drm_clk *clk, bool enable)
{
- struct fimd_context *ctx = crtc->ctx;
- u32 val;
+ struct fimd_context *ctx = container_of(clk, struct fimd_context,
+ dp_clk);
+ u32 val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
- /*
- * Only Exynos 5250, 5260, 5410 and 542x requires enabling DP/MIE
- * clock. On these SoCs the bootloader may enable it but any
- * power domain off/on will reset it to disable state.
- */
- if (ctx->driver_data != &exynos5_fimd_driver_data ||
- ctx->driver_data != &exynos5420_fimd_driver_data)
- return;
-
- val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
writel(val, ctx->regs + DP_MIE_CLKCON);
}
@@ -902,13 +934,11 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank,
- .wait_for_vblank = fimd_wait_for_vblank,
.atomic_begin = fimd_atomic_begin,
.update_plane = fimd_update_plane,
.disable_plane = fimd_disable_plane,
.atomic_flush = fimd_atomic_flush,
.te_handler = fimd_te_handler,
- .clock_enable = fimd_dp_clock_enable,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
@@ -987,6 +1017,11 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(ctx->crtc))
return PTR_ERR(ctx->crtc);
+ if (ctx->driver_data->has_dp_clk) {
+ ctx->dp_clk.enable = fimd_dp_clock_enable;
+ ctx->crtc->pipe_clk = &ctx->dp_clk;
+ }
+
if (ctx->encoder)
exynos_dpi_bind(drm_dev, ctx->encoder);
@@ -1035,7 +1070,7 @@ static int fimd_probe(struct platform_device *pdev)
ctx->dev = dev;
ctx->suspended = true;
- ctx->driver_data = drm_fimd_get_driver_data(pdev);
+ ctx->driver_data = of_device_get_match_data(dev);
if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
ctx->vidcon1 |= VIDCON1_INV_VDEN;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c
index 9869d70e9e54..a0def0be6d65 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_mic.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c
@@ -129,7 +129,7 @@ static void mic_set_path(struct exynos_mic *mic, bool enable)
} else
val &= ~(MIC0_RGB_MUX | MIC0_I80_MUX | MIC0_ON_MUX);
- regmap_write(mic->sysreg, DSD_CFG_MUX, val);
+ ret = regmap_write(mic->sysreg, DSD_CFG_MUX, val);
if (ret)
DRM_ERROR("mic: Failed to read system register\n");
}
@@ -457,6 +457,7 @@ static int exynos_mic_probe(struct platform_device *pdev)
"samsung,disp-syscon");
if (IS_ERR(mic->sysreg)) {
DRM_ERROR("mic: Failed to get system register.\n");
+ ret = PTR_ERR(mic->sysreg);
goto err;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index d86227236f55..50185ac347b2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -11,9 +11,10 @@
#include <drm/drmP.h>
-#include <drm/exynos_drm.h>
-#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
@@ -57,11 +58,12 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last)
}
static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
-
{
struct drm_plane_state *state = &exynos_state->base;
- struct drm_crtc *crtc = exynos_state->base.crtc;
- struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct drm_crtc *crtc = state->crtc;
+ struct drm_crtc_state *crtc_state =
+ drm_atomic_get_existing_crtc_state(state->state, crtc);
+ struct drm_display_mode *mode = &crtc_state->adjusted_mode;
int crtc_x, crtc_y;
unsigned int crtc_w, crtc_h;
unsigned int src_x, src_y;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
index f18fbe43f55f..404367a430b5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <drm/drmP.h>
@@ -696,7 +697,6 @@ static int rotator_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rot_context *rot;
struct exynos_drm_ippdrv *ippdrv;
- const struct of_device_id *match;
int ret;
if (!dev->of_node) {
@@ -708,13 +708,8 @@ static int rotator_probe(struct platform_device *pdev)
if (!rot)
return -ENOMEM;
- match = of_match_node(exynos_rotator_match, dev->of_node);
- if (!match) {
- dev_err(dev, "failed to match node\n");
- return -ENODEV;
- }
- rot->limit_tbl = (struct rot_limit_table *)match->data;
-
+ rot->limit_tbl = (struct rot_limit_table *)
+ of_device_get_match_data(dev);
rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rot->regs = devm_ioremap_resource(dev, rot->regs_res);
if (IS_ERR(rot->regs))
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index e148d728e28c..0f87acb4cf21 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -7,9 +7,9 @@
*
* Based on drivers/media/video/s5p-tv/hdmi_drv.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
+ * 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.
*
*/
@@ -49,14 +49,16 @@
/* AVI header and aspect ratio */
#define HDMI_AVI_VERSION 0x02
-#define HDMI_AVI_LENGTH 0x0D
+#define HDMI_AVI_LENGTH 0x0d
/* 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
+#define HDMI_AUI_VERSION 0x01
+#define HDMI_AUI_LENGTH 0x0a
+
+/* AVI active format aspect ratio */
+#define AVI_SAME_AS_PIC_ASPECT_RATIO 0x08
+#define AVI_4_3_CENTER_RATIO 0x09
+#define AVI_16_9_CENTER_RATIO 0x0a
enum hdmi_type {
HDMI_TYPE13,
@@ -90,11 +92,34 @@ static const char * const supply[] = {
"vdd_pll",
};
+struct hdmiphy_config {
+ int pixel_clock;
+ u8 conf[32];
+};
+
+struct hdmiphy_configs {
+ int count;
+ const struct hdmiphy_config *data;
+};
+
+struct string_array_spec {
+ int count;
+ const char * const *data;
+};
+
+#define INIT_ARRAY_SPEC(a) { .count = ARRAY_SIZE(a), .data = a }
+
struct hdmi_driver_data {
unsigned int type;
- const struct hdmiphy_config *phy_confs;
- unsigned int phy_conf_count;
unsigned int is_apb_phy:1;
+ unsigned int has_sysreg:1;
+ struct hdmiphy_configs phy_confs;
+ struct string_array_spec clk_gates;
+ /*
+ * Array of triplets (p_off, p_on, clock), where p_off and p_on are
+ * required parents of clock when HDMI-PHY is respectively off or on.
+ */
+ struct string_array_spec clk_muxes;
};
struct hdmi_context {
@@ -116,11 +141,9 @@ struct hdmi_context {
struct gpio_desc *hpd_gpio;
int irq;
struct regmap *pmureg;
- struct clk *hdmi;
- struct clk *sclk_hdmi;
- struct clk *sclk_pixel;
- struct clk *sclk_hdmiphy;
- struct clk *mout_hdmi;
+ struct regmap *sysreg;
+ struct clk **clk_gates;
+ struct clk **clk_muxes;
struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
struct regulator *reg_hdmi_en;
};
@@ -135,12 +158,6 @@ static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
return container_of(c, struct hdmi_context, connector);
}
-struct hdmiphy_config {
- int pixel_clock;
- u8 conf[32];
-};
-
-/* list of phy config settings */
static const struct hdmiphy_config hdmiphy_v13_configs[] = {
{
.pixel_clock = 27000000,
@@ -501,25 +518,136 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] = {
},
};
-static struct hdmi_driver_data exynos5420_hdmi_driver_data = {
+static const struct hdmiphy_config hdmiphy_5433_configs[] = {
+ {
+ .pixel_clock = 27000000,
+ .conf = {
+ 0x01, 0x51, 0x22, 0x51, 0x08, 0xfc, 0x88, 0x46,
+ 0x72, 0x50, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
+ 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 27027000,
+ .conf = {
+ 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3,
+ 0x71, 0x50, 0x24, 0x14, 0x24, 0x0f, 0x7c, 0xa5,
+ 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x28, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 40000000,
+ .conf = {
+ 0x01, 0x51, 0x32, 0x55, 0x01, 0x00, 0x88, 0x02,
+ 0x4d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
+ 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 50000000,
+ .conf = {
+ 0x01, 0x51, 0x34, 0x40, 0x64, 0x09, 0x88, 0xc3,
+ 0x3d, 0x50, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
+ 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 65000000,
+ .conf = {
+ 0x01, 0x51, 0x36, 0x31, 0x40, 0x10, 0x04, 0xc6,
+ 0x2e, 0xe8, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
+ 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 74176000,
+ .conf = {
+ 0x01, 0x51, 0x3E, 0x35, 0x5B, 0xDE, 0x88, 0x42,
+ 0x53, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
+ 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 74250000,
+ .conf = {
+ 0x01, 0x51, 0x3E, 0x35, 0x40, 0xF0, 0x88, 0xC2,
+ 0x52, 0x51, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
+ 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 108000000,
+ .conf = {
+ 0x01, 0x51, 0x2d, 0x15, 0x01, 0x00, 0x88, 0x02,
+ 0x72, 0x52, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC,
+ 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40,
+ },
+ },
+ {
+ .pixel_clock = 148500000,
+ .conf = {
+ 0x01, 0x51, 0x1f, 0x00, 0x40, 0xf8, 0x88, 0xc1,
+ 0x52, 0x52, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5,
+ 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30,
+ 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40,
+ },
+ },
+};
+
+static const char * const hdmi_clk_gates4[] = {
+ "hdmi", "sclk_hdmi"
+};
+
+static const char * const hdmi_clk_muxes4[] = {
+ "sclk_pixel", "sclk_hdmiphy", "mout_hdmi"
+};
+
+static const char * const hdmi_clk_gates5433[] = {
+ "hdmi_pclk", "hdmi_i_pclk", "i_tmds_clk", "i_pixel_clk", "i_spdif_clk"
+};
+
+static const char * const hdmi_clk_muxes5433[] = {
+ "oscclk", "tmds_clko", "tmds_clko_user",
+ "oscclk", "pixel_clko", "pixel_clko_user"
+};
+
+static const struct hdmi_driver_data exynos4210_hdmi_driver_data = {
+ .type = HDMI_TYPE13,
+ .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v13_configs),
+ .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
+ .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
+};
+
+static const struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.type = HDMI_TYPE14,
- .phy_confs = hdmiphy_5420_configs,
- .phy_conf_count = ARRAY_SIZE(hdmiphy_5420_configs),
- .is_apb_phy = 1,
+ .phy_confs = INIT_ARRAY_SPEC(hdmiphy_v14_configs),
+ .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
+ .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
};
-static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
+static const struct hdmi_driver_data exynos5420_hdmi_driver_data = {
.type = HDMI_TYPE14,
- .phy_confs = hdmiphy_v14_configs,
- .phy_conf_count = ARRAY_SIZE(hdmiphy_v14_configs),
- .is_apb_phy = 0,
+ .is_apb_phy = 1,
+ .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5420_configs),
+ .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates4),
+ .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes4),
};
-static struct hdmi_driver_data exynos4210_hdmi_driver_data = {
- .type = HDMI_TYPE13,
- .phy_confs = hdmiphy_v13_configs,
- .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
- .is_apb_phy = 0,
+static const struct hdmi_driver_data exynos5433_hdmi_driver_data = {
+ .type = HDMI_TYPE14,
+ .is_apb_phy = 1,
+ .has_sysreg = 1,
+ .phy_confs = INIT_ARRAY_SPEC(hdmiphy_5433_configs),
+ .clk_gates = INIT_ARRAY_SPEC(hdmi_clk_gates5433),
+ .clk_muxes = INIT_ARRAY_SPEC(hdmi_clk_muxes5433),
};
static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
@@ -585,266 +713,52 @@ static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
}
}
-static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
+static int hdmi_clk_enable_gates(struct hdmi_context *hdata)
{
-#define DUMPREG(reg_id) \
- DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
- readl(hdata->regs + reg_id))
- DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix);
- DUMPREG(HDMI_INTC_FLAG);
- DUMPREG(HDMI_INTC_CON);
- DUMPREG(HDMI_HPD_STATUS);
- DUMPREG(HDMI_V13_PHY_RSTOUT);
- DUMPREG(HDMI_V13_PHY_VPLL);
- DUMPREG(HDMI_V13_PHY_CMU);
- DUMPREG(HDMI_V13_CORE_RSTOUT);
-
- DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix);
- DUMPREG(HDMI_CON_0);
- DUMPREG(HDMI_CON_1);
- DUMPREG(HDMI_CON_2);
- DUMPREG(HDMI_SYS_STATUS);
- DUMPREG(HDMI_V13_PHY_STATUS);
- DUMPREG(HDMI_STATUS_EN);
- DUMPREG(HDMI_HPD);
- DUMPREG(HDMI_MODE_SEL);
- DUMPREG(HDMI_V13_HPD_GEN);
- DUMPREG(HDMI_V13_DC_CONTROL);
- DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN);
-
- DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
- DUMPREG(HDMI_H_BLANK_0);
- DUMPREG(HDMI_H_BLANK_1);
- DUMPREG(HDMI_V13_V_BLANK_0);
- DUMPREG(HDMI_V13_V_BLANK_1);
- DUMPREG(HDMI_V13_V_BLANK_2);
- DUMPREG(HDMI_V13_H_V_LINE_0);
- DUMPREG(HDMI_V13_H_V_LINE_1);
- DUMPREG(HDMI_V13_H_V_LINE_2);
- DUMPREG(HDMI_VSYNC_POL);
- DUMPREG(HDMI_INT_PRO_MODE);
- DUMPREG(HDMI_V13_V_BLANK_F_0);
- DUMPREG(HDMI_V13_V_BLANK_F_1);
- DUMPREG(HDMI_V13_V_BLANK_F_2);
- DUMPREG(HDMI_V13_H_SYNC_GEN_0);
- DUMPREG(HDMI_V13_H_SYNC_GEN_1);
- DUMPREG(HDMI_V13_H_SYNC_GEN_2);
- DUMPREG(HDMI_V13_V_SYNC_GEN_1_0);
- DUMPREG(HDMI_V13_V_SYNC_GEN_1_1);
- DUMPREG(HDMI_V13_V_SYNC_GEN_1_2);
- DUMPREG(HDMI_V13_V_SYNC_GEN_2_0);
- DUMPREG(HDMI_V13_V_SYNC_GEN_2_1);
- DUMPREG(HDMI_V13_V_SYNC_GEN_2_2);
- DUMPREG(HDMI_V13_V_SYNC_GEN_3_0);
- DUMPREG(HDMI_V13_V_SYNC_GEN_3_1);
- DUMPREG(HDMI_V13_V_SYNC_GEN_3_2);
-
- DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
- DUMPREG(HDMI_TG_CMD);
- DUMPREG(HDMI_TG_H_FSZ_L);
- DUMPREG(HDMI_TG_H_FSZ_H);
- DUMPREG(HDMI_TG_HACT_ST_L);
- DUMPREG(HDMI_TG_HACT_ST_H);
- DUMPREG(HDMI_TG_HACT_SZ_L);
- DUMPREG(HDMI_TG_HACT_SZ_H);
- DUMPREG(HDMI_TG_V_FSZ_L);
- DUMPREG(HDMI_TG_V_FSZ_H);
- DUMPREG(HDMI_TG_VSYNC_L);
- DUMPREG(HDMI_TG_VSYNC_H);
- DUMPREG(HDMI_TG_VSYNC2_L);
- DUMPREG(HDMI_TG_VSYNC2_H);
- DUMPREG(HDMI_TG_VACT_ST_L);
- DUMPREG(HDMI_TG_VACT_ST_H);
- DUMPREG(HDMI_TG_VACT_SZ_L);
- DUMPREG(HDMI_TG_VACT_SZ_H);
- DUMPREG(HDMI_TG_FIELD_CHG_L);
- DUMPREG(HDMI_TG_FIELD_CHG_H);
- DUMPREG(HDMI_TG_VACT_ST2_L);
- DUMPREG(HDMI_TG_VACT_ST2_H);
- DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
- DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
- DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
- DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
- DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
- DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
- DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
- DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
-#undef DUMPREG
+ int i, ret;
+
+ for (i = 0; i < hdata->drv_data->clk_gates.count; ++i) {
+ ret = clk_prepare_enable(hdata->clk_gates[i]);
+ if (!ret)
+ continue;
+
+ dev_err(hdata->dev, "Cannot enable clock '%s', %d\n",
+ hdata->drv_data->clk_gates.data[i], ret);
+ while (i--)
+ clk_disable_unprepare(hdata->clk_gates[i]);
+ return ret;
+ }
+
+ return 0;
}
-static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)
+static void hdmi_clk_disable_gates(struct hdmi_context *hdata)
{
- int i;
+ int i = hdata->drv_data->clk_gates.count;
-#define DUMPREG(reg_id) \
- DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
- readl(hdata->regs + reg_id))
-
- DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix);
- DUMPREG(HDMI_INTC_CON);
- DUMPREG(HDMI_INTC_FLAG);
- DUMPREG(HDMI_HPD_STATUS);
- DUMPREG(HDMI_INTC_CON_1);
- DUMPREG(HDMI_INTC_FLAG_1);
- DUMPREG(HDMI_PHY_STATUS_0);
- DUMPREG(HDMI_PHY_STATUS_PLL);
- DUMPREG(HDMI_PHY_CON_0);
- DUMPREG(HDMI_V14_PHY_RSTOUT);
- DUMPREG(HDMI_PHY_VPLL);
- DUMPREG(HDMI_PHY_CMU);
- DUMPREG(HDMI_CORE_RSTOUT);
-
- DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix);
- DUMPREG(HDMI_CON_0);
- DUMPREG(HDMI_CON_1);
- DUMPREG(HDMI_CON_2);
- DUMPREG(HDMI_SYS_STATUS);
- DUMPREG(HDMI_PHY_STATUS_0);
- DUMPREG(HDMI_STATUS_EN);
- DUMPREG(HDMI_HPD);
- DUMPREG(HDMI_MODE_SEL);
- DUMPREG(HDMI_ENC_EN);
- DUMPREG(HDMI_DC_CONTROL);
- DUMPREG(HDMI_VIDEO_PATTERN_GEN);
-
- DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
- DUMPREG(HDMI_H_BLANK_0);
- DUMPREG(HDMI_H_BLANK_1);
- DUMPREG(HDMI_V2_BLANK_0);
- DUMPREG(HDMI_V2_BLANK_1);
- DUMPREG(HDMI_V1_BLANK_0);
- DUMPREG(HDMI_V1_BLANK_1);
- DUMPREG(HDMI_V_LINE_0);
- DUMPREG(HDMI_V_LINE_1);
- DUMPREG(HDMI_H_LINE_0);
- DUMPREG(HDMI_H_LINE_1);
- DUMPREG(HDMI_HSYNC_POL);
-
- DUMPREG(HDMI_VSYNC_POL);
- DUMPREG(HDMI_INT_PRO_MODE);
- DUMPREG(HDMI_V_BLANK_F0_0);
- DUMPREG(HDMI_V_BLANK_F0_1);
- DUMPREG(HDMI_V_BLANK_F1_0);
- DUMPREG(HDMI_V_BLANK_F1_1);
-
- DUMPREG(HDMI_H_SYNC_START_0);
- DUMPREG(HDMI_H_SYNC_START_1);
- DUMPREG(HDMI_H_SYNC_END_0);
- DUMPREG(HDMI_H_SYNC_END_1);
-
- DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0);
- DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1);
- DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0);
- DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1);
-
- DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1);
-
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1);
-
- DUMPREG(HDMI_V_BLANK_F2_0);
- DUMPREG(HDMI_V_BLANK_F2_1);
- DUMPREG(HDMI_V_BLANK_F3_0);
- DUMPREG(HDMI_V_BLANK_F3_1);
- DUMPREG(HDMI_V_BLANK_F4_0);
- DUMPREG(HDMI_V_BLANK_F4_1);
- DUMPREG(HDMI_V_BLANK_F5_0);
- DUMPREG(HDMI_V_BLANK_F5_1);
-
- DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1);
-
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0);
- DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1);
-
- DUMPREG(HDMI_VACT_SPACE_1_0);
- DUMPREG(HDMI_VACT_SPACE_1_1);
- DUMPREG(HDMI_VACT_SPACE_2_0);
- DUMPREG(HDMI_VACT_SPACE_2_1);
- DUMPREG(HDMI_VACT_SPACE_3_0);
- DUMPREG(HDMI_VACT_SPACE_3_1);
- DUMPREG(HDMI_VACT_SPACE_4_0);
- DUMPREG(HDMI_VACT_SPACE_4_1);
- DUMPREG(HDMI_VACT_SPACE_5_0);
- DUMPREG(HDMI_VACT_SPACE_5_1);
- DUMPREG(HDMI_VACT_SPACE_6_0);
- DUMPREG(HDMI_VACT_SPACE_6_1);
-
- DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
- DUMPREG(HDMI_TG_CMD);
- DUMPREG(HDMI_TG_H_FSZ_L);
- DUMPREG(HDMI_TG_H_FSZ_H);
- DUMPREG(HDMI_TG_HACT_ST_L);
- DUMPREG(HDMI_TG_HACT_ST_H);
- DUMPREG(HDMI_TG_HACT_SZ_L);
- DUMPREG(HDMI_TG_HACT_SZ_H);
- DUMPREG(HDMI_TG_V_FSZ_L);
- DUMPREG(HDMI_TG_V_FSZ_H);
- DUMPREG(HDMI_TG_VSYNC_L);
- DUMPREG(HDMI_TG_VSYNC_H);
- DUMPREG(HDMI_TG_VSYNC2_L);
- DUMPREG(HDMI_TG_VSYNC2_H);
- DUMPREG(HDMI_TG_VACT_ST_L);
- DUMPREG(HDMI_TG_VACT_ST_H);
- DUMPREG(HDMI_TG_VACT_SZ_L);
- DUMPREG(HDMI_TG_VACT_SZ_H);
- DUMPREG(HDMI_TG_FIELD_CHG_L);
- DUMPREG(HDMI_TG_FIELD_CHG_H);
- DUMPREG(HDMI_TG_VACT_ST2_L);
- DUMPREG(HDMI_TG_VACT_ST2_H);
- DUMPREG(HDMI_TG_VACT_ST3_L);
- DUMPREG(HDMI_TG_VACT_ST3_H);
- DUMPREG(HDMI_TG_VACT_ST4_L);
- DUMPREG(HDMI_TG_VACT_ST4_H);
- DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
- DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
- DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
- DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
- DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
- DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
- DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
- DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
- DUMPREG(HDMI_TG_3D);
-
- DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix);
- DUMPREG(HDMI_AVI_CON);
- DUMPREG(HDMI_AVI_HEADER0);
- DUMPREG(HDMI_AVI_HEADER1);
- DUMPREG(HDMI_AVI_HEADER2);
- DUMPREG(HDMI_AVI_CHECK_SUM);
- DUMPREG(HDMI_VSI_CON);
- DUMPREG(HDMI_VSI_HEADER0);
- DUMPREG(HDMI_VSI_HEADER1);
- DUMPREG(HDMI_VSI_HEADER2);
- for (i = 0; i < 7; ++i)
- DUMPREG(HDMI_VSI_DATA(i));
-
-#undef DUMPREG
+ while (i--)
+ clk_disable_unprepare(hdata->clk_gates[i]);
}
-static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
+static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
{
- if (hdata->drv_data->type == HDMI_TYPE13)
- hdmi_v13_regs_dump(hdata, prefix);
- else
- hdmi_v14_regs_dump(hdata, prefix);
+ struct device *dev = hdata->dev;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < hdata->drv_data->clk_muxes.count; i += 3) {
+ struct clk **c = &hdata->clk_muxes[i];
+
+ ret = clk_set_parent(c[2], c[to_phy]);
+ if (!ret)
+ continue;
+
+ dev_err(dev, "Cannot set clock parent of '%s' to '%s', %d\n",
+ hdata->drv_data->clk_muxes.data[i + 2],
+ hdata->drv_data->clk_muxes.data[i + to_phy], ret);
+ }
+
+ return ret;
}
static u8 hdmi_chksum(struct hdmi_context *hdata,
@@ -993,10 +907,11 @@ static int hdmi_get_modes(struct drm_connector *connector)
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
{
+ const struct hdmiphy_configs *confs = &hdata->drv_data->phy_confs;
int i;
- for (i = 0; i < hdata->drv_data->phy_conf_count; i++)
- if (hdata->drv_data->phy_confs[i].pixel_clock == pixel_clock)
+ for (i = 0; i < confs->count; i++)
+ if (confs->data[i].pixel_clock == pixel_clock)
return i;
DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
@@ -1078,13 +993,11 @@ static bool hdmi_mode_fixup(struct drm_encoder *encoder,
mode_ok = hdmi_mode_valid(connector, adjusted_mode);
- /* just return if user desired mode exists. */
if (mode_ok == MODE_OK)
return true;
/*
- * otherwise, find the most suitable mode among modes and change it
- * to adjusted_mode.
+ * Find the most suitable mode and copy it to adjusted_mode.
*/
list_for_each_entry(m, &connector->modes, head) {
mode_ok = hdmi_mode_valid(connector, m);
@@ -1129,15 +1042,15 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
switch (bits_per_sample) {
case 20:
data_num = 2;
- bit_ch = 1;
+ bit_ch = 1;
break;
case 24:
data_num = 3;
- bit_ch = 1;
+ bit_ch = 1;
break;
default:
data_num = 1;
- bit_ch = 0;
+ bit_ch = 0;
break;
}
@@ -1230,13 +1143,12 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
/* choose HDMI mode */
hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
- /* Apply Video preable and Guard band in HDMI mode only */
+ /* apply video pre-amble and guard band in HDMI mode only */
hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
/* disable bluescreen */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
if (hdata->dvi_mode) {
- /* choose DVI mode */
hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
hdmi_reg_writeb(hdata, HDMI_CON_2,
@@ -1308,7 +1220,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
val = (m->hsync_start - m->hdisplay - 2);
val |= ((m->hsync_end - m->hdisplay - 2) << 10);
- val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
+ val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
/*
@@ -1319,7 +1231,6 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
/* Following values & calculations differ for different type of modes */
if (m->flags & DRM_MODE_FLAG_INTERLACE) {
- /* Interlaced Mode */
val = ((m->vsync_end - m->vdisplay) / 2);
val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
@@ -1348,8 +1259,6 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
} else {
- /* Progressive Mode */
-
val = m->vtotal;
val |= (m->vtotal - m->vdisplay) << 11;
hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
@@ -1365,21 +1274,12 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
m->vtotal - m->vdisplay);
hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
- hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x248);
}
- /* Timing generator registers */
hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC_L, 2, 0x1);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, 0x233);
- hdmi_reg_writev(hdata, HDMI_TG_FIELD_CHG_L, 2, 0x233);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, 2, 0x1);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, 0x233);
- hdmi_reg_writev(hdata, HDMI_TG_FIELD_TOP_HDMI_L, 2, 0x1);
- hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, 0x233);
}
static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
@@ -1390,7 +1290,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
- (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
+ (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
(m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
@@ -1404,7 +1304,6 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
/* Following values & calculations differ for different type of modes */
if (m->flags & DRM_MODE_FLAG_INTERLACE) {
- /* Interlaced Mode */
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
(m->vsync_end - m->vdisplay) / 2);
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
@@ -1437,7 +1336,6 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
} else {
- /* Progressive Mode */
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
m->vsync_end - m->vdisplay);
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
@@ -1454,15 +1352,8 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
m->vtotal - m->vdisplay);
hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
- hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x248);
- hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x47b);
- hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x6ae);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, 0x233);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, 0x233);
- hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, 0x233);
}
- /* Following values & calculations are same irrespective of mode type */
hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
m->hsync_start - m->hdisplay - 2);
hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
@@ -1486,16 +1377,12 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
- /* Timing generator registers */
hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC_L, 2, 0x1);
- hdmi_reg_writev(hdata, HDMI_TG_FIELD_CHG_L, 2, 0x233);
- hdmi_reg_writev(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, 2, 0x1);
- hdmi_reg_writev(hdata, HDMI_TG_FIELD_TOP_HDMI_L, 2, 0x1);
- hdmi_reg_writev(hdata, HDMI_TG_3D, 1, 0x0);
+ if (hdata->drv_data == &exynos5433_hdmi_driver_data)
+ hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
}
static void hdmi_mode_apply(struct hdmi_context *hdata)
@@ -1505,62 +1392,65 @@ static void hdmi_mode_apply(struct hdmi_context *hdata)
else
hdmi_v14_mode_apply(hdata);
- hdmiphy_wait_for_pll(hdata);
-
- clk_set_parent(hdata->mout_hdmi, hdata->sclk_hdmiphy);
-
- /* enable HDMI and timing generator */
hdmi_start(hdata, true);
}
static void hdmiphy_conf_reset(struct hdmi_context *hdata)
{
- clk_set_parent(hdata->mout_hdmi, hdata->sclk_pixel);
-
- /* reset hdmiphy */
+ hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, 1);
+ usleep_range(10000, 12000);
+ hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, 1);
+ usleep_range(10000, 12000);
hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
usleep_range(10000, 12000);
- hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
usleep_range(10000, 12000);
}
+static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
+{
+ u8 v = enable ? HDMI_PHY_ENABLE_MODE_SET : HDMI_PHY_DISABLE_MODE_SET;
+
+ if (hdata->drv_data == &exynos5433_hdmi_driver_data)
+ writel(v, hdata->regs_hdmiphy + HDMIPHY5433_MODE_SET_DONE);
+}
+
static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{
int ret;
- int i;
+ const u8 *phy_conf;
- /* pixel clock */
- i = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
- if (i < 0) {
+ ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
+ if (ret < 0) {
DRM_ERROR("failed to find hdmiphy conf\n");
return;
}
+ phy_conf = hdata->drv_data->phy_confs.data[ret].conf;
+
+ hdmi_clk_set_parents(hdata, false);
+
+ hdmiphy_conf_reset(hdata);
- ret = hdmiphy_reg_write_buf(hdata, 0,
- hdata->drv_data->phy_confs[i].conf, 32);
+ hdmiphy_enable_mode_set(hdata, true);
+ ret = hdmiphy_reg_write_buf(hdata, 0, phy_conf, 32);
if (ret) {
DRM_ERROR("failed to configure hdmiphy\n");
return;
}
-
+ hdmiphy_enable_mode_set(hdata, false);
+ hdmi_clk_set_parents(hdata, true);
usleep_range(10000, 12000);
+ hdmiphy_wait_for_pll(hdata);
}
static void hdmi_conf_apply(struct hdmi_context *hdata)
{
- hdmiphy_conf_reset(hdata);
hdmiphy_conf_apply(hdata);
-
hdmi_start(hdata, false);
hdmi_conf_init(hdata);
-
hdmi_audio_init(hdata);
-
- /* setting core registers */
hdmi_mode_apply(hdata);
hdmi_audio_control(hdata, true);
-
- hdmi_regs_dump(hdata, "start");
}
static void hdmi_mode_set(struct drm_encoder *encoder,
@@ -1579,6 +1469,15 @@ static void hdmi_mode_set(struct drm_encoder *encoder,
hdata->cea_video_id = drm_match_cea_mode(mode);
}
+static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
+{
+ if (!hdata->sysreg)
+ return;
+
+ regmap_update_bits(hdata->sysreg, EXYNOS5433_SYSREG_DISP_HDMI_PHY,
+ SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
+}
+
static void hdmi_enable(struct drm_encoder *encoder)
{
struct hdmi_context *hdata = encoder_to_hdmi(encoder);
@@ -1591,10 +1490,13 @@ static void hdmi_enable(struct drm_encoder *encoder)
if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
DRM_DEBUG_KMS("failed to enable regulator bulk\n");
- /* set pmu hdmiphy control bit to enable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 1);
+ hdmi_set_refclk(hdata, true);
+
+ hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN);
+
hdmi_conf_apply(hdata);
hdata->powered = true;
@@ -1623,12 +1525,14 @@ static void hdmi_disable(struct drm_encoder *encoder)
if (funcs && funcs->disable)
(*funcs->disable)(crtc);
- /* HDMI System Disable */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
cancel_delayed_work(&hdata->hotplug_work);
- /* reset pmu hdmiphy control bit to disable hdmiphy */
+ hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN);
+
+ hdmi_set_refclk(hdata, false);
+
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 0);
@@ -1670,6 +1574,57 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
return IRQ_HANDLED;
}
+static int hdmi_clks_get(struct hdmi_context *hdata,
+ const struct string_array_spec *names,
+ struct clk **clks)
+{
+ struct device *dev = hdata->dev;
+ int i;
+
+ for (i = 0; i < names->count; ++i) {
+ struct clk *clk = devm_clk_get(dev, names->data[i]);
+
+ if (IS_ERR(clk)) {
+ int ret = PTR_ERR(clk);
+
+ dev_err(dev, "Cannot get clock %s, %d\n",
+ names->data[i], ret);
+
+ return ret;
+ }
+
+ clks[i] = clk;
+ }
+
+ return 0;
+}
+
+static int hdmi_clk_init(struct hdmi_context *hdata)
+{
+ const struct hdmi_driver_data *drv_data = hdata->drv_data;
+ int count = drv_data->clk_gates.count + drv_data->clk_muxes.count;
+ struct device *dev = hdata->dev;
+ struct clk **clks;
+ int ret;
+
+ if (!count)
+ return 0;
+
+ clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL);
+ if (!clks)
+ return -ENOMEM;
+
+ hdata->clk_gates = clks;
+ hdata->clk_muxes = clks + drv_data->clk_gates.count;
+
+ ret = hdmi_clks_get(hdata, &drv_data->clk_gates, hdata->clk_gates);
+ if (ret)
+ return ret;
+
+ return hdmi_clks_get(hdata, &drv_data->clk_muxes, hdata->clk_muxes);
+}
+
+
static int hdmi_resources_init(struct hdmi_context *hdata)
{
struct device *dev = hdata->dev;
@@ -1688,39 +1643,14 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
DRM_ERROR("failed to get GPIO irq\n");
return hdata->irq;
}
- /* get clocks, power */
- hdata->hdmi = devm_clk_get(dev, "hdmi");
- if (IS_ERR(hdata->hdmi)) {
- DRM_ERROR("failed to get clock 'hdmi'\n");
- ret = PTR_ERR(hdata->hdmi);
- goto fail;
- }
- hdata->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
- if (IS_ERR(hdata->sclk_hdmi)) {
- DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
- ret = PTR_ERR(hdata->sclk_hdmi);
- goto fail;
- }
- hdata->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
- if (IS_ERR(hdata->sclk_pixel)) {
- DRM_ERROR("failed to get clock 'sclk_pixel'\n");
- ret = PTR_ERR(hdata->sclk_pixel);
- goto fail;
- }
- hdata->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
- if (IS_ERR(hdata->sclk_hdmiphy)) {
- DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
- ret = PTR_ERR(hdata->sclk_hdmiphy);
- goto fail;
- }
- hdata->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
- if (IS_ERR(hdata->mout_hdmi)) {
- DRM_ERROR("failed to get clock 'mout_hdmi'\n");
- ret = PTR_ERR(hdata->mout_hdmi);
- goto fail;
- }
- clk_set_parent(hdata->mout_hdmi, hdata->sclk_pixel);
+ ret = hdmi_clk_init(hdata);
+ if (ret)
+ return ret;
+
+ ret = hdmi_clk_set_parents(hdata, false);
+ if (ret)
+ return ret;
for (i = 0; i < ARRAY_SIZE(supply); ++i) {
hdata->regul_bulk[i].supply = supply[i];
@@ -1745,9 +1675,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
DRM_ERROR("failed to enable hdmi-en regulator\n");
return ret;
-fail:
- DRM_ERROR("HDMI resource init - failed\n");
- return ret;
}
static struct of_device_id hdmi_match_types[] = {
@@ -1761,6 +1688,9 @@ static struct of_device_id hdmi_match_types[] = {
.compatible = "samsung,exynos5420-hdmi",
.data = &exynos5420_hdmi_driver_data,
}, {
+ .compatible = "samsung,exynos5433-hdmi",
+ .data = &exynos5433_hdmi_driver_data,
+ }, {
/* end node */
}
};
@@ -1830,7 +1760,6 @@ static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
static int hdmi_probe(struct platform_device *pdev)
{
struct device_node *ddc_node, *phy_node;
- const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct hdmi_context *hdata;
struct resource *res;
@@ -1840,11 +1769,7 @@ static int hdmi_probe(struct platform_device *pdev)
if (!hdata)
return -ENOMEM;
- match = of_match_device(hdmi_match_types, dev);
- if (!match)
- return -ENODEV;
-
- hdata->drv_data = match->data;
+ hdata->drv_data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, hdata);
@@ -1867,7 +1792,6 @@ static int hdmi_probe(struct platform_device *pdev)
if (ddc_node)
goto out_get_ddc_adpt;
- /* DDC i2c driver */
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
@@ -1885,7 +1809,6 @@ out_get_ddc_adpt:
if (phy_node)
goto out_get_phy_port;
- /* hdmiphy i2c driver */
phy_node = of_parse_phandle(dev->of_node, "phy", 0);
if (!phy_node) {
DRM_ERROR("Failed to find hdmiphy node in device tree\n");
@@ -1929,6 +1852,16 @@ out_get_phy_port:
goto err_hdmiphy;
}
+ if (hdata->drv_data->has_sysreg) {
+ hdata->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,sysreg-phandle");
+ if (IS_ERR(hdata->sysreg)) {
+ DRM_ERROR("sysreg regmap lookup failed.\n");
+ ret = -EPROBE_DEFER;
+ goto err_hdmiphy;
+ }
+ }
+
pm_runtime_enable(dev);
ret = component_add(&pdev->dev, &hdmi_component_ops);
@@ -1975,8 +1908,7 @@ static int exynos_hdmi_suspend(struct device *dev)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
- clk_disable_unprepare(hdata->sclk_hdmi);
- clk_disable_unprepare(hdata->hdmi);
+ hdmi_clk_disable_gates(hdata);
return 0;
}
@@ -1986,17 +1918,9 @@ static int exynos_hdmi_resume(struct device *dev)
struct hdmi_context *hdata = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(hdata->hdmi);
- if (ret < 0) {
- DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
- return ret;
- }
- ret = clk_prepare_enable(hdata->sclk_hdmi);
- if (ret < 0) {
- DRM_ERROR("Failed to prepare_enable the sclk_mixer clk [%d]\n",
- ret);
+ ret = hdmi_clk_enable_gates(hdata);
+ if (ret < 0)
return ret;
- }
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 0a5a60005f7e..74a4269cc1b0 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -31,6 +31,7 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/component.h>
#include <drm/exynos_drm.h>
@@ -103,8 +104,6 @@ struct mixer_context {
struct mixer_resources mixer_res;
enum mixer_version_id mxr_ver;
- wait_queue_head_t wait_vsync_queue;
- atomic_t wait_vsync_event;
};
struct mixer_drv_data {
@@ -787,12 +786,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
-
- /* 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:
@@ -1027,34 +1020,6 @@ static void mixer_atomic_flush(struct exynos_drm_crtc *crtc)
mixer_vsync_set_update(mixer_ctx, true);
}
-static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
-{
- struct mixer_context *mixer_ctx = crtc->ctx;
- int err;
-
- if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
- return;
-
- err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
- if (err < 0) {
- DRM_DEBUG_KMS("failed to acquire vblank counter\n");
- return;
- }
-
- atomic_set(&mixer_ctx->wait_vsync_event, 1);
-
- /*
- * wait for MIXER to signal VSYNC interrupt or return after
- * timeout which is set to 50ms (refresh rate of 20).
- */
- if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
- !atomic_read(&mixer_ctx->wait_vsync_event),
- HZ/20))
- DRM_DEBUG_KMS("vblank wait timed out.\n");
-
- drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
-}
-
static void mixer_enable(struct exynos_drm_crtc *crtc)
{
struct mixer_context *ctx = crtc->ctx;
@@ -1065,6 +1030,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
pm_runtime_get_sync(ctx->dev);
+ exynos_drm_pipe_clk_enable(crtc, true);
+
mixer_vsync_set_update(ctx, false);
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
@@ -1094,6 +1061,8 @@ static void mixer_disable(struct exynos_drm_crtc *crtc)
for (i = 0; i < MIXER_WIN_NR; i++)
mixer_disable_plane(crtc, &ctx->planes[i]);
+ exynos_drm_pipe_clk_enable(crtc, false);
+
pm_runtime_put(ctx->dev);
clear_bit(MXR_BIT_POWERED, &ctx->flags);
@@ -1126,7 +1095,6 @@ static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
.disable = mixer_disable,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
- .wait_for_vblank = mixer_wait_for_vblank,
.atomic_begin = mixer_atomic_begin,
.update_plane = mixer_update_plane,
.disable_plane = mixer_disable_plane,
@@ -1155,18 +1123,6 @@ static struct mixer_drv_data exynos4210_mxr_drv_data = {
.has_sclk = 1,
};
-static const struct platform_device_id mixer_driver_types[] = {
- {
- .name = "s5p-mixer",
- .driver_data = (unsigned long)&exynos4210_mxr_drv_data,
- }, {
- .name = "exynos5-mixer",
- .driver_data = (unsigned long)&exynos5250_mxr_drv_data,
- }, {
- /* end node */
- }
-};
-
static struct of_device_id mixer_match_types[] = {
{
.compatible = "samsung,exynos4210-mixer",
@@ -1243,7 +1199,7 @@ static const struct component_ops mixer_component_ops = {
static int mixer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct mixer_drv_data *drv;
+ const struct mixer_drv_data *drv;
struct mixer_context *ctx;
int ret;
@@ -1253,23 +1209,13 @@ static int mixer_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (dev->of_node) {
- const struct of_device_id *match;
-
- match = of_match_node(mixer_match_types, dev->of_node);
- drv = (struct mixer_drv_data *)match->data;
- } else {
- drv = (struct mixer_drv_data *)
- platform_get_device_id(pdev)->driver_data;
- }
+ drv = of_device_get_match_data(dev);
ctx->pdev = pdev;
ctx->dev = dev;
ctx->vp_enabled = drv->is_vp_enabled;
ctx->has_sclk = drv->has_sclk;
ctx->mxr_ver = drv->version;
- init_waitqueue_head(&ctx->wait_vsync_queue);
- atomic_set(&ctx->wait_vsync_event, 0);
platform_set_drvdata(pdev, ctx);
@@ -1355,5 +1301,4 @@ struct platform_driver mixer_driver = {
},
.probe = mixer_probe,
.remove = mixer_remove,
- .id_table = mixer_driver_types,
};
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h
index 8c891e59be21..169667a22bdc 100644
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -586,10 +586,12 @@
#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070)
#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
+#define HDMI_TG_DECON_EN HDMI_TG_BASE(0x01e0)
/* HDMI PHY Registers Offsets*/
-#define HDMIPHY_POWER (0x74 >> 2)
-#define HDMIPHY_MODE_SET_DONE (0x7c >> 2)
+#define HDMIPHY_POWER 0x74
+#define HDMIPHY_MODE_SET_DONE 0x7c
+#define HDMIPHY5433_MODE_SET_DONE 0x84
/* HDMI PHY Values */
#define HDMI_PHY_POWER_ON 0x80
@@ -603,4 +605,7 @@
#define PMU_HDMI_PHY_CONTROL 0x700
#define PMU_HDMI_PHY_ENABLE_BIT BIT(0)
+#define EXYNOS5433_SYSREG_DISP_HDMI_PHY 0x1008
+#define SYSREG_HDMI_REFCLK_INT_CLK 1
+
#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/gpu/drm/fsl-dcu/Makefile b/drivers/gpu/drm/fsl-dcu/Makefile
index 6ea1523ae6ec..b35a292287f3 100644
--- a/drivers/gpu/drm/fsl-dcu/Makefile
+++ b/drivers/gpu/drm/fsl-dcu/Makefile
@@ -3,5 +3,6 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
fsl_dcu_drm_rgb.o \
fsl_dcu_drm_plane.o \
fsl_dcu_drm_crtc.o \
- fsl_dcu_drm_fbdev.o
+ fsl_dcu_drm_fbdev.o \
+ fsl_tcon.o
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
index 4ed7798533f9..365809edf29a 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
@@ -67,12 +67,10 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
struct drm_display_mode *mode = &crtc->state->mode;
- unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index, pol = 0;
- unsigned long dcuclk;
+ unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;
index = drm_crtc_index(crtc);
- dcuclk = clk_get_rate(fsl_dev->clk);
- div = dcuclk / mode->clock / 1000;
+ clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);
/* Configure timings: */
hbp = mode->htotal - mode->hsync_end;
@@ -99,7 +97,6 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
- regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div);
regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
DCU_BGND_G(0) | DCU_BGND_B(0));
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
index e8d9337a66d8..44f6f262d75a 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
@@ -23,10 +23,12 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include "fsl_dcu_drm_crtc.h"
#include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -62,46 +64,55 @@ static int fsl_dcu_drm_irq_init(struct drm_device *dev)
return ret;
}
-static int fsl_dcu_load(struct drm_device *drm, unsigned long flags)
+static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
{
- struct device *dev = drm->dev;
- struct fsl_dcu_drm_device *fsl_dev = drm->dev_private;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
int ret;
ret = fsl_dcu_drm_modeset_init(fsl_dev);
if (ret < 0) {
- dev_err(dev, "failed to initialize mode setting\n");
+ dev_err(dev->dev, "failed to initialize mode setting\n");
return ret;
}
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret < 0) {
- dev_err(dev, "failed to initialize vblank\n");
+ dev_err(dev->dev, "failed to initialize vblank\n");
goto done;
}
- drm->vblank_disable_allowed = true;
+ dev->vblank_disable_allowed = true;
- ret = fsl_dcu_drm_irq_init(drm);
+ ret = fsl_dcu_drm_irq_init(dev);
if (ret < 0)
goto done;
- drm->irq_enabled = true;
+ dev->irq_enabled = true;
- fsl_dcu_fbdev_init(drm);
+ fsl_dcu_fbdev_init(dev);
return 0;
done:
- if (ret) {
- drm_mode_config_cleanup(drm);
- drm_vblank_cleanup(drm);
- drm_irq_uninstall(drm);
- drm->dev_private = NULL;
- }
+ drm_kms_helper_poll_fini(dev);
+
+ if (fsl_dev->fbdev)
+ drm_fbdev_cma_fini(fsl_dev->fbdev);
+
+ drm_mode_config_cleanup(dev);
+ drm_vblank_cleanup(dev);
+ drm_irq_uninstall(dev);
+ dev->dev_private = NULL;
return ret;
}
static int fsl_dcu_unload(struct drm_device *dev)
{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ drm_kms_helper_poll_fini(dev);
+
+ if (fsl_dev->fbdev)
+ drm_fbdev_cma_fini(fsl_dev->fbdev);
+
drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);
drm_irq_uninstall(dev);
@@ -157,6 +168,13 @@ static void fsl_dcu_drm_disable_vblank(struct drm_device *dev,
regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
}
+static void fsl_dcu_drm_lastclose(struct drm_device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
+}
+
static const struct file_operations fsl_dcu_drm_fops = {
.owner = THIS_MODULE,
.open = drm_open,
@@ -174,6 +192,7 @@ static const struct file_operations fsl_dcu_drm_fops = {
static struct drm_driver fsl_dcu_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
| DRIVER_PRIME | DRIVER_ATOMIC,
+ .lastclose = fsl_dcu_drm_lastclose,
.load = fsl_dcu_load,
.unload = fsl_dcu_unload,
.irq_handler = fsl_dcu_drm_irq,
@@ -197,9 +216,9 @@ static struct drm_driver fsl_dcu_drm_driver = {
.fops = &fsl_dcu_drm_fops,
.name = "fsl-dcu-drm",
.desc = "Freescale DCU DRM",
- .date = "20150213",
+ .date = "20160425",
.major = 1,
- .minor = 0,
+ .minor = 1,
};
#ifdef CONFIG_PM_SLEEP
@@ -283,6 +302,9 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
struct drm_driver *driver = &fsl_dcu_drm_driver;
+ struct clk *pix_clk_in;
+ char pix_clk_name[32];
+ const char *pix_clk_in_name;
const struct of_device_id *id;
int ret;
@@ -290,6 +312,11 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
if (!fsl_dev)
return -ENOMEM;
+ id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
+ if (!id)
+ return -ENODEV;
+ fsl_dev->soc = id->data;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "could not get memory IO resource\n");
@@ -308,39 +335,54 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
return -ENXIO;
}
+ fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
+ &fsl_dcu_regmap_config);
+ if (IS_ERR(fsl_dev->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(fsl_dev->regmap);
+ }
+
fsl_dev->clk = devm_clk_get(dev, "dcu");
if (IS_ERR(fsl_dev->clk)) {
- ret = PTR_ERR(fsl_dev->clk);
dev_err(dev, "failed to get dcu clock\n");
- return ret;
- }
- ret = clk_prepare(fsl_dev->clk);
- if (ret < 0) {
- dev_err(dev, "failed to prepare dcu clk\n");
- return ret;
+ return PTR_ERR(fsl_dev->clk);
}
- ret = clk_enable(fsl_dev->clk);
+ ret = clk_prepare_enable(fsl_dev->clk);
if (ret < 0) {
dev_err(dev, "failed to enable dcu clk\n");
- clk_unprepare(fsl_dev->clk);
return ret;
}
- fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
- &fsl_dcu_regmap_config);
- if (IS_ERR(fsl_dev->regmap)) {
- dev_err(dev, "regmap init failed\n");
- return PTR_ERR(fsl_dev->regmap);
+ pix_clk_in = devm_clk_get(dev, "pix");
+ if (IS_ERR(pix_clk_in)) {
+ /* legancy binding, use dcu clock as pixel clock input */
+ pix_clk_in = fsl_dev->clk;
}
- id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
- if (!id)
- return -ENODEV;
- fsl_dev->soc = id->data;
+ pix_clk_in_name = __clk_get_name(pix_clk_in);
+ snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
+ fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
+ pix_clk_in_name, 0, base + DCU_DIV_RATIO,
+ 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
+ if (IS_ERR(fsl_dev->pix_clk)) {
+ dev_err(dev, "failed to register pix clk\n");
+ ret = PTR_ERR(fsl_dev->pix_clk);
+ goto disable_clk;
+ }
+
+ ret = clk_prepare_enable(fsl_dev->pix_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable pix clk\n");
+ goto unregister_pix_clk;
+ }
+
+ fsl_dev->tcon = fsl_tcon_init(dev);
drm = drm_dev_alloc(driver, dev);
- if (!drm)
- return -ENOMEM;
+ if (!drm) {
+ ret = -ENOMEM;
+ goto disable_pix_clk;
+ }
fsl_dev->dev = dev;
fsl_dev->drm = drm;
@@ -360,6 +402,12 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
unref:
drm_dev_unref(drm);
+disable_pix_clk:
+ clk_disable_unprepare(fsl_dev->pix_clk);
+unregister_pix_clk:
+ clk_unregister(fsl_dev->pix_clk);
+disable_clk:
+ clk_disable_unprepare(fsl_dev->clk);
return ret;
}
@@ -367,6 +415,9 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev)
{
struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
+ clk_disable_unprepare(fsl_dev->clk);
+ clk_disable_unprepare(fsl_dev->pix_clk);
+ clk_unregister(fsl_dev->pix_clk);
drm_put_dev(fsl_dev->drm);
return 0;
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
index 6413ac9e4769..5bb7c261fe95 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
@@ -183,6 +183,8 @@ struct fsl_dcu_drm_device {
struct regmap *regmap;
int irq;
struct clk *clk;
+ struct clk *pix_clk;
+ struct fsl_tcon *tcon;
/*protects hardware register*/
spinlock_t irq_lock;
struct drm_device *drm;
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index 8780deba5e8a..98c998da91eb 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -17,6 +17,7 @@
#include <drm/drm_panel.h>
#include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
static int
fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
@@ -28,10 +29,20 @@ fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder)
{
+ struct drm_device *dev = encoder->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ if (fsl_dev->tcon)
+ fsl_tcon_bypass_disable(fsl_dev->tcon);
}
static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder)
{
+ struct drm_device *dev = encoder->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ if (fsl_dev->tcon)
+ fsl_tcon_bypass_enable(fsl_dev->tcon);
}
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
@@ -68,7 +79,10 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector)
{
+ struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
+
drm_connector_unregister(connector);
+ drm_panel_detach(fsl_con->panel);
drm_connector_cleanup(connector);
}
@@ -131,7 +145,7 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev,
struct drm_encoder *encoder)
{
struct drm_connector *connector = &fsl_dev->connector.base;
- struct drm_mode_config mode_config = fsl_dev->drm->mode_config;
+ struct drm_mode_config *mode_config = &fsl_dev->drm->mode_config;
struct device_node *panel_node;
int ret;
@@ -153,19 +167,23 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev,
goto err_sysfs;
drm_object_property_set_value(&connector->base,
- mode_config.dpms_property,
+ mode_config->dpms_property,
DRM_MODE_DPMS_OFF);
panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0);
- if (panel_node) {
- fsl_dev->connector.panel = of_drm_find_panel(panel_node);
- if (!fsl_dev->connector.panel) {
- ret = -EPROBE_DEFER;
- goto err_sysfs;
- }
- of_node_put(panel_node);
+ if (!panel_node) {
+ dev_err(fsl_dev->dev, "fsl,panel property not found\n");
+ ret = -ENODEV;
+ goto err_sysfs;
}
+ fsl_dev->connector.panel = of_drm_find_panel(panel_node);
+ if (!fsl_dev->connector.panel) {
+ ret = -EPROBE_DEFER;
+ goto err_panel;
+ }
+ of_node_put(panel_node);
+
ret = drm_panel_attach(fsl_dev->connector.panel, connector);
if (ret) {
dev_err(fsl_dev->dev, "failed to attach panel\n");
@@ -174,6 +192,8 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev,
return 0;
+err_panel:
+ of_node_put(panel_node);
err_sysfs:
drm_connector_unregister(connector);
err_cleanup:
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
new file mode 100644
index 000000000000..bbe34f1c0505
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "fsl_tcon.h"
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
+{
+ regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+ FSL_TCON_CTRL1_TCON_BYPASS, 0);
+}
+
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
+{
+ regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+ FSL_TCON_CTRL1_TCON_BYPASS,
+ FSL_TCON_CTRL1_TCON_BYPASS);
+}
+
+static struct regmap_config fsl_tcon_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .name = "tcon",
+};
+
+static int fsl_tcon_init_regmap(struct device *dev,
+ struct fsl_tcon *tcon,
+ struct device_node *np)
+{
+ struct resource res;
+ void __iomem *regs;
+
+ if (of_address_to_resource(np, 0, &res))
+ return -EINVAL;
+
+ regs = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ tcon->regs = devm_regmap_init_mmio(dev, regs,
+ &fsl_tcon_regmap_config);
+ if (IS_ERR(tcon->regs))
+ return PTR_ERR(tcon->regs);
+
+ return 0;
+}
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev)
+{
+ struct fsl_tcon *tcon;
+ struct device_node *np;
+ int ret;
+
+ /* TCON node is not mandatory, some devices do not provide TCON */
+ np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
+ if (!np)
+ return NULL;
+
+ tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
+ if (!tcon) {
+ ret = -ENOMEM;
+ goto err_node_put;
+ }
+
+ ret = fsl_tcon_init_regmap(dev, tcon, np);
+ if (ret) {
+ dev_err(dev, "Couldn't create the TCON regmap\n");
+ goto err_node_put;
+ }
+
+ tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
+ if (IS_ERR(tcon->ipg_clk)) {
+ dev_err(dev, "Couldn't get the TCON bus clock\n");
+ goto err_node_put;
+ }
+
+ clk_prepare_enable(tcon->ipg_clk);
+
+ dev_info(dev, "Using TCON in bypass mode\n");
+
+ return tcon;
+
+err_node_put:
+ of_node_put(np);
+ return NULL;
+}
+
+void fsl_tcon_free(struct fsl_tcon *tcon)
+{
+ clk_disable_unprepare(tcon->ipg_clk);
+ clk_put(tcon->ipg_clk);
+}
+
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.h b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
new file mode 100644
index 000000000000..80a7617de58f
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * 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 __FSL_TCON_H__
+#define __FSL_TCON_H__
+
+#include <linux/bitops.h>
+
+#define FSL_TCON_CTRL1 0x0
+#define FSL_TCON_CTRL1_TCON_BYPASS BIT(29)
+
+struct fsl_tcon {
+ struct regmap *regs;
+ struct clk *ipg_clk;
+};
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev);
+void fsl_tcon_free(struct fsl_tcon *tcon);
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon);
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon);
+
+#endif /* __FSL_TCON_H__ */
diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig
new file mode 100644
index 000000000000..558c61b1b8e8
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/Kconfig
@@ -0,0 +1,5 @@
+#
+# hisilicon drm device configuration.
+# Please keep this list sorted alphabetically
+
+source "drivers/gpu/drm/hisilicon/kirin/Kconfig"
diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile
new file mode 100644
index 000000000000..e3f6d493c996
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for hisilicon drm drivers.
+# Please keep this list sorted alphabetically
+
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/
diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig
new file mode 100644
index 000000000000..ea0df6115f7e
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig
@@ -0,0 +1,18 @@
+config DRM_HISI_KIRIN
+ tristate "DRM Support for Hisilicon Kirin series SoCs Platform"
+ depends on DRM && OF && ARM64
+ select DRM_KMS_HELPER
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ help
+ Choose this option if you have a hisilicon Kirin chipsets(hi6220).
+ If M is selected the module will be called kirin-drm.
+
+config HISI_KIRIN_DW_DSI
+ tristate "HiSilicon Kirin specific extensions for Synopsys DW MIPI DSI"
+ depends on DRM_HISI_KIRIN
+ select DRM_MIPI_DSI
+ help
+ This selects support for HiSilicon Kirin SoC specific extensions for
+ the Synopsys DesignWare DSI driver. If you want to enable MIPI DSI on
+ hi6220 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile
new file mode 100644
index 000000000000..cdf61589485c
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/Makefile
@@ -0,0 +1,6 @@
+kirin-drm-y := kirin_drm_drv.o \
+ kirin_drm_ade.o
+
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
+
+obj-$(CONFIG_HISI_KIRIN_DW_DSI) += dw_drm_dsi.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
new file mode 100644
index 000000000000..bfbc2159250d
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
@@ -0,0 +1,857 @@
+/*
+ * DesignWare MIPI DSI Host Controller v1.02 driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * Author:
+ * Xinliang Liu <z.liuxinliang@hisilicon.com>
+ * Xinliang Liu <xinliang.liu@linaro.org>
+ * Xinwei Kong <kong.kongxinwei@hisilicon.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/component.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_of.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "dw_dsi_reg.h"
+
+#define MAX_TX_ESC_CLK 10
+#define ROUND(x, y) ((x) / (y) + \
+ ((x) % (y) * 10 / (y) >= 5 ? 1 : 0))
+#define PHY_REF_CLK_RATE 19200000
+#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000))
+
+#define encoder_to_dsi(encoder) \
+ container_of(encoder, struct dw_dsi, encoder)
+#define host_to_dsi(host) \
+ container_of(host, struct dw_dsi, host)
+
+struct mipi_phy_params {
+ u32 clk_t_lpx;
+ u32 clk_t_hs_prepare;
+ u32 clk_t_hs_zero;
+ u32 clk_t_hs_trial;
+ u32 clk_t_wakeup;
+ u32 data_t_lpx;
+ u32 data_t_hs_prepare;
+ u32 data_t_hs_zero;
+ u32 data_t_hs_trial;
+ u32 data_t_ta_go;
+ u32 data_t_ta_get;
+ u32 data_t_wakeup;
+ u32 hstx_ckg_sel;
+ u32 pll_fbd_div5f;
+ u32 pll_fbd_div1f;
+ u32 pll_fbd_2p;
+ u32 pll_enbwt;
+ u32 pll_fbd_p;
+ u32 pll_fbd_s;
+ u32 pll_pre_div1p;
+ u32 pll_pre_p;
+ u32 pll_vco_750M;
+ u32 pll_lpf_rs;
+ u32 pll_lpf_cs;
+ u32 clklp2hs_time;
+ u32 clkhs2lp_time;
+ u32 lp2hs_time;
+ u32 hs2lp_time;
+ u32 clk_to_data_delay;
+ u32 data_to_clk_delay;
+ u32 lane_byte_clk_kHz;
+ u32 clk_division;
+};
+
+struct dsi_hw_ctx {
+ void __iomem *base;
+ struct clk *pclk;
+};
+
+struct dw_dsi {
+ struct drm_encoder encoder;
+ struct drm_bridge *bridge;
+ struct mipi_dsi_host host;
+ struct drm_display_mode cur_mode;
+ struct dsi_hw_ctx *ctx;
+ struct mipi_phy_params phy;
+
+ u32 lanes;
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+ bool enable;
+};
+
+struct dsi_data {
+ struct dw_dsi dsi;
+ struct dsi_hw_ctx ctx;
+};
+
+struct dsi_phy_range {
+ u32 min_range_kHz;
+ u32 max_range_kHz;
+ u32 pll_vco_750M;
+ u32 hstx_ckg_sel;
+};
+
+static const struct dsi_phy_range dphy_range_info[] = {
+ { 46875, 62500, 1, 7 },
+ { 62500, 93750, 0, 7 },
+ { 93750, 125000, 1, 6 },
+ { 125000, 187500, 0, 6 },
+ { 187500, 250000, 1, 5 },
+ { 250000, 375000, 0, 5 },
+ { 375000, 500000, 1, 4 },
+ { 500000, 750000, 0, 4 },
+ { 750000, 1000000, 1, 0 },
+ { 1000000, 1500000, 0, 0 }
+};
+
+static u32 dsi_calc_phy_rate(u32 req_kHz, struct mipi_phy_params *phy)
+{
+ u32 ref_clk_ps = PHY_REF_CLK_PERIOD_PS;
+ u32 tmp_kHz = req_kHz;
+ u32 i = 0;
+ u32 q_pll = 1;
+ u32 m_pll = 0;
+ u32 n_pll = 0;
+ u32 r_pll = 1;
+ u32 m_n = 0;
+ u32 m_n_int = 0;
+ u32 f_kHz = 0;
+ u64 temp;
+
+ /*
+ * Find a rate >= req_kHz.
+ */
+ do {
+ f_kHz = tmp_kHz;
+
+ for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++)
+ if (f_kHz >= dphy_range_info[i].min_range_kHz &&
+ f_kHz <= dphy_range_info[i].max_range_kHz)
+ break;
+
+ if (i == ARRAY_SIZE(dphy_range_info)) {
+ DRM_ERROR("%dkHz out of range\n", f_kHz);
+ return 0;
+ }
+
+ phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M;
+ phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel;
+
+ if (phy->hstx_ckg_sel <= 7 &&
+ phy->hstx_ckg_sel >= 4)
+ q_pll = 0x10 >> (7 - phy->hstx_ckg_sel);
+
+ temp = f_kHz * (u64)q_pll * (u64)ref_clk_ps;
+ m_n_int = temp / (u64)1000000000;
+ m_n = (temp % (u64)1000000000) / (u64)100000000;
+
+ if (m_n_int % 2 == 0) {
+ if (m_n * 6 >= 50) {
+ n_pll = 2;
+ m_pll = (m_n_int + 1) * n_pll;
+ } else if (m_n * 6 >= 30) {
+ n_pll = 3;
+ m_pll = m_n_int * n_pll + 2;
+ } else {
+ n_pll = 1;
+ m_pll = m_n_int * n_pll;
+ }
+ } else {
+ if (m_n * 6 >= 50) {
+ n_pll = 1;
+ m_pll = (m_n_int + 1) * n_pll;
+ } else if (m_n * 6 >= 30) {
+ n_pll = 1;
+ m_pll = (m_n_int + 1) * n_pll;
+ } else if (m_n * 6 >= 10) {
+ n_pll = 3;
+ m_pll = m_n_int * n_pll + 1;
+ } else {
+ n_pll = 2;
+ m_pll = m_n_int * n_pll;
+ }
+ }
+
+ if (n_pll == 1) {
+ phy->pll_fbd_p = 0;
+ phy->pll_pre_div1p = 1;
+ } else {
+ phy->pll_fbd_p = n_pll;
+ phy->pll_pre_div1p = 0;
+ }
+
+ if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4)
+ r_pll = 0x10 >> (7 - phy->pll_fbd_2p);
+
+ if (m_pll == 2) {
+ phy->pll_pre_p = 0;
+ phy->pll_fbd_s = 0;
+ phy->pll_fbd_div1f = 0;
+ phy->pll_fbd_div5f = 1;
+ } else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) {
+ phy->pll_pre_p = m_pll / (2 * r_pll);
+ phy->pll_fbd_s = 0;
+ phy->pll_fbd_div1f = 1;
+ phy->pll_fbd_div5f = 0;
+ } else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) {
+ if (((m_pll / (2 * r_pll)) % 2) == 0) {
+ phy->pll_pre_p =
+ (m_pll / (2 * r_pll)) / 2 - 1;
+ phy->pll_fbd_s =
+ (m_pll / (2 * r_pll)) % 2 + 2;
+ } else {
+ phy->pll_pre_p =
+ (m_pll / (2 * r_pll)) / 2;
+ phy->pll_fbd_s =
+ (m_pll / (2 * r_pll)) % 2;
+ }
+ phy->pll_fbd_div1f = 0;
+ phy->pll_fbd_div5f = 0;
+ } else {
+ phy->pll_pre_p = 0;
+ phy->pll_fbd_s = 0;
+ phy->pll_fbd_div1f = 0;
+ phy->pll_fbd_div5f = 1;
+ }
+
+ f_kHz = (u64)1000000000 * (u64)m_pll /
+ ((u64)ref_clk_ps * (u64)n_pll * (u64)q_pll);
+
+ if (f_kHz >= req_kHz)
+ break;
+
+ tmp_kHz += 10;
+
+ } while (true);
+
+ return f_kHz;
+}
+
+static void dsi_get_phy_params(u32 phy_req_kHz,
+ struct mipi_phy_params *phy)
+{
+ u32 ref_clk_ps = PHY_REF_CLK_PERIOD_PS;
+ u32 phy_rate_kHz;
+ u32 ui;
+
+ memset(phy, 0, sizeof(*phy));
+
+ phy_rate_kHz = dsi_calc_phy_rate(phy_req_kHz, phy);
+ if (!phy_rate_kHz)
+ return;
+
+ ui = 1000000 / phy_rate_kHz;
+
+ phy->clk_t_lpx = ROUND(50, 8 * ui);
+ phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1;
+
+ phy->clk_t_hs_zero = ROUND(262, 8 * ui);
+ phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1);
+ phy->clk_t_wakeup = ROUND(1000000, (ref_clk_ps / 1000) - 1);
+ if (phy->clk_t_wakeup > 0xff)
+ phy->clk_t_wakeup = 0xff;
+ phy->data_t_wakeup = phy->clk_t_wakeup;
+ phy->data_t_lpx = phy->clk_t_lpx;
+ phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1;
+ phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui);
+ phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1);
+ phy->data_t_ta_go = 3;
+ phy->data_t_ta_get = 4;
+
+ phy->pll_enbwt = 1;
+ phy->clklp2hs_time = ROUND(407, 8 * ui) + 12;
+ phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui);
+ phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1;
+ phy->hs2lp_time = phy->clkhs2lp_time;
+ phy->clk_to_data_delay = 1 + phy->clklp2hs_time;
+ phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) +
+ phy->clkhs2lp_time;
+
+ phy->lane_byte_clk_kHz = phy_rate_kHz / 8;
+ phy->clk_division =
+ DIV_ROUND_UP(phy->lane_byte_clk_kHz, MAX_TX_ESC_CLK);
+}
+
+static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format)
+{
+ u32 val;
+
+ /*
+ * TODO: only support RGB888 now, to support more
+ */
+ switch (format) {
+ case MIPI_DSI_FMT_RGB888:
+ val = DSI_24BITS_1;
+ break;
+ default:
+ val = DSI_24BITS_1;
+ break;
+ }
+
+ return val;
+}
+
+/*
+ * dsi phy reg write function
+ */
+static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val)
+{
+ u32 reg_write = 0x10000 + reg;
+
+ /*
+ * latch reg first
+ */
+ writel(reg_write, base + PHY_TST_CTRL1);
+ writel(0x02, base + PHY_TST_CTRL0);
+ writel(0x00, base + PHY_TST_CTRL0);
+
+ /*
+ * then latch value
+ */
+ writel(val, base + PHY_TST_CTRL1);
+ writel(0x02, base + PHY_TST_CTRL0);
+ writel(0x00, base + PHY_TST_CTRL0);
+}
+
+static void dsi_set_phy_timer(void __iomem *base,
+ struct mipi_phy_params *phy,
+ u32 lanes)
+{
+ u32 val;
+
+ /*
+ * Set lane value and phy stop wait time.
+ */
+ val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8);
+ writel(val, base + PHY_IF_CFG);
+
+ /*
+ * Set phy clk division.
+ */
+ val = readl(base + CLKMGR_CFG) | phy->clk_division;
+ writel(val, base + CLKMGR_CFG);
+
+ /*
+ * Set lp and hs switching params.
+ */
+ dw_update_bits(base + PHY_TMR_CFG, 24, MASK(8), phy->hs2lp_time);
+ dw_update_bits(base + PHY_TMR_CFG, 16, MASK(8), phy->lp2hs_time);
+ dw_update_bits(base + PHY_TMR_LPCLK_CFG, 16, MASK(10),
+ phy->clkhs2lp_time);
+ dw_update_bits(base + PHY_TMR_LPCLK_CFG, 0, MASK(10),
+ phy->clklp2hs_time);
+ dw_update_bits(base + CLK_DATA_TMR_CFG, 8, MASK(8),
+ phy->data_to_clk_delay);
+ dw_update_bits(base + CLK_DATA_TMR_CFG, 0, MASK(8),
+ phy->clk_to_data_delay);
+}
+
+static void dsi_set_mipi_phy(void __iomem *base,
+ struct mipi_phy_params *phy,
+ u32 lanes)
+{
+ u32 delay_count;
+ u32 val;
+ u32 i;
+
+ /* phy timer setting */
+ dsi_set_phy_timer(base, phy, lanes);
+
+ /*
+ * Reset to clean up phy tst params.
+ */
+ writel(0, base + PHY_RSTZ);
+ writel(0, base + PHY_TST_CTRL0);
+ writel(1, base + PHY_TST_CTRL0);
+ writel(0, base + PHY_TST_CTRL0);
+
+ /*
+ * Clock lane timing control setting: TLPX, THS-PREPARE,
+ * THS-ZERO, THS-TRAIL, TWAKEUP.
+ */
+ dsi_phy_tst_set(base, CLK_TLPX, phy->clk_t_lpx);
+ dsi_phy_tst_set(base, CLK_THS_PREPARE, phy->clk_t_hs_prepare);
+ dsi_phy_tst_set(base, CLK_THS_ZERO, phy->clk_t_hs_zero);
+ dsi_phy_tst_set(base, CLK_THS_TRAIL, phy->clk_t_hs_trial);
+ dsi_phy_tst_set(base, CLK_TWAKEUP, phy->clk_t_wakeup);
+
+ /*
+ * Data lane timing control setting: TLPX, THS-PREPARE,
+ * THS-ZERO, THS-TRAIL, TTA-GO, TTA-GET, TWAKEUP.
+ */
+ for (i = 0; i < lanes; i++) {
+ dsi_phy_tst_set(base, DATA_TLPX(i), phy->data_t_lpx);
+ dsi_phy_tst_set(base, DATA_THS_PREPARE(i),
+ phy->data_t_hs_prepare);
+ dsi_phy_tst_set(base, DATA_THS_ZERO(i), phy->data_t_hs_zero);
+ dsi_phy_tst_set(base, DATA_THS_TRAIL(i), phy->data_t_hs_trial);
+ dsi_phy_tst_set(base, DATA_TTA_GO(i), phy->data_t_ta_go);
+ dsi_phy_tst_set(base, DATA_TTA_GET(i), phy->data_t_ta_get);
+ dsi_phy_tst_set(base, DATA_TWAKEUP(i), phy->data_t_wakeup);
+ }
+
+ /*
+ * physical configuration: I, pll I, pll II, pll III,
+ * pll IV, pll V.
+ */
+ dsi_phy_tst_set(base, PHY_CFG_I, phy->hstx_ckg_sel);
+ val = (phy->pll_fbd_div5f << 5) + (phy->pll_fbd_div1f << 4) +
+ (phy->pll_fbd_2p << 1) + phy->pll_enbwt;
+ dsi_phy_tst_set(base, PHY_CFG_PLL_I, val);
+ dsi_phy_tst_set(base, PHY_CFG_PLL_II, phy->pll_fbd_p);
+ dsi_phy_tst_set(base, PHY_CFG_PLL_III, phy->pll_fbd_s);
+ val = (phy->pll_pre_div1p << 7) + phy->pll_pre_p;
+ dsi_phy_tst_set(base, PHY_CFG_PLL_IV, val);
+ val = (5 << 5) + (phy->pll_vco_750M << 4) + (phy->pll_lpf_rs << 2) +
+ phy->pll_lpf_cs;
+ dsi_phy_tst_set(base, PHY_CFG_PLL_V, val);
+
+ writel(PHY_ENABLECLK, base + PHY_RSTZ);
+ udelay(1);
+ writel(PHY_ENABLECLK | PHY_UNSHUTDOWNZ, base + PHY_RSTZ);
+ udelay(1);
+ writel(PHY_ENABLECLK | PHY_UNRSTZ | PHY_UNSHUTDOWNZ, base + PHY_RSTZ);
+ usleep_range(1000, 1500);
+
+ /*
+ * wait for phy's clock ready
+ */
+ delay_count = 100;
+ while (delay_count--) {
+ val = readl(base + PHY_STATUS);
+ if ((BIT(0) | BIT(2)) & val)
+ break;
+
+ udelay(1);
+ }
+
+ if (!delay_count)
+ DRM_INFO("phylock and phystopstateclklane is not ready.\n");
+}
+
+static void dsi_set_mode_timing(void __iomem *base,
+ u32 lane_byte_clk_kHz,
+ struct drm_display_mode *mode,
+ enum mipi_dsi_pixel_format format)
+{
+ u32 hfp, hbp, hsw, vfp, vbp, vsw;
+ u32 hline_time;
+ u32 hsa_time;
+ u32 hbp_time;
+ u32 pixel_clk_kHz;
+ int htot, vtot;
+ u32 val;
+ u64 tmp;
+
+ val = dsi_get_dpi_color_coding(format);
+ writel(val, base + DPI_COLOR_CODING);
+
+ val = (mode->flags & DRM_MODE_FLAG_NHSYNC ? 1 : 0) << 2;
+ val |= (mode->flags & DRM_MODE_FLAG_NVSYNC ? 1 : 0) << 1;
+ writel(val, base + DPI_CFG_POL);
+
+ /*
+ * The DSI IP accepts vertical timing using lines as normal,
+ * but horizontal timing is a mixture of pixel-clocks for the
+ * active region and byte-lane clocks for the blanking-related
+ * timings. hfp is specified as the total hline_time in byte-
+ * lane clocks minus hsa, hbp and active.
+ */
+ pixel_clk_kHz = mode->clock;
+ htot = mode->htotal;
+ vtot = mode->vtotal;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hbp = mode->htotal - mode->hsync_end;
+ hsw = mode->hsync_end - mode->hsync_start;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+ vsw = mode->vsync_end - mode->vsync_start;
+ if (vsw > 15) {
+ DRM_DEBUG_DRIVER("vsw exceeded 15\n");
+ vsw = 15;
+ }
+
+ hsa_time = (hsw * lane_byte_clk_kHz) / pixel_clk_kHz;
+ hbp_time = (hbp * lane_byte_clk_kHz) / pixel_clk_kHz;
+ tmp = (u64)htot * (u64)lane_byte_clk_kHz;
+ hline_time = DIV_ROUND_UP(tmp, pixel_clk_kHz);
+
+ /* all specified in byte-lane clocks */
+ writel(hsa_time, base + VID_HSA_TIME);
+ writel(hbp_time, base + VID_HBP_TIME);
+ writel(hline_time, base + VID_HLINE_TIME);
+
+ writel(vsw, base + VID_VSA_LINES);
+ writel(vbp, base + VID_VBP_LINES);
+ writel(vfp, base + VID_VFP_LINES);
+ writel(mode->vdisplay, base + VID_VACTIVE_LINES);
+ writel(mode->hdisplay, base + VID_PKT_SIZE);
+
+ DRM_DEBUG_DRIVER("htot=%d, hfp=%d, hbp=%d, hsw=%d\n",
+ htot, hfp, hbp, hsw);
+ DRM_DEBUG_DRIVER("vtol=%d, vfp=%d, vbp=%d, vsw=%d\n",
+ vtot, vfp, vbp, vsw);
+ DRM_DEBUG_DRIVER("hsa_time=%d, hbp_time=%d, hline_time=%d\n",
+ hsa_time, hbp_time, hline_time);
+}
+
+static void dsi_set_video_mode(void __iomem *base, unsigned long flags)
+{
+ u32 val;
+ u32 mode_mask = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+ u32 non_burst_sync_pulse = MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+ u32 non_burst_sync_event = MIPI_DSI_MODE_VIDEO;
+
+ /*
+ * choose video mode type
+ */
+ if ((flags & mode_mask) == non_burst_sync_pulse)
+ val = DSI_NON_BURST_SYNC_PULSES;
+ else if ((flags & mode_mask) == non_burst_sync_event)
+ val = DSI_NON_BURST_SYNC_EVENTS;
+ else
+ val = DSI_BURST_SYNC_PULSES_1;
+ writel(val, base + VID_MODE_CFG);
+
+ writel(PHY_TXREQUESTCLKHS, base + LPCLK_CTRL);
+ writel(DSI_VIDEO_MODE, base + MODE_CFG);
+}
+
+static void dsi_mipi_init(struct dw_dsi *dsi)
+{
+ struct dsi_hw_ctx *ctx = dsi->ctx;
+ struct mipi_phy_params *phy = &dsi->phy;
+ struct drm_display_mode *mode = &dsi->cur_mode;
+ u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+ void __iomem *base = ctx->base;
+ u32 dphy_req_kHz;
+
+ /*
+ * count phy params
+ */
+ dphy_req_kHz = mode->clock * bpp / dsi->lanes;
+ dsi_get_phy_params(dphy_req_kHz, phy);
+
+ /* reset Core */
+ writel(RESET, base + PWR_UP);
+
+ /* set dsi phy params */
+ dsi_set_mipi_phy(base, phy, dsi->lanes);
+
+ /* set dsi mode timing */
+ dsi_set_mode_timing(base, phy->lane_byte_clk_kHz, mode, dsi->format);
+
+ /* set dsi video mode */
+ dsi_set_video_mode(base, dsi->mode_flags);
+
+ /* dsi wake up */
+ writel(POWERUP, base + PWR_UP);
+
+ DRM_DEBUG_DRIVER("lanes=%d, pixel_clk=%d kHz, bytes_freq=%d kHz\n",
+ dsi->lanes, mode->clock, phy->lane_byte_clk_kHz);
+}
+
+static void dsi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct dw_dsi *dsi = encoder_to_dsi(encoder);
+ struct dsi_hw_ctx *ctx = dsi->ctx;
+ void __iomem *base = ctx->base;
+
+ if (!dsi->enable)
+ return;
+
+ writel(0, base + PWR_UP);
+ writel(0, base + LPCLK_CTRL);
+ writel(0, base + PHY_RSTZ);
+ clk_disable_unprepare(ctx->pclk);
+
+ dsi->enable = false;
+}
+
+static void dsi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct dw_dsi *dsi = encoder_to_dsi(encoder);
+ struct dsi_hw_ctx *ctx = dsi->ctx;
+ int ret;
+
+ if (dsi->enable)
+ return;
+
+ ret = clk_prepare_enable(ctx->pclk);
+ if (ret) {
+ DRM_ERROR("fail to enable pclk: %d\n", ret);
+ return;
+ }
+
+ dsi_mipi_init(dsi);
+
+ dsi->enable = true;
+}
+
+static void dsi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct dw_dsi *dsi = encoder_to_dsi(encoder);
+
+ drm_mode_copy(&dsi->cur_mode, adj_mode);
+}
+
+static int dsi_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ /* do nothing */
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = {
+ .atomic_check = dsi_encoder_atomic_check,
+ .mode_set = dsi_encoder_mode_set,
+ .enable = dsi_encoder_enable,
+ .disable = dsi_encoder_disable
+};
+
+static const struct drm_encoder_funcs dw_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static int dw_drm_encoder_init(struct device *dev,
+ struct drm_device *drm_dev,
+ struct drm_encoder *encoder)
+{
+ int ret;
+ u32 crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+ if (!crtc_mask) {
+ DRM_ERROR("failed to find crtc mask\n");
+ return -EINVAL;
+ }
+
+ encoder->possible_crtcs = crtc_mask;
+ ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, NULL);
+ if (ret) {
+ DRM_ERROR("failed to init dsi encoder\n");
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs);
+
+ return 0;
+}
+
+static int dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *mdsi)
+{
+ struct dw_dsi *dsi = host_to_dsi(host);
+
+ if (mdsi->lanes < 1 || mdsi->lanes > 4) {
+ DRM_ERROR("dsi device params invalid\n");
+ return -EINVAL;
+ }
+
+ dsi->lanes = mdsi->lanes;
+ dsi->format = mdsi->format;
+ dsi->mode_flags = mdsi->mode_flags;
+
+ return 0;
+}
+
+static int dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *mdsi)
+{
+ /* do nothing */
+ return 0;
+}
+
+static const struct mipi_dsi_host_ops dsi_host_ops = {
+ .attach = dsi_host_attach,
+ .detach = dsi_host_detach,
+};
+
+static int dsi_host_init(struct device *dev, struct dw_dsi *dsi)
+{
+ struct mipi_dsi_host *host = &dsi->host;
+ int ret;
+
+ host->dev = dev;
+ host->ops = &dsi_host_ops;
+ ret = mipi_dsi_host_register(host);
+ if (ret) {
+ DRM_ERROR("failed to register dsi host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
+{
+ struct drm_encoder *encoder = &dsi->encoder;
+ struct drm_bridge *bridge = dsi->bridge;
+ int ret;
+
+ /* associate the bridge to dsi encoder */
+ encoder->bridge = bridge;
+ bridge->encoder = encoder;
+
+ ret = drm_bridge_attach(dev, bridge);
+ if (ret) {
+ DRM_ERROR("failed to attach external bridge\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dsi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct dsi_data *ddata = dev_get_drvdata(dev);
+ struct dw_dsi *dsi = &ddata->dsi;
+ struct drm_device *drm_dev = data;
+ int ret;
+
+ ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder);
+ if (ret)
+ return ret;
+
+ ret = dsi_host_init(dev, dsi);
+ if (ret)
+ return ret;
+
+ ret = dsi_bridge_init(drm_dev, dsi);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void dsi_unbind(struct device *dev, struct device *master, void *data)
+{
+ /* do nothing */
+}
+
+static const struct component_ops dsi_ops = {
+ .bind = dsi_bind,
+ .unbind = dsi_unbind,
+};
+
+static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi)
+{
+ struct dsi_hw_ctx *ctx = dsi->ctx;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *endpoint, *bridge_node;
+ struct drm_bridge *bridge;
+ struct resource *res;
+
+ /*
+ * Get the endpoint node. In our case, dsi has one output port1
+ * to which the external HDMI bridge is connected.
+ */
+ endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
+ if (!endpoint) {
+ DRM_ERROR("no valid endpoint node\n");
+ return -ENODEV;
+ }
+ of_node_put(endpoint);
+
+ bridge_node = of_graph_get_remote_port_parent(endpoint);
+ if (!bridge_node) {
+ DRM_ERROR("no valid bridge node\n");
+ return -ENODEV;
+ }
+ of_node_put(bridge_node);
+
+ bridge = of_drm_find_bridge(bridge_node);
+ if (!bridge) {
+ DRM_INFO("wait for external HDMI bridge driver.\n");
+ return -EPROBE_DEFER;
+ }
+ dsi->bridge = bridge;
+
+ ctx->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(ctx->pclk)) {
+ DRM_ERROR("failed to get pclk clock\n");
+ return PTR_ERR(ctx->pclk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ctx->base)) {
+ DRM_ERROR("failed to remap dsi io region\n");
+ return PTR_ERR(ctx->base);
+ }
+
+ return 0;
+}
+
+static int dsi_probe(struct platform_device *pdev)
+{
+ struct dsi_data *data;
+ struct dw_dsi *dsi;
+ struct dsi_hw_ctx *ctx;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ DRM_ERROR("failed to allocate dsi data.\n");
+ return -ENOMEM;
+ }
+ dsi = &data->dsi;
+ ctx = &data->ctx;
+ dsi->ctx = ctx;
+
+ ret = dsi_parse_dt(pdev, dsi);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return component_add(&pdev->dev, &dsi_ops);
+}
+
+static int dsi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dsi_ops);
+
+ return 0;
+}
+
+static const struct of_device_id dsi_of_match[] = {
+ {.compatible = "hisilicon,hi6220-dsi"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, dsi_of_match);
+
+static struct platform_driver dsi_driver = {
+ .probe = dsi_probe,
+ .remove = dsi_remove,
+ .driver = {
+ .name = "dw-dsi",
+ .of_match_table = dsi_of_match,
+ },
+};
+
+module_platform_driver(dsi_driver);
+
+MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>");
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
+MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
new file mode 100644
index 000000000000..18808fc9f362
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * 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 __DW_DSI_REG_H__
+#define __DW_DSI_REG_H__
+
+#define MASK(x) (BIT(x) - 1)
+
+/*
+ * regs
+ */
+#define PWR_UP 0x04 /* Core power-up */
+#define RESET 0
+#define POWERUP BIT(0)
+#define PHY_IF_CFG 0xA4 /* D-PHY interface configuration */
+#define CLKMGR_CFG 0x08 /* the internal clock dividers */
+#define PHY_RSTZ 0xA0 /* D-PHY reset control */
+#define PHY_ENABLECLK BIT(2)
+#define PHY_UNRSTZ BIT(1)
+#define PHY_UNSHUTDOWNZ BIT(0)
+#define PHY_TST_CTRL0 0xB4 /* D-PHY test interface control 0 */
+#define PHY_TST_CTRL1 0xB8 /* D-PHY test interface control 1 */
+#define CLK_TLPX 0x10
+#define CLK_THS_PREPARE 0x11
+#define CLK_THS_ZERO 0x12
+#define CLK_THS_TRAIL 0x13
+#define CLK_TWAKEUP 0x14
+#define DATA_TLPX(x) (0x20 + ((x) << 4))
+#define DATA_THS_PREPARE(x) (0x21 + ((x) << 4))
+#define DATA_THS_ZERO(x) (0x22 + ((x) << 4))
+#define DATA_THS_TRAIL(x) (0x23 + ((x) << 4))
+#define DATA_TTA_GO(x) (0x24 + ((x) << 4))
+#define DATA_TTA_GET(x) (0x25 + ((x) << 4))
+#define DATA_TWAKEUP(x) (0x26 + ((x) << 4))
+#define PHY_CFG_I 0x60
+#define PHY_CFG_PLL_I 0x63
+#define PHY_CFG_PLL_II 0x64
+#define PHY_CFG_PLL_III 0x65
+#define PHY_CFG_PLL_IV 0x66
+#define PHY_CFG_PLL_V 0x67
+#define DPI_COLOR_CODING 0x10 /* DPI color coding */
+#define DPI_CFG_POL 0x14 /* DPI polarity configuration */
+#define VID_HSA_TIME 0x48 /* Horizontal Sync Active time */
+#define VID_HBP_TIME 0x4C /* Horizontal Back Porch time */
+#define VID_HLINE_TIME 0x50 /* Line time */
+#define VID_VSA_LINES 0x54 /* Vertical Sync Active period */
+#define VID_VBP_LINES 0x58 /* Vertical Back Porch period */
+#define VID_VFP_LINES 0x5C /* Vertical Front Porch period */
+#define VID_VACTIVE_LINES 0x60 /* Vertical resolution */
+#define VID_PKT_SIZE 0x3C /* Video packet size */
+#define VID_MODE_CFG 0x38 /* Video mode configuration */
+#define PHY_TMR_CFG 0x9C /* Data lanes timing configuration */
+#define BTA_TO_CNT 0x8C /* Response timeout definition */
+#define PHY_TMR_LPCLK_CFG 0x98 /* clock lane timing configuration */
+#define CLK_DATA_TMR_CFG 0xCC
+#define LPCLK_CTRL 0x94 /* Low-power in clock lane */
+#define PHY_TXREQUESTCLKHS BIT(0)
+#define MODE_CFG 0x34 /* Video or Command mode selection */
+#define PHY_STATUS 0xB0 /* D-PHY PPI status interface */
+
+#define PHY_STOP_WAIT_TIME 0x30
+
+/*
+ * regs relevant enum
+ */
+enum dpi_color_coding {
+ DSI_24BITS_1 = 5,
+};
+
+enum dsi_video_mode_type {
+ DSI_NON_BURST_SYNC_PULSES = 0,
+ DSI_NON_BURST_SYNC_EVENTS,
+ DSI_BURST_SYNC_PULSES_1,
+ DSI_BURST_SYNC_PULSES_2
+};
+
+enum dsi_work_mode {
+ DSI_VIDEO_MODE = 0,
+ DSI_COMMAND_MODE
+};
+
+/*
+ * Register Write/Read Helper functions
+ */
+static inline void dw_update_bits(void __iomem *addr, u32 bit_start,
+ u32 mask, u32 val)
+{
+ u32 tmp, orig;
+
+ orig = readl(addr);
+ tmp = orig & ~(mask << bit_start);
+ tmp |= (val & mask) << bit_start;
+ writel(tmp, addr);
+}
+
+#endif /* __DW_DRM_DSI_H__ */
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h
new file mode 100644
index 000000000000..4cf281b7ed63
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * 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 __KIRIN_ADE_REG_H__
+#define __KIRIN_ADE_REG_H__
+
+/*
+ * ADE Registers
+ */
+#define MASK(x) (BIT(x) - 1)
+
+#define ADE_CTRL 0x0004
+#define FRM_END_START_OFST 0
+#define FRM_END_START_MASK MASK(2)
+#define AUTO_CLK_GATE_EN_OFST 0
+#define AUTO_CLK_GATE_EN BIT(0)
+#define ADE_DISP_SRC_CFG 0x0018
+#define ADE_CTRL1 0x008C
+#define ADE_EN 0x0100
+#define ADE_DISABLE 0
+#define ADE_ENABLE 1
+/* reset and reload regs */
+#define ADE_SOFT_RST_SEL(x) (0x0078 + (x) * 0x4)
+#define ADE_RELOAD_DIS(x) (0x00AC + (x) * 0x4)
+#define RDMA_OFST 0
+#define CLIP_OFST 15
+#define SCL_OFST 21
+#define CTRAN_OFST 24
+#define OVLY_OFST 37 /* 32+5 */
+/* channel regs */
+#define RD_CH_CTRL(x) (0x1004 + (x) * 0x80)
+#define RD_CH_ADDR(x) (0x1008 + (x) * 0x80)
+#define RD_CH_SIZE(x) (0x100C + (x) * 0x80)
+#define RD_CH_STRIDE(x) (0x1010 + (x) * 0x80)
+#define RD_CH_SPACE(x) (0x1014 + (x) * 0x80)
+#define RD_CH_EN(x) (0x1020 + (x) * 0x80)
+/* overlay regs */
+#define ADE_OVLY1_TRANS_CFG 0x002C
+#define ADE_OVLY_CTL 0x0098
+#define ADE_OVLY_CH_XY0(x) (0x2004 + (x) * 4)
+#define ADE_OVLY_CH_XY1(x) (0x2024 + (x) * 4)
+#define ADE_OVLY_CH_CTL(x) (0x204C + (x) * 4)
+#define ADE_OVLY_OUTPUT_SIZE(x) (0x2070 + (x) * 8)
+#define OUTPUT_XSIZE_OFST 16
+#define ADE_OVLYX_CTL(x) (0x209C + (x) * 4)
+#define CH_OVLY_SEL_OFST(x) ((x) * 4)
+#define CH_OVLY_SEL_MASK MASK(2)
+#define CH_OVLY_SEL_VAL(x) ((x) + 1)
+#define CH_ALP_MODE_OFST 0
+#define CH_ALP_SEL_OFST 2
+#define CH_UNDER_ALP_SEL_OFST 4
+#define CH_EN_OFST 6
+#define CH_ALP_GBL_OFST 15
+#define CH_SEL_OFST 28
+/* ctran regs */
+#define ADE_CTRAN_DIS(x) (0x5004 + (x) * 0x100)
+#define CTRAN_BYPASS_ON 1
+#define CTRAN_BYPASS_OFF 0
+#define ADE_CTRAN_IMAGE_SIZE(x) (0x503C + (x) * 0x100)
+/* clip regs */
+#define ADE_CLIP_DISABLE(x) (0x6800 + (x) * 0x100)
+#define ADE_CLIP_SIZE0(x) (0x6804 + (x) * 0x100)
+#define ADE_CLIP_SIZE1(x) (0x6808 + (x) * 0x100)
+
+/*
+ * LDI Registers
+ */
+#define LDI_HRZ_CTRL0 0x7400
+#define HBP_OFST 20
+#define LDI_HRZ_CTRL1 0x7404
+#define LDI_VRT_CTRL0 0x7408
+#define VBP_OFST 20
+#define LDI_VRT_CTRL1 0x740C
+#define LDI_PLR_CTRL 0x7410
+#define FLAG_NVSYNC BIT(0)
+#define FLAG_NHSYNC BIT(1)
+#define FLAG_NPIXCLK BIT(2)
+#define FLAG_NDE BIT(3)
+#define LDI_DSP_SIZE 0x7414
+#define VSIZE_OFST 20
+#define LDI_INT_EN 0x741C
+#define FRAME_END_INT_EN_OFST 1
+#define LDI_CTRL 0x7420
+#define BPP_OFST 3
+#define DATA_GATE_EN BIT(2)
+#define LDI_EN BIT(0)
+#define LDI_MSK_INT 0x7428
+#define LDI_INT_CLR 0x742C
+#define LDI_WORK_MODE 0x7430
+#define LDI_HDMI_DSI_GT 0x7434
+
+/*
+ * ADE media bus service regs
+ */
+#define ADE0_QOSGENERATOR_MODE 0x010C
+#define QOSGENERATOR_MODE_MASK MASK(2)
+#define ADE0_QOSGENERATOR_EXTCONTROL 0x0118
+#define SOCKET_QOS_EN BIT(0)
+#define ADE1_QOSGENERATOR_MODE 0x020C
+#define ADE1_QOSGENERATOR_EXTCONTROL 0x0218
+
+/*
+ * ADE regs relevant enums
+ */
+enum frame_end_start {
+ /* regs take effect in every vsync */
+ REG_EFFECTIVE_IN_VSYNC = 0,
+ /* regs take effect in fist ade en and every frame end */
+ REG_EFFECTIVE_IN_ADEEN_FRMEND,
+ /* regs take effect in ade en immediately */
+ REG_EFFECTIVE_IN_ADEEN,
+ /* regs take effect in first vsync and every frame end */
+ REG_EFFECTIVE_IN_VSYNC_FRMEND
+};
+
+enum ade_fb_format {
+ ADE_RGB_565 = 0,
+ ADE_BGR_565,
+ ADE_XRGB_8888,
+ ADE_XBGR_8888,
+ ADE_ARGB_8888,
+ ADE_ABGR_8888,
+ ADE_RGBA_8888,
+ ADE_BGRA_8888,
+ ADE_RGB_888,
+ ADE_BGR_888 = 9,
+ ADE_FORMAT_UNSUPPORT = 800
+};
+
+enum ade_channel {
+ ADE_CH1 = 0, /* channel 1 for primary plane */
+ ADE_CH_NUM
+};
+
+enum ade_scale {
+ ADE_SCL1 = 0,
+ ADE_SCL2,
+ ADE_SCL3,
+ ADE_SCL_NUM
+};
+
+enum ade_ctran {
+ ADE_CTRAN1 = 0,
+ ADE_CTRAN2,
+ ADE_CTRAN3,
+ ADE_CTRAN4,
+ ADE_CTRAN5,
+ ADE_CTRAN6,
+ ADE_CTRAN_NUM
+};
+
+enum ade_overlay {
+ ADE_OVLY1 = 0,
+ ADE_OVLY2,
+ ADE_OVLY3,
+ ADE_OVLY_NUM
+};
+
+enum ade_alpha_mode {
+ ADE_ALP_GLOBAL = 0,
+ ADE_ALP_PIXEL,
+ ADE_ALP_PIXEL_AND_GLB
+};
+
+enum ade_alpha_blending_mode {
+ ADE_ALP_MUL_COEFF_0 = 0, /* alpha */
+ ADE_ALP_MUL_COEFF_1, /* 1-alpha */
+ ADE_ALP_MUL_COEFF_2, /* 0 */
+ ADE_ALP_MUL_COEFF_3 /* 1 */
+};
+
+/*
+ * LDI regs relevant enums
+ */
+enum dsi_pclk_en {
+ DSI_PCLK_ON = 0,
+ DSI_PCLK_OFF
+};
+
+enum ldi_output_format {
+ LDI_OUT_RGB_565 = 0,
+ LDI_OUT_RGB_666,
+ LDI_OUT_RGB_888
+};
+
+enum ldi_work_mode {
+ TEST_MODE = 0,
+ NORMAL_MODE
+};
+
+enum ldi_input_source {
+ DISP_SRC_NONE = 0,
+ DISP_SRC_OVLY2,
+ DISP_SRC_DISP,
+ DISP_SRC_ROT,
+ DISP_SRC_SCL2
+};
+
+/*
+ * ADE media bus service relevant enums
+ */
+enum qos_generator_mode {
+ FIXED_MODE = 0,
+ LIMITER_MODE,
+ BYPASS_MODE,
+ REGULATOR_MODE
+};
+
+/*
+ * Register Write/Read Helper functions
+ */
+static inline void ade_update_bits(void __iomem *addr, u32 bit_start,
+ u32 mask, u32 val)
+{
+ u32 tmp, orig;
+
+ orig = readl(addr);
+ tmp = orig & ~(mask << bit_start);
+ tmp |= (val & mask) << bit_start;
+ writel(tmp, addr);
+}
+
+#endif
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
new file mode 100644
index 000000000000..fba6372d060e
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
@@ -0,0 +1,1057 @@
+/*
+ * Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * Author:
+ * Xinliang Liu <z.liuxinliang@hisilicon.com>
+ * Xinliang Liu <xinliang.liu@linaro.org>
+ * Xinwei Kong <kong.kongxinwei@hisilicon.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/clk.h>
+#include <video/display_timing.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "kirin_drm_drv.h"
+#include "kirin_ade_reg.h"
+
+#define PRIMARY_CH ADE_CH1 /* primary plane */
+#define OUT_OVLY ADE_OVLY2 /* output overlay compositor */
+#define ADE_DEBUG 1
+
+#define to_ade_crtc(crtc) \
+ container_of(crtc, struct ade_crtc, base)
+
+#define to_ade_plane(plane) \
+ container_of(plane, struct ade_plane, base)
+
+struct ade_hw_ctx {
+ void __iomem *base;
+ struct regmap *noc_regmap;
+ struct clk *ade_core_clk;
+ struct clk *media_noc_clk;
+ struct clk *ade_pix_clk;
+ struct reset_control *reset;
+ bool power_on;
+ int irq;
+};
+
+struct ade_crtc {
+ struct drm_crtc base;
+ struct ade_hw_ctx *ctx;
+ bool enable;
+ u32 out_format;
+};
+
+struct ade_plane {
+ struct drm_plane base;
+ void *ctx;
+ u8 ch; /* channel */
+};
+
+struct ade_data {
+ struct ade_crtc acrtc;
+ struct ade_plane aplane[ADE_CH_NUM];
+ struct ade_hw_ctx ctx;
+};
+
+/* ade-format info: */
+struct ade_format {
+ u32 pixel_format;
+ enum ade_fb_format ade_format;
+};
+
+static const struct ade_format ade_formats[] = {
+ /* 16bpp RGB: */
+ { DRM_FORMAT_RGB565, ADE_RGB_565 },
+ { DRM_FORMAT_BGR565, ADE_BGR_565 },
+ /* 24bpp RGB: */
+ { DRM_FORMAT_RGB888, ADE_RGB_888 },
+ { DRM_FORMAT_BGR888, ADE_BGR_888 },
+ /* 32bpp [A]RGB: */
+ { DRM_FORMAT_XRGB8888, ADE_XRGB_8888 },
+ { DRM_FORMAT_XBGR8888, ADE_XBGR_8888 },
+ { DRM_FORMAT_RGBA8888, ADE_RGBA_8888 },
+ { DRM_FORMAT_BGRA8888, ADE_BGRA_8888 },
+ { DRM_FORMAT_ARGB8888, ADE_ARGB_8888 },
+ { DRM_FORMAT_ABGR8888, ADE_ABGR_8888 },
+};
+
+static const u32 channel_formats1[] = {
+ /* channel 1,2,3,4 */
+ DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888
+};
+
+u32 ade_get_channel_formats(u8 ch, const u32 **formats)
+{
+ switch (ch) {
+ case ADE_CH1:
+ *formats = channel_formats1;
+ return ARRAY_SIZE(channel_formats1);
+ default:
+ DRM_ERROR("no this channel %d\n", ch);
+ *formats = NULL;
+ return 0;
+ }
+}
+
+/* convert from fourcc format to ade format */
+static u32 ade_get_format(u32 pixel_format)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ade_formats); i++)
+ if (ade_formats[i].pixel_format == pixel_format)
+ return ade_formats[i].ade_format;
+
+ /* not found */
+ DRM_ERROR("Not found pixel format!!fourcc_format= %d\n",
+ pixel_format);
+ return ADE_FORMAT_UNSUPPORT;
+}
+
+static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val)
+{
+ u32 bit_ofst, reg_num;
+
+ bit_ofst = bit_num % 32;
+ reg_num = bit_num / 32;
+
+ ade_update_bits(base + ADE_RELOAD_DIS(reg_num), bit_ofst,
+ MASK(1), !!val);
+}
+
+static u32 ade_read_reload_bit(void __iomem *base, u32 bit_num)
+{
+ u32 tmp, bit_ofst, reg_num;
+
+ bit_ofst = bit_num % 32;
+ reg_num = bit_num / 32;
+
+ tmp = readl(base + ADE_RELOAD_DIS(reg_num));
+ return !!(BIT(bit_ofst) & tmp);
+}
+
+static void ade_init(struct ade_hw_ctx *ctx)
+{
+ void __iomem *base = ctx->base;
+
+ /* enable clk gate */
+ ade_update_bits(base + ADE_CTRL1, AUTO_CLK_GATE_EN_OFST,
+ AUTO_CLK_GATE_EN, ADE_ENABLE);
+ /* clear overlay */
+ writel(0, base + ADE_OVLY1_TRANS_CFG);
+ writel(0, base + ADE_OVLY_CTL);
+ writel(0, base + ADE_OVLYX_CTL(OUT_OVLY));
+ /* clear reset and reload regs */
+ writel(MASK(32), base + ADE_SOFT_RST_SEL(0));
+ writel(MASK(32), base + ADE_SOFT_RST_SEL(1));
+ writel(MASK(32), base + ADE_RELOAD_DIS(0));
+ writel(MASK(32), base + ADE_RELOAD_DIS(1));
+ /*
+ * for video mode, all the ade registers should
+ * become effective at frame end.
+ */
+ ade_update_bits(base + ADE_CTRL, FRM_END_START_OFST,
+ FRM_END_START_MASK, REG_EFFECTIVE_IN_ADEEN_FRMEND);
+}
+
+static void ade_set_pix_clk(struct ade_hw_ctx *ctx,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ u32 clk_Hz = mode->clock * 1000;
+ int ret;
+
+ /*
+ * Success should be guaranteed in mode_valid call back,
+ * so failure shouldn't happen here
+ */
+ ret = clk_set_rate(ctx->ade_pix_clk, clk_Hz);
+ if (ret)
+ DRM_ERROR("failed to set pixel clk %dHz (%d)\n", clk_Hz, ret);
+ adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
+}
+
+static void ade_ldi_set_mode(struct ade_crtc *acrtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj_mode)
+{
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ void __iomem *base = ctx->base;
+ u32 width = mode->hdisplay;
+ u32 height = mode->vdisplay;
+ u32 hfp, hbp, hsw, vfp, vbp, vsw;
+ u32 plr_flags;
+
+ plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? FLAG_NVSYNC : 0;
+ plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? FLAG_NHSYNC : 0;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hbp = mode->htotal - mode->hsync_end;
+ hsw = mode->hsync_end - mode->hsync_start;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+ vsw = mode->vsync_end - mode->vsync_start;
+ if (vsw > 15) {
+ DRM_DEBUG_DRIVER("vsw exceeded 15\n");
+ vsw = 15;
+ }
+
+ writel((hbp << HBP_OFST) | hfp, base + LDI_HRZ_CTRL0);
+ /* the configured value is actual value - 1 */
+ writel(hsw - 1, base + LDI_HRZ_CTRL1);
+ writel((vbp << VBP_OFST) | vfp, base + LDI_VRT_CTRL0);
+ /* the configured value is actual value - 1 */
+ writel(vsw - 1, base + LDI_VRT_CTRL1);
+ /* the configured value is actual value - 1 */
+ writel(((height - 1) << VSIZE_OFST) | (width - 1),
+ base + LDI_DSP_SIZE);
+ writel(plr_flags, base + LDI_PLR_CTRL);
+
+ /* set overlay compositor output size */
+ writel(((width - 1) << OUTPUT_XSIZE_OFST) | (height - 1),
+ base + ADE_OVLY_OUTPUT_SIZE(OUT_OVLY));
+
+ /* ctran6 setting */
+ writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6));
+ /* the configured value is actual value - 1 */
+ writel(width * height - 1, base + ADE_CTRAN_IMAGE_SIZE(ADE_CTRAN6));
+ ade_update_reload_bit(base, CTRAN_OFST + ADE_CTRAN6, 0);
+
+ ade_set_pix_clk(ctx, mode, adj_mode);
+
+ DRM_DEBUG_DRIVER("set mode: %dx%d\n", width, height);
+}
+
+static int ade_power_up(struct ade_hw_ctx *ctx)
+{
+ int ret;
+
+ ret = clk_prepare_enable(ctx->media_noc_clk);
+ if (ret) {
+ DRM_ERROR("failed to enable media_noc_clk (%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(ctx->reset);
+ if (ret) {
+ DRM_ERROR("failed to deassert reset\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ctx->ade_core_clk);
+ if (ret) {
+ DRM_ERROR("failed to enable ade_core_clk (%d)\n", ret);
+ return ret;
+ }
+
+ ade_init(ctx);
+ ctx->power_on = true;
+ return 0;
+}
+
+static void ade_power_down(struct ade_hw_ctx *ctx)
+{
+ void __iomem *base = ctx->base;
+
+ writel(ADE_DISABLE, base + LDI_CTRL);
+ /* dsi pixel off */
+ writel(DSI_PCLK_OFF, base + LDI_HDMI_DSI_GT);
+
+ clk_disable_unprepare(ctx->ade_core_clk);
+ reset_control_assert(ctx->reset);
+ clk_disable_unprepare(ctx->media_noc_clk);
+ ctx->power_on = false;
+}
+
+static void ade_set_medianoc_qos(struct ade_crtc *acrtc)
+{
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ struct regmap *map = ctx->noc_regmap;
+
+ regmap_update_bits(map, ADE0_QOSGENERATOR_MODE,
+ QOSGENERATOR_MODE_MASK, BYPASS_MODE);
+ regmap_update_bits(map, ADE0_QOSGENERATOR_EXTCONTROL,
+ SOCKET_QOS_EN, SOCKET_QOS_EN);
+
+ regmap_update_bits(map, ADE1_QOSGENERATOR_MODE,
+ QOSGENERATOR_MODE_MASK, BYPASS_MODE);
+ regmap_update_bits(map, ADE1_QOSGENERATOR_EXTCONTROL,
+ SOCKET_QOS_EN, SOCKET_QOS_EN);
+}
+
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+ struct kirin_drm_private *priv = dev->dev_private;
+ struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ void __iomem *base = ctx->base;
+
+ if (!ctx->power_on)
+ (void)ade_power_up(ctx);
+
+ ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
+ MASK(1), 1);
+
+ return 0;
+}
+
+static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+ struct kirin_drm_private *priv = dev->dev_private;
+ struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ void __iomem *base = ctx->base;
+
+ if (!ctx->power_on) {
+ DRM_ERROR("power is down! vblank disable fail\n");
+ return;
+ }
+
+ ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
+ MASK(1), 0);
+}
+
+static irqreturn_t ade_irq_handler(int irq, void *data)
+{
+ struct ade_crtc *acrtc = data;
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ struct drm_crtc *crtc = &acrtc->base;
+ void __iomem *base = ctx->base;
+ u32 status;
+
+ status = readl(base + LDI_MSK_INT);
+ DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status);
+
+ /* vblank irq */
+ if (status & BIT(FRAME_END_INT_EN_OFST)) {
+ ade_update_bits(base + LDI_INT_CLR, FRAME_END_INT_EN_OFST,
+ MASK(1), 1);
+ drm_crtc_handle_vblank(crtc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void ade_display_enable(struct ade_crtc *acrtc)
+{
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ void __iomem *base = ctx->base;
+ u32 out_fmt = acrtc->out_format;
+
+ /* enable output overlay compositor */
+ writel(ADE_ENABLE, base + ADE_OVLYX_CTL(OUT_OVLY));
+ ade_update_reload_bit(base, OVLY_OFST + OUT_OVLY, 0);
+
+ /* display source setting */
+ writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
+
+ /* enable ade */
+ writel(ADE_ENABLE, base + ADE_EN);
+ /* enable ldi */
+ writel(NORMAL_MODE, base + LDI_WORK_MODE);
+ writel((out_fmt << BPP_OFST) | DATA_GATE_EN | LDI_EN,
+ base + LDI_CTRL);
+ /* dsi pixel on */
+ writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT);
+}
+
+#if ADE_DEBUG
+static void ade_rdma_dump_regs(void __iomem *base, u32 ch)
+{
+ u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
+ u32 val;
+
+ reg_ctrl = RD_CH_CTRL(ch);
+ reg_addr = RD_CH_ADDR(ch);
+ reg_size = RD_CH_SIZE(ch);
+ reg_stride = RD_CH_STRIDE(ch);
+ reg_space = RD_CH_SPACE(ch);
+ reg_en = RD_CH_EN(ch);
+
+ val = ade_read_reload_bit(base, RDMA_OFST + ch);
+ DRM_DEBUG_DRIVER("[rdma%d]: reload(%d)\n", ch + 1, val);
+ val = readl(base + reg_ctrl);
+ DRM_DEBUG_DRIVER("[rdma%d]: reg_ctrl(0x%08x)\n", ch + 1, val);
+ val = readl(base + reg_addr);
+ DRM_DEBUG_DRIVER("[rdma%d]: reg_addr(0x%08x)\n", ch + 1, val);
+ val = readl(base + reg_size);
+ DRM_DEBUG_DRIVER("[rdma%d]: reg_size(0x%08x)\n", ch + 1, val);
+ val = readl(base + reg_stride);
+ DRM_DEBUG_DRIVER("[rdma%d]: reg_stride(0x%08x)\n", ch + 1, val);
+ val = readl(base + reg_space);
+ DRM_DEBUG_DRIVER("[rdma%d]: reg_space(0x%08x)\n", ch + 1, val);
+ val = readl(base + reg_en);
+ DRM_DEBUG_DRIVER("[rdma%d]: reg_en(0x%08x)\n", ch + 1, val);
+}
+
+static void ade_clip_dump_regs(void __iomem *base, u32 ch)
+{
+ u32 val;
+
+ val = ade_read_reload_bit(base, CLIP_OFST + ch);
+ DRM_DEBUG_DRIVER("[clip%d]: reload(%d)\n", ch + 1, val);
+ val = readl(base + ADE_CLIP_DISABLE(ch));
+ DRM_DEBUG_DRIVER("[clip%d]: reg_clip_disable(0x%08x)\n", ch + 1, val);
+ val = readl(base + ADE_CLIP_SIZE0(ch));
+ DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size0(0x%08x)\n", ch + 1, val);
+ val = readl(base + ADE_CLIP_SIZE1(ch));
+ DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size1(0x%08x)\n", ch + 1, val);
+}
+
+static void ade_compositor_routing_dump_regs(void __iomem *base, u32 ch)
+{
+ u8 ovly_ch = 0; /* TODO: Only primary plane now */
+ u32 val;
+
+ val = readl(base + ADE_OVLY_CH_XY0(ovly_ch));
+ DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy0(0x%08x)\n", ovly_ch, val);
+ val = readl(base + ADE_OVLY_CH_XY1(ovly_ch));
+ DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy1(0x%08x)\n", ovly_ch, val);
+ val = readl(base + ADE_OVLY_CH_CTL(ovly_ch));
+ DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_ctl(0x%08x)\n", ovly_ch, val);
+}
+
+static void ade_dump_overlay_compositor_regs(void __iomem *base, u32 comp)
+{
+ u32 val;
+
+ val = ade_read_reload_bit(base, OVLY_OFST + comp);
+ DRM_DEBUG_DRIVER("[overlay%d]: reload(%d)\n", comp + 1, val);
+ writel(ADE_ENABLE, base + ADE_OVLYX_CTL(comp));
+ DRM_DEBUG_DRIVER("[overlay%d]: reg_ctl(0x%08x)\n", comp + 1, val);
+ val = readl(base + ADE_OVLY_CTL);
+ DRM_DEBUG_DRIVER("ovly_ctl(0x%08x)\n", val);
+}
+
+static void ade_dump_regs(void __iomem *base)
+{
+ u32 i;
+
+ /* dump channel regs */
+ for (i = 0; i < ADE_CH_NUM; i++) {
+ /* dump rdma regs */
+ ade_rdma_dump_regs(base, i);
+
+ /* dump clip regs */
+ ade_clip_dump_regs(base, i);
+
+ /* dump compositor routing regs */
+ ade_compositor_routing_dump_regs(base, i);
+ }
+
+ /* dump overlay compositor regs */
+ ade_dump_overlay_compositor_regs(base, OUT_OVLY);
+}
+#else
+static void ade_dump_regs(void __iomem *base) { }
+#endif
+
+static void ade_crtc_enable(struct drm_crtc *crtc)
+{
+ struct ade_crtc *acrtc = to_ade_crtc(crtc);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ int ret;
+
+ if (acrtc->enable)
+ return;
+
+ if (!ctx->power_on) {
+ ret = ade_power_up(ctx);
+ if (ret)
+ return;
+ }
+
+ ade_set_medianoc_qos(acrtc);
+ ade_display_enable(acrtc);
+ ade_dump_regs(ctx->base);
+ acrtc->enable = true;
+}
+
+static void ade_crtc_disable(struct drm_crtc *crtc)
+{
+ struct ade_crtc *acrtc = to_ade_crtc(crtc);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+
+ if (!acrtc->enable)
+ return;
+
+ ade_power_down(ctx);
+ acrtc->enable = false;
+}
+
+static int ade_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ /* do nothing */
+ return 0;
+}
+
+static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct ade_crtc *acrtc = to_ade_crtc(crtc);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ struct drm_display_mode *mode = &crtc->state->mode;
+ struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode;
+
+ if (!ctx->power_on)
+ (void)ade_power_up(ctx);
+ ade_ldi_set_mode(acrtc, mode, adj_mode);
+}
+
+static void ade_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct ade_crtc *acrtc = to_ade_crtc(crtc);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+
+ if (!ctx->power_on)
+ (void)ade_power_up(ctx);
+}
+
+static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+
+{
+ struct ade_crtc *acrtc = to_ade_crtc(crtc);
+ struct ade_hw_ctx *ctx = acrtc->ctx;
+ void __iomem *base = ctx->base;
+
+ /* only crtc is enabled regs take effect */
+ if (acrtc->enable) {
+ ade_dump_regs(base);
+ /* flush ade registers */
+ writel(ADE_ENABLE, base + ADE_EN);
+ }
+}
+
+static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = {
+ .enable = ade_crtc_enable,
+ .disable = ade_crtc_disable,
+ .atomic_check = ade_crtc_atomic_check,
+ .mode_set_nofb = ade_crtc_mode_set_nofb,
+ .atomic_begin = ade_crtc_atomic_begin,
+ .atomic_flush = ade_crtc_atomic_flush,
+};
+
+static const struct drm_crtc_funcs ade_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .set_property = drm_atomic_helper_crtc_set_property,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+ struct drm_plane *plane)
+{
+ struct kirin_drm_private *priv = dev->dev_private;
+ struct device_node *port;
+ int ret;
+
+ /* set crtc port so that
+ * drm_of_find_possible_crtcs call works
+ */
+ port = of_get_child_by_name(dev->dev->of_node, "port");
+ if (!port) {
+ DRM_ERROR("no port node found in %s\n",
+ dev->dev->of_node->full_name);
+ return -EINVAL;
+ }
+ of_node_put(port);
+ crtc->port = port;
+
+ ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+ &ade_crtc_funcs, NULL);
+ if (ret) {
+ DRM_ERROR("failed to init crtc.\n");
+ return ret;
+ }
+
+ drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
+ priv->crtc[drm_crtc_index(crtc)] = crtc;
+
+ return 0;
+}
+
+static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb,
+ u32 ch, u32 y, u32 in_h, u32 fmt)
+{
+ struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
+ u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
+ u32 stride = fb->pitches[0];
+ u32 addr = (u32)obj->paddr + y * stride;
+
+ DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n",
+ ch + 1, y, in_h, stride, (u32)obj->paddr);
+ DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n",
+ addr, fb->width, fb->height, fmt,
+ drm_get_format_name(fb->pixel_format));
+
+ /* get reg offset */
+ reg_ctrl = RD_CH_CTRL(ch);
+ reg_addr = RD_CH_ADDR(ch);
+ reg_size = RD_CH_SIZE(ch);
+ reg_stride = RD_CH_STRIDE(ch);
+ reg_space = RD_CH_SPACE(ch);
+ reg_en = RD_CH_EN(ch);
+
+ /*
+ * TODO: set rotation
+ */
+ writel((fmt << 16) & 0x1f0000, base + reg_ctrl);
+ writel(addr, base + reg_addr);
+ writel((in_h << 16) | stride, base + reg_size);
+ writel(stride, base + reg_stride);
+ writel(in_h * stride, base + reg_space);
+ writel(ADE_ENABLE, base + reg_en);
+ ade_update_reload_bit(base, RDMA_OFST + ch, 0);
+}
+
+static void ade_rdma_disable(void __iomem *base, u32 ch)
+{
+ u32 reg_en;
+
+ /* get reg offset */
+ reg_en = RD_CH_EN(ch);
+ writel(0, base + reg_en);
+ ade_update_reload_bit(base, RDMA_OFST + ch, 1);
+}
+
+static void ade_clip_set(void __iomem *base, u32 ch, u32 fb_w, u32 x,
+ u32 in_w, u32 in_h)
+{
+ u32 disable_val;
+ u32 clip_left;
+ u32 clip_right;
+
+ /*
+ * clip width, no need to clip height
+ */
+ if (fb_w == in_w) { /* bypass */
+ disable_val = 1;
+ clip_left = 0;
+ clip_right = 0;
+ } else {
+ disable_val = 0;
+ clip_left = x;
+ clip_right = fb_w - (x + in_w) - 1;
+ }
+
+ DRM_DEBUG_DRIVER("clip%d: clip_left=%d, clip_right=%d\n",
+ ch + 1, clip_left, clip_right);
+
+ writel(disable_val, base + ADE_CLIP_DISABLE(ch));
+ writel((fb_w - 1) << 16 | (in_h - 1), base + ADE_CLIP_SIZE0(ch));
+ writel(clip_left << 16 | clip_right, base + ADE_CLIP_SIZE1(ch));
+ ade_update_reload_bit(base, CLIP_OFST + ch, 0);
+}
+
+static void ade_clip_disable(void __iomem *base, u32 ch)
+{
+ writel(1, base + ADE_CLIP_DISABLE(ch));
+ ade_update_reload_bit(base, CLIP_OFST + ch, 1);
+}
+
+static bool has_Alpha_channel(int format)
+{
+ switch (format) {
+ case ADE_ARGB_8888:
+ case ADE_ABGR_8888:
+ case ADE_RGBA_8888:
+ case ADE_BGRA_8888:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void ade_get_blending_params(u32 fmt, u8 glb_alpha, u8 *alp_mode,
+ u8 *alp_sel, u8 *under_alp_sel)
+{
+ bool has_alpha = has_Alpha_channel(fmt);
+
+ /*
+ * get alp_mode
+ */
+ if (has_alpha && glb_alpha < 255)
+ *alp_mode = ADE_ALP_PIXEL_AND_GLB;
+ else if (has_alpha)
+ *alp_mode = ADE_ALP_PIXEL;
+ else
+ *alp_mode = ADE_ALP_GLOBAL;
+
+ /*
+ * get alp sel
+ */
+ *alp_sel = ADE_ALP_MUL_COEFF_3; /* 1 */
+ *under_alp_sel = ADE_ALP_MUL_COEFF_2; /* 0 */
+}
+
+static void ade_compositor_routing_set(void __iomem *base, u8 ch,
+ u32 x0, u32 y0,
+ u32 in_w, u32 in_h, u32 fmt)
+{
+ u8 ovly_ch = 0; /* TODO: This is the zpos, only one plane now */
+ u8 glb_alpha = 255;
+ u32 x1 = x0 + in_w - 1;
+ u32 y1 = y0 + in_h - 1;
+ u32 val;
+ u8 alp_sel;
+ u8 under_alp_sel;
+ u8 alp_mode;
+
+ ade_get_blending_params(fmt, glb_alpha, &alp_mode, &alp_sel,
+ &under_alp_sel);
+
+ /* overlay routing setting
+ */
+ writel(x0 << 16 | y0, base + ADE_OVLY_CH_XY0(ovly_ch));
+ writel(x1 << 16 | y1, base + ADE_OVLY_CH_XY1(ovly_ch));
+ val = (ch + 1) << CH_SEL_OFST | BIT(CH_EN_OFST) |
+ alp_sel << CH_ALP_SEL_OFST |
+ under_alp_sel << CH_UNDER_ALP_SEL_OFST |
+ glb_alpha << CH_ALP_GBL_OFST |
+ alp_mode << CH_ALP_MODE_OFST;
+ writel(val, base + ADE_OVLY_CH_CTL(ovly_ch));
+ /* connect this plane/channel to overlay2 compositor */
+ ade_update_bits(base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch),
+ CH_OVLY_SEL_MASK, CH_OVLY_SEL_VAL(OUT_OVLY));
+}
+
+static void ade_compositor_routing_disable(void __iomem *base, u32 ch)
+{
+ u8 ovly_ch = 0; /* TODO: Only primary plane now */
+
+ /* disable this plane/channel */
+ ade_update_bits(base + ADE_OVLY_CH_CTL(ovly_ch), CH_EN_OFST,
+ MASK(1), 0);
+ /* dis-connect this plane/channel of overlay2 compositor */
+ ade_update_bits(base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch),
+ CH_OVLY_SEL_MASK, 0);
+}
+
+/*
+ * Typicaly, a channel looks like: DMA-->clip-->scale-->ctrans-->compositor
+ */
+static void ade_update_channel(struct ade_plane *aplane,
+ struct drm_framebuffer *fb, int crtc_x,
+ int crtc_y, unsigned int crtc_w,
+ unsigned int crtc_h, u32 src_x,
+ u32 src_y, u32 src_w, u32 src_h)
+{
+ struct ade_hw_ctx *ctx = aplane->ctx;
+ void __iomem *base = ctx->base;
+ u32 fmt = ade_get_format(fb->pixel_format);
+ u32 ch = aplane->ch;
+ u32 in_w;
+ u32 in_h;
+
+ DRM_DEBUG_DRIVER("channel%d: src:(%d, %d)-%dx%d, crtc:(%d, %d)-%dx%d",
+ ch + 1, src_x, src_y, src_w, src_h,
+ crtc_x, crtc_y, crtc_w, crtc_h);
+
+ /* 1) DMA setting */
+ in_w = src_w;
+ in_h = src_h;
+ ade_rdma_set(base, fb, ch, src_y, in_h, fmt);
+
+ /* 2) clip setting */
+ ade_clip_set(base, ch, fb->width, src_x, in_w, in_h);
+
+ /* 3) TODO: scale setting for overlay planes */
+
+ /* 4) TODO: ctran/csc setting for overlay planes */
+
+ /* 5) compositor routing setting */
+ ade_compositor_routing_set(base, ch, crtc_x, crtc_y, in_w, in_h, fmt);
+}
+
+static void ade_disable_channel(struct ade_plane *aplane)
+{
+ struct ade_hw_ctx *ctx = aplane->ctx;
+ void __iomem *base = ctx->base;
+ u32 ch = aplane->ch;
+
+ DRM_DEBUG_DRIVER("disable channel%d\n", ch + 1);
+
+ /* disable read DMA */
+ ade_rdma_disable(base, ch);
+
+ /* disable clip */
+ ade_clip_disable(base, ch);
+
+ /* disable compositor routing */
+ ade_compositor_routing_disable(base, ch);
+}
+
+static int ade_plane_prepare_fb(struct drm_plane *plane,
+ const struct drm_plane_state *new_state)
+{
+ /* do nothing */
+ return 0;
+}
+
+static void ade_plane_cleanup_fb(struct drm_plane *plane,
+ const struct drm_plane_state *old_state)
+{
+ /* do nothing */
+}
+
+static int ade_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_crtc *crtc = state->crtc;
+ struct drm_crtc_state *crtc_state;
+ u32 src_x = state->src_x >> 16;
+ u32 src_y = state->src_y >> 16;
+ u32 src_w = state->src_w >> 16;
+ u32 src_h = state->src_h >> 16;
+ int crtc_x = state->crtc_x;
+ int crtc_y = state->crtc_y;
+ u32 crtc_w = state->crtc_w;
+ u32 crtc_h = state->crtc_h;
+ u32 fmt;
+
+ if (!crtc || !fb)
+ return 0;
+
+ fmt = ade_get_format(fb->pixel_format);
+ if (fmt == ADE_FORMAT_UNSUPPORT)
+ return -EINVAL;
+
+ crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ if (src_w != crtc_w || src_h != crtc_h) {
+ DRM_ERROR("Scale not support!!!\n");
+ return -EINVAL;
+ }
+
+ if (src_x + src_w > fb->width ||
+ src_y + src_h > fb->height)
+ return -EINVAL;
+
+ if (crtc_x < 0 || crtc_y < 0)
+ return -EINVAL;
+
+ if (crtc_x + crtc_w > crtc_state->adjusted_mode.hdisplay ||
+ crtc_y + crtc_h > crtc_state->adjusted_mode.vdisplay)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void ade_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct ade_plane *aplane = to_ade_plane(plane);
+
+ ade_update_channel(aplane, state->fb, state->crtc_x, state->crtc_y,
+ state->crtc_w, state->crtc_h,
+ state->src_x >> 16, state->src_y >> 16,
+ state->src_w >> 16, state->src_h >> 16);
+}
+
+static void ade_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct ade_plane *aplane = to_ade_plane(plane);
+
+ ade_disable_channel(aplane);
+}
+
+static const struct drm_plane_helper_funcs ade_plane_helper_funcs = {
+ .prepare_fb = ade_plane_prepare_fb,
+ .cleanup_fb = ade_plane_cleanup_fb,
+ .atomic_check = ade_plane_atomic_check,
+ .atomic_update = ade_plane_atomic_update,
+ .atomic_disable = ade_plane_atomic_disable,
+};
+
+static struct drm_plane_funcs ade_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .set_property = drm_atomic_helper_plane_set_property,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int ade_plane_init(struct drm_device *dev, struct ade_plane *aplane,
+ enum drm_plane_type type)
+{
+ const u32 *fmts;
+ u32 fmts_cnt;
+ int ret = 0;
+
+ /* get properties */
+ fmts_cnt = ade_get_channel_formats(aplane->ch, &fmts);
+ if (ret)
+ return ret;
+
+ ret = drm_universal_plane_init(dev, &aplane->base, 1, &ade_plane_funcs,
+ fmts, fmts_cnt, type, NULL);
+ if (ret) {
+ DRM_ERROR("fail to init plane, ch=%d\n", aplane->ch);
+ return ret;
+ }
+
+ drm_plane_helper_add(&aplane->base, &ade_plane_helper_funcs);
+
+ return 0;
+}
+
+static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx)
+{
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->base)) {
+ DRM_ERROR("failed to remap ade io base\n");
+ return PTR_ERR(ctx->base);
+ }
+
+ ctx->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(ctx->reset))
+ return PTR_ERR(ctx->reset);
+
+ ctx->noc_regmap =
+ syscon_regmap_lookup_by_phandle(np, "hisilicon,noc-syscon");
+ if (IS_ERR(ctx->noc_regmap)) {
+ DRM_ERROR("failed to get noc regmap\n");
+ return PTR_ERR(ctx->noc_regmap);
+ }
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ DRM_ERROR("failed to get irq\n");
+ return -ENODEV;
+ }
+
+ ctx->ade_core_clk = devm_clk_get(dev, "clk_ade_core");
+ if (!ctx->ade_core_clk) {
+ DRM_ERROR("failed to parse clk ADE_CORE\n");
+ return -ENODEV;
+ }
+
+ ctx->media_noc_clk = devm_clk_get(dev, "clk_codec_jpeg");
+ if (!ctx->media_noc_clk) {
+ DRM_ERROR("failed to parse clk CODEC_JPEG\n");
+ return -ENODEV;
+ }
+
+ ctx->ade_pix_clk = devm_clk_get(dev, "clk_ade_pix");
+ if (!ctx->ade_pix_clk) {
+ DRM_ERROR("failed to parse clk ADE_PIX\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ade_drm_init(struct drm_device *dev)
+{
+ struct platform_device *pdev = dev->platformdev;
+ struct ade_data *ade;
+ struct ade_hw_ctx *ctx;
+ struct ade_crtc *acrtc;
+ struct ade_plane *aplane;
+ enum drm_plane_type type;
+ int ret;
+ int i;
+
+ ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL);
+ if (!ade) {
+ DRM_ERROR("failed to alloc ade_data\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, ade);
+
+ ctx = &ade->ctx;
+ acrtc = &ade->acrtc;
+ acrtc->ctx = ctx;
+ acrtc->out_format = LDI_OUT_RGB_888;
+
+ ret = ade_dts_parse(pdev, ctx);
+ if (ret)
+ return ret;
+
+ /*
+ * plane init
+ * TODO: Now only support primary plane, overlay planes
+ * need to do.
+ */
+ for (i = 0; i < ADE_CH_NUM; i++) {
+ aplane = &ade->aplane[i];
+ aplane->ch = i;
+ aplane->ctx = ctx;
+ type = i == PRIMARY_CH ? DRM_PLANE_TYPE_PRIMARY :
+ DRM_PLANE_TYPE_OVERLAY;
+
+ ret = ade_plane_init(dev, aplane, type);
+ if (ret)
+ return ret;
+ }
+
+ /* crtc init */
+ ret = ade_crtc_init(dev, &acrtc->base, &ade->aplane[PRIMARY_CH].base);
+ if (ret)
+ return ret;
+
+ /* vblank irq init */
+ ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
+ IRQF_SHARED, dev->driver->name, acrtc);
+ if (ret)
+ return ret;
+ dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
+ dev->driver->enable_vblank = ade_enable_vblank;
+ dev->driver->disable_vblank = ade_disable_vblank;
+
+ return 0;
+}
+
+static void ade_drm_cleanup(struct drm_device *dev)
+{
+ struct platform_device *pdev = dev->platformdev;
+ struct ade_data *ade = platform_get_drvdata(pdev);
+ struct drm_crtc *crtc = &ade->acrtc.base;
+
+ drm_crtc_cleanup(crtc);
+}
+
+const struct kirin_dc_ops ade_dc_ops = {
+ .init = ade_drm_init,
+ .cleanup = ade_drm_cleanup
+};
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
new file mode 100644
index 000000000000..e102c9e1e7b2
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
@@ -0,0 +1,367 @@
+/*
+ * Hisilicon Kirin SoCs drm master driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * Author:
+ * Xinliang Liu <z.liuxinliang@hisilicon.com>
+ * Xinliang Liu <xinliang.liu@linaro.org>
+ * Xinwei Kong <kong.kongxinwei@hisilicon.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/of_platform.h>
+#include <linux/component.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "kirin_drm_drv.h"
+
+static struct kirin_dc_ops *dc_ops;
+
+static int kirin_drm_kms_cleanup(struct drm_device *dev)
+{
+ struct kirin_drm_private *priv = dev->dev_private;
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+ if (priv->fbdev) {
+ drm_fbdev_cma_fini(priv->fbdev);
+ priv->fbdev = NULL;
+ }
+#endif
+ drm_kms_helper_poll_fini(dev);
+ drm_vblank_cleanup(dev);
+ dc_ops->cleanup(dev);
+ drm_mode_config_cleanup(dev);
+ devm_kfree(dev->dev, priv);
+ dev->dev_private = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+static void kirin_fbdev_output_poll_changed(struct drm_device *dev)
+{
+ struct kirin_drm_private *priv = dev->dev_private;
+
+ if (priv->fbdev) {
+ drm_fbdev_cma_hotplug_event(priv->fbdev);
+ } else {
+ priv->fbdev = drm_fbdev_cma_init(dev, 32,
+ dev->mode_config.num_crtc,
+ dev->mode_config.num_connector);
+ if (IS_ERR(priv->fbdev))
+ priv->fbdev = NULL;
+ }
+}
+#endif
+
+static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+ .output_poll_changed = kirin_fbdev_output_poll_changed,
+#endif
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void kirin_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+
+ dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
+}
+
+static int kirin_drm_kms_init(struct drm_device *dev)
+{
+ struct kirin_drm_private *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev->dev_private = priv;
+ dev_set_drvdata(dev->dev, dev);
+
+ /* dev->mode_config initialization */
+ drm_mode_config_init(dev);
+ kirin_drm_mode_config_init(dev);
+
+ /* display controller init */
+ ret = dc_ops->init(dev);
+ if (ret)
+ goto err_mode_config_cleanup;
+
+ /* bind and init sub drivers */
+ ret = component_bind_all(dev->dev, dev);
+ if (ret) {
+ DRM_ERROR("failed to bind all component.\n");
+ goto err_dc_cleanup;
+ }
+
+ /* vblank init */
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (ret) {
+ DRM_ERROR("failed to initialize vblank.\n");
+ goto err_unbind_all;
+ }
+ /* with irq_enabled = true, we can use the vblank feature. */
+ dev->irq_enabled = true;
+
+ /* reset all the states of crtc/plane/encoder/connector */
+ drm_mode_config_reset(dev);
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(dev);
+
+ /* force detection after connectors init */
+ (void)drm_helper_hpd_irq_event(dev);
+
+ return 0;
+
+err_unbind_all:
+ component_unbind_all(dev->dev, dev);
+err_dc_cleanup:
+ dc_ops->cleanup(dev);
+err_mode_config_cleanup:
+ drm_mode_config_cleanup(dev);
+ devm_kfree(dev->dev, priv);
+ dev->dev_private = NULL;
+
+ return ret;
+}
+
+static const struct file_operations kirin_drm_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static int kirin_gem_cma_dumb_create(struct drm_file *file,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ return drm_gem_cma_dumb_create_internal(file, dev, args);
+}
+
+static struct drm_driver kirin_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
+ DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
+ .fops = &kirin_drm_fops,
+ .set_busid = drm_platform_set_busid,
+
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = kirin_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+
+ .name = "kirin",
+ .desc = "Hisilicon Kirin SoCs' DRM Driver",
+ .date = "20150718",
+ .major = 1,
+ .minor = 0,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int kirin_drm_connectors_register(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ struct drm_connector *failed_connector;
+ int ret;
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_for_each_connector(connector, dev) {
+ ret = drm_connector_register(connector);
+ if (ret) {
+ failed_connector = connector;
+ goto err;
+ }
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 0;
+
+err:
+ drm_for_each_connector(connector, dev) {
+ if (failed_connector == connector)
+ break;
+ drm_connector_unregister(connector);
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
+}
+
+static int kirin_drm_bind(struct device *dev)
+{
+ struct drm_driver *driver = &kirin_drm_driver;
+ struct drm_device *drm_dev;
+ int ret;
+
+ drm_dev = drm_dev_alloc(driver, dev);
+ if (!drm_dev)
+ return -ENOMEM;
+
+ drm_dev->platformdev = to_platform_device(dev);
+
+ ret = kirin_drm_kms_init(drm_dev);
+ if (ret)
+ goto err_drm_dev_unref;
+
+ ret = drm_dev_register(drm_dev, 0);
+ if (ret)
+ goto err_kms_cleanup;
+
+ /* connectors should be registered after drm device register */
+ ret = kirin_drm_connectors_register(drm_dev);
+ if (ret)
+ goto err_drm_dev_unregister;
+
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+ driver->name, driver->major, driver->minor, driver->patchlevel,
+ driver->date, drm_dev->primary->index);
+
+ return 0;
+
+err_drm_dev_unregister:
+ drm_dev_unregister(drm_dev);
+err_kms_cleanup:
+ kirin_drm_kms_cleanup(drm_dev);
+err_drm_dev_unref:
+ drm_dev_unref(drm_dev);
+
+ return ret;
+}
+
+static void kirin_drm_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops kirin_drm_ops = {
+ .bind = kirin_drm_bind,
+ .unbind = kirin_drm_unbind,
+};
+
+static struct device_node *kirin_get_remote_node(struct device_node *np)
+{
+ struct device_node *endpoint, *remote;
+
+ /* get the first endpoint, in our case only one remote node
+ * is connected to display controller.
+ */
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint) {
+ DRM_ERROR("no valid endpoint node\n");
+ return ERR_PTR(-ENODEV);
+ }
+ of_node_put(endpoint);
+
+ remote = of_graph_get_remote_port_parent(endpoint);
+ if (!remote) {
+ DRM_ERROR("no valid remote node\n");
+ return ERR_PTR(-ENODEV);
+ }
+ of_node_put(remote);
+
+ if (!of_device_is_available(remote)) {
+ DRM_ERROR("not available for remote node\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ return remote;
+}
+
+static int kirin_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct component_match *match = NULL;
+ struct device_node *remote;
+
+ dc_ops = (struct kirin_dc_ops *)of_device_get_match_data(dev);
+ if (!dc_ops) {
+ DRM_ERROR("failed to get dt id data\n");
+ return -EINVAL;
+ }
+
+ remote = kirin_get_remote_node(np);
+ if (IS_ERR(remote))
+ return PTR_ERR(remote);
+
+ component_match_add(dev, &match, compare_of, remote);
+
+ return component_master_add_with_match(dev, &kirin_drm_ops, match);
+
+ return 0;
+}
+
+static int kirin_drm_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &kirin_drm_ops);
+ dc_ops = NULL;
+ return 0;
+}
+
+static const struct of_device_id kirin_drm_dt_ids[] = {
+ { .compatible = "hisilicon,hi6220-ade",
+ .data = &ade_dc_ops,
+ },
+ { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);
+
+static struct platform_driver kirin_drm_platform_driver = {
+ .probe = kirin_drm_platform_probe,
+ .remove = kirin_drm_platform_remove,
+ .driver = {
+ .name = "kirin-drm",
+ .of_match_table = kirin_drm_dt_ids,
+ },
+};
+
+module_platform_driver(kirin_drm_platform_driver);
+
+MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>");
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
+MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
new file mode 100644
index 000000000000..1a07caf8e7f4
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2014-2016 Hisilicon Limited.
+ *
+ * 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 __KIRIN_DRM_DRV_H__
+#define __KIRIN_DRM_DRV_H__
+
+#define MAX_CRTC 2
+
+/* display controller init/cleanup ops */
+struct kirin_dc_ops {
+ int (*init)(struct drm_device *dev);
+ void (*cleanup)(struct drm_device *dev);
+};
+
+struct kirin_drm_private {
+ struct drm_crtc *crtc[MAX_CRTC];
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+ struct drm_fbdev_cma *fbdev;
+#endif
+};
+
+extern const struct kirin_dc_ops ade_dc_ops;
+
+#endif /* __KIRIN_DRM_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
index 649a562ddf17..8f404103341d 100644
--- a/drivers/gpu/drm/i915/Kconfig.debug
+++ b/drivers/gpu/drm/i915/Kconfig.debug
@@ -1,3 +1,20 @@
+config DRM_I915_WERROR
+ bool "Force GCC to throw an error instead of a warning when compiling"
+ # As this may inadvertently break the build, only allow the user
+ # to shoot oneself in the foot iff they aim really hard
+ depends on EXPERT
+ # We use the dependency on !COMPILE_TEST to not be enabled in
+ # allmodconfig or allyesconfig configurations
+ depends on !COMPILE_TEST
+ default n
+ help
+ Add -Werror to the build flags for (and only for) i915.ko.
+ Do not enable this unless you are writing code for the i915.ko module.
+
+ Recommended for driver developers only.
+
+ If in doubt, say "N".
+
config DRM_I915_DEBUG
bool "Enable additional driver debugging"
depends on DRM_I915
@@ -10,3 +27,15 @@ config DRM_I915_DEBUG
If in doubt, say "N".
+config DRM_I915_DEBUG_GEM
+ bool "Insert extra checks into the GEM internals"
+ default n
+ depends on DRM_I915_WERROR
+ help
+ Enable extra sanity checks (including BUGs) along the GEM driver
+ paths that may slow the system down and if hit hang the machine.
+
+ Recommended for driver developers only.
+
+ If in doubt, say "N".
+
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 7ffb51b0cbc2..0b88ba0f3c1f 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -2,6 +2,8 @@
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+subdir-ccflags-$(CONFIG_DRM_I915_WERROR) := -Werror
+
# Please keep these build lists sorted!
# core driver code
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index c298312f83f6..8b8d6f07d7aa 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -89,27 +89,34 @@ static int i915_capabilities(struct seq_file *m, void *data)
return 0;
}
-static const char *get_pin_flag(struct drm_i915_gem_object *obj)
+static const char get_active_flag(struct drm_i915_gem_object *obj)
{
- if (obj->pin_display)
- return "p";
- else
- return " ";
+ return obj->active ? '*' : ' ';
}
-static const char *get_tiling_flag(struct drm_i915_gem_object *obj)
+static const char get_pin_flag(struct drm_i915_gem_object *obj)
+{
+ return obj->pin_display ? 'p' : ' ';
+}
+
+static const char get_tiling_flag(struct drm_i915_gem_object *obj)
{
switch (obj->tiling_mode) {
default:
- case I915_TILING_NONE: return " ";
- case I915_TILING_X: return "X";
- case I915_TILING_Y: return "Y";
+ case I915_TILING_NONE: return ' ';
+ case I915_TILING_X: return 'X';
+ case I915_TILING_Y: return 'Y';
}
}
-static inline const char *get_global_flag(struct drm_i915_gem_object *obj)
+static inline const char get_global_flag(struct drm_i915_gem_object *obj)
{
- return i915_gem_obj_to_ggtt(obj) ? "g" : " ";
+ return i915_gem_obj_to_ggtt(obj) ? 'g' : ' ';
+}
+
+static inline const char get_pin_mapped_flag(struct drm_i915_gem_object *obj)
+{
+ return obj->mapping ? 'M' : ' ';
}
static u64 i915_gem_obj_total_ggtt_size(struct drm_i915_gem_object *obj)
@@ -136,12 +143,13 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
lockdep_assert_held(&obj->base.dev->struct_mutex);
- seq_printf(m, "%pK: %s%s%s%s %8zdKiB %02x %02x [ ",
+ seq_printf(m, "%pK: %c%c%c%c%c %8zdKiB %02x %02x [ ",
&obj->base,
- obj->active ? "*" : " ",
+ get_active_flag(obj),
get_pin_flag(obj),
get_tiling_flag(obj),
get_global_flag(obj),
+ get_pin_mapped_flag(obj),
obj->base.size / 1024,
obj->base.read_domains,
obj->base.write_domain);
@@ -435,6 +443,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
struct i915_ggtt *ggtt = &dev_priv->ggtt;
u32 count, mappable_count, purgeable_count;
u64 size, mappable_size, purgeable_size;
+ unsigned long pin_mapped_count = 0, pin_mapped_purgeable_count = 0;
+ u64 pin_mapped_size = 0, pin_mapped_purgeable_size = 0;
struct drm_i915_gem_object *obj;
struct drm_file *file;
struct i915_vma *vma;
@@ -468,6 +478,14 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
size += obj->base.size, ++count;
if (obj->madv == I915_MADV_DONTNEED)
purgeable_size += obj->base.size, ++purgeable_count;
+ if (obj->mapping) {
+ pin_mapped_count++;
+ pin_mapped_size += obj->base.size;
+ if (obj->pages_pin_count == 0) {
+ pin_mapped_purgeable_count++;
+ pin_mapped_purgeable_size += obj->base.size;
+ }
+ }
}
seq_printf(m, "%u unbound objects, %llu bytes\n", count, size);
@@ -485,6 +503,14 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
purgeable_size += obj->base.size;
++purgeable_count;
}
+ if (obj->mapping) {
+ pin_mapped_count++;
+ pin_mapped_size += obj->base.size;
+ if (obj->pages_pin_count == 0) {
+ pin_mapped_purgeable_count++;
+ pin_mapped_purgeable_size += obj->base.size;
+ }
+ }
}
seq_printf(m, "%u purgeable objects, %llu bytes\n",
purgeable_count, purgeable_size);
@@ -492,6 +518,10 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
mappable_count, mappable_size);
seq_printf(m, "%u fault mappable objects, %llu bytes\n",
count, size);
+ seq_printf(m,
+ "%lu [%lu] pin mapped objects, %llu [%llu] bytes [purgeable]\n",
+ pin_mapped_count, pin_mapped_purgeable_count,
+ pin_mapped_size, pin_mapped_purgeable_size);
seq_printf(m, "%llu [%llu] gtt total\n",
ggtt->base.total, ggtt->mappable_end - ggtt->base.start);
@@ -1219,12 +1249,12 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
rpdeclimit = I915_READ(GEN6_RP_DOWN_THRESHOLD);
rpstat = I915_READ(GEN6_RPSTAT1);
- rpupei = I915_READ(GEN6_RP_CUR_UP_EI);
- rpcurup = I915_READ(GEN6_RP_CUR_UP);
- rpprevup = I915_READ(GEN6_RP_PREV_UP);
- rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI);
- rpcurdown = I915_READ(GEN6_RP_CUR_DOWN);
- rpprevdown = I915_READ(GEN6_RP_PREV_DOWN);
+ rpupei = I915_READ(GEN6_RP_CUR_UP_EI) & GEN6_CURICONT_MASK;
+ rpcurup = I915_READ(GEN6_RP_CUR_UP) & GEN6_CURBSYTAVG_MASK;
+ rpprevup = I915_READ(GEN6_RP_PREV_UP) & GEN6_CURBSYTAVG_MASK;
+ rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI) & GEN6_CURIAVG_MASK;
+ rpcurdown = I915_READ(GEN6_RP_CUR_DOWN) & GEN6_CURBSYTAVG_MASK;
+ rpprevdown = I915_READ(GEN6_RP_PREV_DOWN) & GEN6_CURBSYTAVG_MASK;
if (IS_GEN9(dev))
cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT;
else if (IS_HASWELL(dev) || IS_BROADWELL(dev))
@@ -1264,21 +1294,21 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
seq_printf(m, "RPDECLIMIT: 0x%08x\n", rpdeclimit);
seq_printf(m, "RPNSWREQ: %dMHz\n", reqf);
seq_printf(m, "CAGF: %dMHz\n", cagf);
- seq_printf(m, "RP CUR UP EI: %dus\n", rpupei &
- GEN6_CURICONT_MASK);
- seq_printf(m, "RP CUR UP: %dus\n", rpcurup &
- GEN6_CURBSYTAVG_MASK);
- seq_printf(m, "RP PREV UP: %dus\n", rpprevup &
- GEN6_CURBSYTAVG_MASK);
+ seq_printf(m, "RP CUR UP EI: %d (%dus)\n",
+ rpupei, GT_PM_INTERVAL_TO_US(dev_priv, rpupei));
+ seq_printf(m, "RP CUR UP: %d (%dus)\n",
+ rpcurup, GT_PM_INTERVAL_TO_US(dev_priv, rpcurup));
+ seq_printf(m, "RP PREV UP: %d (%dus)\n",
+ rpprevup, GT_PM_INTERVAL_TO_US(dev_priv, rpprevup));
seq_printf(m, "Up threshold: %d%%\n",
dev_priv->rps.up_threshold);
- seq_printf(m, "RP CUR DOWN EI: %dus\n", rpdownei &
- GEN6_CURIAVG_MASK);
- seq_printf(m, "RP CUR DOWN: %dus\n", rpcurdown &
- GEN6_CURBSYTAVG_MASK);
- seq_printf(m, "RP PREV DOWN: %dus\n", rpprevdown &
- GEN6_CURBSYTAVG_MASK);
+ seq_printf(m, "RP CUR DOWN EI: %d (%dus)\n",
+ rpdownei, GT_PM_INTERVAL_TO_US(dev_priv, rpdownei));
+ seq_printf(m, "RP CUR DOWN: %d (%dus)\n",
+ rpcurdown, GT_PM_INTERVAL_TO_US(dev_priv, rpcurdown));
+ seq_printf(m, "RP PREV DOWN: %d (%dus)\n",
+ rpprevdown, GT_PM_INTERVAL_TO_US(dev_priv, rpprevdown));
seq_printf(m, "Down threshold: %d%%\n",
dev_priv->rps.down_threshold);
@@ -1472,12 +1502,11 @@ static int i915_forcewake_domains(struct seq_file *m, void *data)
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_uncore_forcewake_domain *fw_domain;
- int i;
spin_lock_irq(&dev_priv->uncore.lock);
- for_each_fw_domain(fw_domain, dev_priv, i) {
+ for_each_fw_domain(fw_domain, dev_priv) {
seq_printf(m, "%s.wake_count = %u\n",
- intel_uncore_forcewake_domain_to_str(i),
+ intel_uncore_forcewake_domain_to_str(fw_domain->id),
fw_domain->wake_count);
}
spin_unlock_irq(&dev_priv->uncore.lock);
@@ -2413,10 +2442,11 @@ static int i915_llc(struct seq_file *m, void *data)
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ const bool edram = INTEL_GEN(dev_priv) > 8;
- /* Size calculation for LLC is a bit of a pain. Ignore for now. */
seq_printf(m, "LLC: %s\n", yesno(HAS_LLC(dev)));
- seq_printf(m, "eLLC: %zuMB\n", dev_priv->ellc_size);
+ seq_printf(m, "%s: %lluMB\n", edram ? "eDRAM" : "eLLC",
+ intel_uncore_edram_size(dev_priv)/1024/1024);
return 0;
}
@@ -4731,7 +4761,7 @@ i915_wedged_get(void *data, u64 *val)
struct drm_device *dev = data;
struct drm_i915_private *dev_priv = dev->dev_private;
- *val = atomic_read(&dev_priv->gpu_error.reset_counter);
+ *val = i915_terminally_wedged(&dev_priv->gpu_error);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index b377753717d1..5c7615041b31 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -257,13 +257,6 @@ static int i915_get_bridge_dev(struct drm_device *dev)
return 0;
}
-#define MCHBAR_I915 0x44
-#define MCHBAR_I965 0x48
-#define MCHBAR_SIZE (4*4096)
-
-#define DEVEN_REG 0x54
-#define DEVEN_MCHBAR_EN (1 << 28)
-
/* Allocate space for the MCH regs if needed, return nonzero on error */
static int
intel_alloc_mchbar_resource(struct drm_device *dev)
@@ -325,7 +318,7 @@ intel_setup_mchbar(struct drm_device *dev)
dev_priv->mchbar_need_disable = false;
if (IS_I915G(dev) || IS_I915GM(dev)) {
- pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
+ pci_read_config_dword(dev_priv->bridge_dev, DEVEN, &temp);
enabled = !!(temp & DEVEN_MCHBAR_EN);
} else {
pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
@@ -343,7 +336,7 @@ intel_setup_mchbar(struct drm_device *dev)
/* Space is allocated or reserved, so enable it. */
if (IS_I915G(dev) || IS_I915GM(dev)) {
- pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG,
+ pci_write_config_dword(dev_priv->bridge_dev, DEVEN,
temp | DEVEN_MCHBAR_EN);
} else {
pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
@@ -356,17 +349,24 @@ intel_teardown_mchbar(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
- u32 temp;
if (dev_priv->mchbar_need_disable) {
if (IS_I915G(dev) || IS_I915GM(dev)) {
- pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
- temp &= ~DEVEN_MCHBAR_EN;
- pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, temp);
+ u32 deven_val;
+
+ pci_read_config_dword(dev_priv->bridge_dev, DEVEN,
+ &deven_val);
+ deven_val &= ~DEVEN_MCHBAR_EN;
+ pci_write_config_dword(dev_priv->bridge_dev, DEVEN,
+ deven_val);
} else {
- pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
- temp &= ~1;
- pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp);
+ u32 mchbar_val;
+
+ pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg,
+ &mchbar_val);
+ mchbar_val &= ~1;
+ pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg,
+ mchbar_val);
}
}
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 29b4e79c85a6..d37c0a671eed 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -567,10 +567,9 @@ static void intel_suspend_encoders(struct drm_i915_private *dev_priv)
drm_modeset_unlock_all(dev);
}
-static int intel_suspend_complete(struct drm_i915_private *dev_priv);
static int vlv_resume_prepare(struct drm_i915_private *dev_priv,
bool rpm_resume);
-static int bxt_resume_prepare(struct drm_i915_private *dev_priv);
+static int vlv_suspend_complete(struct drm_i915_private *dev_priv);
static bool suspend_to_idle(struct drm_i915_private *dev_priv)
{
@@ -640,8 +639,7 @@ static int i915_drm_suspend(struct drm_device *dev)
intel_display_set_init_power(dev_priv, false);
- if (HAS_CSR(dev_priv))
- flush_work(&dev_priv->csr.work);
+ intel_csr_ucode_suspend(dev_priv);
out:
enable_rpm_wakeref_asserts(dev_priv);
@@ -657,7 +655,8 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
disable_rpm_wakeref_asserts(dev_priv);
- fw_csr = suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload;
+ fw_csr = !IS_BROXTON(dev_priv) &&
+ suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload;
/*
* In case of firmware assisted context save/restore don't manually
* deinit the power domains. This also means the CSR/DMC firmware will
@@ -668,7 +667,13 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
if (!fw_csr)
intel_power_domains_suspend(dev_priv);
- ret = intel_suspend_complete(dev_priv);
+ ret = 0;
+ if (IS_BROXTON(dev_priv))
+ bxt_enable_dc9(dev_priv);
+ else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+ hsw_enable_pc8(dev_priv);
+ else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+ ret = vlv_suspend_complete(dev_priv);
if (ret) {
DRM_ERROR("Suspend complete failed: %d\n", ret);
@@ -732,6 +737,8 @@ static int i915_drm_resume(struct drm_device *dev)
disable_rpm_wakeref_asserts(dev_priv);
+ intel_csr_ucode_resume(dev_priv);
+
mutex_lock(&dev->struct_mutex);
i915_gem_restore_gtt_mappings(dev);
mutex_unlock(&dev->struct_mutex);
@@ -802,7 +809,7 @@ static int i915_drm_resume(struct drm_device *dev)
static int i915_drm_resume_early(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int ret = 0;
+ int ret;
/*
* We have a resume ordering issue with the snd-hda driver also
@@ -813,6 +820,36 @@ static int i915_drm_resume_early(struct drm_device *dev)
* FIXME: This should be solved with a special hdmi sink device or
* similar so that power domains can be employed.
*/
+
+ /*
+ * Note that we need to set the power state explicitly, since we
+ * powered off the device during freeze and the PCI core won't power
+ * it back up for us during thaw. Powering off the device during
+ * freeze is not a hard requirement though, and during the
+ * suspend/resume phases the PCI core makes sure we get here with the
+ * device powered on. So in case we change our freeze logic and keep
+ * the device powered we can also remove the following set power state
+ * call.
+ */
+ ret = pci_set_power_state(dev->pdev, PCI_D0);
+ if (ret) {
+ DRM_ERROR("failed to set PCI D0 power state (%d)\n", ret);
+ goto out;
+ }
+
+ /*
+ * Note that pci_enable_device() first enables any parent bridge
+ * device and only then sets the power state for this device. The
+ * bridge enabling is a nop though, since bridge devices are resumed
+ * first. The order of enabling power and enabling the device is
+ * imposed by the PCI core as described above, so here we preserve the
+ * same order for the freeze/thaw phases.
+ *
+ * TODO: eventually we should remove pci_disable_device() /
+ * pci_enable_enable_device() from suspend/resume. Due to how they
+ * depend on the device enable refcount we can't anyway depend on them
+ * disabling/enabling the device.
+ */
if (pci_enable_device(dev->pdev)) {
ret = -EIO;
goto out;
@@ -830,21 +867,25 @@ static int i915_drm_resume_early(struct drm_device *dev)
intel_uncore_early_sanitize(dev, true);
- if (IS_BROXTON(dev))
- ret = bxt_resume_prepare(dev_priv);
- else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+ if (IS_BROXTON(dev)) {
+ if (!dev_priv->suspended_to_idle)
+ gen9_sanitize_dc_state(dev_priv);
+ bxt_disable_dc9(dev_priv);
+ } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
hsw_disable_pc8(dev_priv);
+ }
intel_uncore_sanitize(dev);
- if (!(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload))
+ if (IS_BROXTON(dev_priv) ||
+ !(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload))
intel_power_domains_init_hw(dev_priv, true);
+ enable_rpm_wakeref_asserts(dev_priv);
+
out:
dev_priv->suspended_to_idle = false;
- enable_rpm_wakeref_asserts(dev_priv);
-
return ret;
}
@@ -880,23 +921,32 @@ int i915_resume_switcheroo(struct drm_device *dev)
int i915_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- bool simulated;
+ struct i915_gpu_error *error = &dev_priv->gpu_error;
+ unsigned reset_counter;
int ret;
intel_reset_gt_powersave(dev);
mutex_lock(&dev->struct_mutex);
- i915_gem_reset(dev);
+ /* Clear any previous failed attempts at recovery. Time to try again. */
+ atomic_andnot(I915_WEDGED, &error->reset_counter);
- simulated = dev_priv->gpu_error.stop_rings != 0;
+ /* Clear the reset-in-progress flag and increment the reset epoch. */
+ reset_counter = atomic_inc_return(&error->reset_counter);
+ if (WARN_ON(__i915_reset_in_progress(reset_counter))) {
+ ret = -EIO;
+ goto error;
+ }
+
+ i915_gem_reset(dev);
ret = intel_gpu_reset(dev, ALL_ENGINES);
/* Also reset the gpu hangman. */
- if (simulated) {
+ if (error->stop_rings != 0) {
DRM_INFO("Simulated gpu hang, resetting stop_rings\n");
- dev_priv->gpu_error.stop_rings = 0;
+ error->stop_rings = 0;
if (ret == -ENODEV) {
DRM_INFO("Reset not implemented, but ignoring "
"error for simulated gpu hangs\n");
@@ -908,9 +958,11 @@ int i915_reset(struct drm_device *dev)
pr_notice("drm/i915: Resetting chip after gpu hang\n");
if (ret) {
- DRM_ERROR("Failed to reset chip: %i\n", ret);
- mutex_unlock(&dev->struct_mutex);
- return ret;
+ if (ret != -ENODEV)
+ DRM_ERROR("Failed to reset chip: %i\n", ret);
+ else
+ DRM_DEBUG_DRIVER("GPU reset disabled\n");
+ goto error;
}
intel_overlay_reset(dev_priv);
@@ -929,20 +981,14 @@ int i915_reset(struct drm_device *dev)
* was running at the time of the reset (i.e. we weren't VT
* switched away).
*/
-
- /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */
- dev_priv->gpu_error.reload_in_reset = true;
-
ret = i915_gem_init_hw(dev);
-
- dev_priv->gpu_error.reload_in_reset = false;
-
- mutex_unlock(&dev->struct_mutex);
if (ret) {
DRM_ERROR("Failed hw init on reset %d\n", ret);
- return ret;
+ goto error;
}
+ mutex_unlock(&dev->struct_mutex);
+
/*
* rps/rc6 re-init is necessary to restore state lost after the
* reset and the re-install of gt irqs. Skip for ironlake per
@@ -953,6 +999,11 @@ int i915_reset(struct drm_device *dev)
intel_enable_gt_powersave(dev);
return 0;
+
+error:
+ atomic_or(I915_WEDGED, &error->reset_counter);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
}
static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -1059,44 +1110,6 @@ static int i915_pm_resume(struct device *dev)
return i915_drm_resume(drm_dev);
}
-static int hsw_suspend_complete(struct drm_i915_private *dev_priv)
-{
- hsw_enable_pc8(dev_priv);
-
- return 0;
-}
-
-static int bxt_suspend_complete(struct drm_i915_private *dev_priv)
-{
- struct drm_device *dev = dev_priv->dev;
-
- /* TODO: when DC5 support is added disable DC5 here. */
-
- broxton_ddi_phy_uninit(dev);
- broxton_uninit_cdclk(dev);
- bxt_enable_dc9(dev_priv);
-
- return 0;
-}
-
-static int bxt_resume_prepare(struct drm_i915_private *dev_priv)
-{
- struct drm_device *dev = dev_priv->dev;
-
- /* TODO: when CSR FW support is added make sure the FW is loaded */
-
- bxt_disable_dc9(dev_priv);
-
- /*
- * TODO: when DC5 support is added enable DC5 here if the CSR FW
- * is available.
- */
- broxton_init_cdclk(dev);
- broxton_ddi_phy_init(dev);
-
- return 0;
-}
-
/*
* Save all Gunit registers that may be lost after a D3 and a subsequent
* S0i[R123] transition. The list of registers needing a save/restore is
@@ -1502,7 +1515,16 @@ static int intel_runtime_suspend(struct device *device)
intel_suspend_gt_powersave(dev);
intel_runtime_pm_disable_interrupts(dev_priv);
- ret = intel_suspend_complete(dev_priv);
+ ret = 0;
+ if (IS_BROXTON(dev_priv)) {
+ bxt_display_core_uninit(dev_priv);
+ bxt_enable_dc9(dev_priv);
+ } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
+ hsw_enable_pc8(dev_priv);
+ } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+ ret = vlv_suspend_complete(dev_priv);
+ }
+
if (ret) {
DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret);
intel_runtime_pm_enable_interrupts(dev_priv);
@@ -1576,12 +1598,17 @@ static int intel_runtime_resume(struct device *device)
if (IS_GEN6(dev_priv))
intel_init_pch_refclk(dev);
- if (IS_BROXTON(dev))
- ret = bxt_resume_prepare(dev_priv);
- else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+ if (IS_BROXTON(dev)) {
+ bxt_disable_dc9(dev_priv);
+ bxt_display_core_init(dev_priv, true);
+ if (dev_priv->csr.dmc_payload &&
+ (dev_priv->csr.allowed_dc_mask & DC_STATE_EN_UPTO_DC5))
+ gen9_enable_dc5(dev_priv);
+ } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
hsw_disable_pc8(dev_priv);
- else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+ } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
ret = vlv_resume_prepare(dev_priv, true);
+ }
/*
* No point of rolling back things in case of an error, as the best
@@ -1612,26 +1639,6 @@ static int intel_runtime_resume(struct device *device)
return ret;
}
-/*
- * This function implements common functionality of runtime and system
- * suspend sequence.
- */
-static int intel_suspend_complete(struct drm_i915_private *dev_priv)
-{
- int ret;
-
- if (IS_BROXTON(dev_priv))
- ret = bxt_suspend_complete(dev_priv);
- else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
- ret = hsw_suspend_complete(dev_priv);
- else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
- ret = vlv_suspend_complete(dev_priv);
- else
- ret = 0;
-
- return ret;
-}
-
static const struct dev_pm_ops i915_pm_ops = {
/*
* S0ix (via system suspend) and S3 event handlers [PMSG_SUSPEND,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index a9c8211c8e5e..9d7b54ea14f9 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -33,34 +33,40 @@
#include <uapi/drm/i915_drm.h>
#include <uapi/drm/drm_fourcc.h>
-#include <drm/drmP.h>
-#include "i915_params.h"
-#include "i915_reg.h"
-#include "intel_bios.h"
-#include "intel_ringbuffer.h"
-#include "intel_lrc.h"
-#include "i915_gem_gtt.h"
-#include "i915_gem_render_state.h"
#include <linux/io-mapping.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
-#include <drm/intel-gtt.h>
-#include <drm/drm_legacy.h> /* for struct drm_dma_handle */
-#include <drm/drm_gem.h>
#include <linux/backlight.h>
#include <linux/hashtable.h>
#include <linux/intel-iommu.h>
#include <linux/kref.h>
#include <linux/pm_qos.h>
-#include "intel_guc.h"
+#include <linux/shmem_fs.h>
+
+#include <drm/drmP.h>
+#include <drm/intel-gtt.h>
+#include <drm/drm_legacy.h> /* for struct drm_dma_handle */
+#include <drm/drm_gem.h>
+
+#include "i915_params.h"
+#include "i915_reg.h"
+
+#include "intel_bios.h"
#include "intel_dpll_mgr.h"
+#include "intel_guc.h"
+#include "intel_lrc.h"
+#include "intel_ringbuffer.h"
+
+#include "i915_gem.h"
+#include "i915_gem_gtt.h"
+#include "i915_gem_render_state.h"
/* General customization:
*/
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
-#define DRIVER_DATE "20160411"
+#define DRIVER_DATE "20160425"
#undef WARN_ON
/* Many gcc seem to no see through this and fall over :( */
@@ -634,6 +640,13 @@ enum forcewake_domains {
FORCEWAKE_MEDIA)
};
+#define FW_REG_READ (1)
+#define FW_REG_WRITE (2)
+
+enum forcewake_domains
+intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
+ i915_reg_t reg, unsigned int op);
+
struct intel_uncore_funcs {
void (*force_wake_get)(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
@@ -666,8 +679,9 @@ struct intel_uncore {
struct intel_uncore_forcewake_domain {
struct drm_i915_private *i915;
enum forcewake_domain_id id;
+ enum forcewake_domains mask;
unsigned wake_count;
- struct timer_list timer;
+ struct hrtimer timer;
i915_reg_t reg_set;
u32 val_set;
u32 val_clear;
@@ -680,14 +694,14 @@ struct intel_uncore {
};
/* Iterate over initialised fw domains */
-#define for_each_fw_domain_mask(domain__, mask__, dev_priv__, i__) \
- for ((i__) = 0, (domain__) = &(dev_priv__)->uncore.fw_domain[0]; \
- (i__) < FW_DOMAIN_ID_COUNT; \
- (i__)++, (domain__) = &(dev_priv__)->uncore.fw_domain[i__]) \
- for_each_if (((mask__) & (dev_priv__)->uncore.fw_domains) & (1 << (i__)))
+#define for_each_fw_domain_masked(domain__, mask__, dev_priv__) \
+ for ((domain__) = &(dev_priv__)->uncore.fw_domain[0]; \
+ (domain__) < &(dev_priv__)->uncore.fw_domain[FW_DOMAIN_ID_COUNT]; \
+ (domain__)++) \
+ for_each_if ((mask__) & (domain__)->mask)
-#define for_each_fw_domain(domain__, dev_priv__, i__) \
- for_each_fw_domain_mask(domain__, FORCEWAKE_ALL, dev_priv__, i__)
+#define for_each_fw_domain(domain__, dev_priv__) \
+ for_each_fw_domain_masked(domain__, FORCEWAKE_ALL, dev_priv__)
#define CSR_VERSION(major, minor) ((major) << 16 | (minor))
#define CSR_VERSION_MAJOR(version) ((version) >> 16)
@@ -996,6 +1010,7 @@ struct intel_fbc_work;
struct intel_gmbus {
struct i2c_adapter adapter;
+#define GMBUS_FORCE_BIT_RETRY (1U << 31)
u32 force_bit;
u32 reg0;
i915_reg_t gpio_reg;
@@ -1385,9 +1400,6 @@ struct i915_gpu_error {
/* For missed irq/seqno simulation. */
unsigned int test_irq_rings;
-
- /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */
- bool reload_in_reset;
};
enum modeset_restore {
@@ -1444,6 +1456,7 @@ struct intel_vbt_data {
unsigned int lvds_use_ssc:1;
unsigned int display_clock_mode:1;
unsigned int fdi_rx_polarity_inverted:1;
+ unsigned int panel_type:4;
int lvds_ssc_freq;
unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
@@ -1863,7 +1876,7 @@ struct drm_i915_private {
struct intel_l3_parity l3_parity;
/* Cannot be determined by PCIID. You must always read a register. */
- size_t ellc_size;
+ u32 edram_cap;
/* gen6+ rps state */
struct intel_gen6_power_mgmt rps;
@@ -1911,6 +1924,7 @@ struct drm_i915_private {
* crappiness (can't read out DPLL_MD for pipes B & C).
*/
u32 chv_dpll_md[I915_MAX_PIPES];
+ u32 bxt_phy_grc;
u32 suspend_count;
bool suspended_to_idle;
@@ -2237,6 +2251,7 @@ struct drm_i915_gem_request {
/** On Which ring this request was generated */
struct drm_i915_private *i915;
struct intel_engine_cs *engine;
+ unsigned reset_counter;
/** GEM sequence number associated with the previous request,
* when the HWS breadcrumb is equal to this the GPU is processing
@@ -2317,7 +2332,6 @@ struct drm_i915_gem_request {
struct drm_i915_gem_request * __must_check
i915_gem_request_alloc(struct intel_engine_cs *engine,
struct intel_context *ctx);
-void i915_gem_request_cancel(struct drm_i915_gem_request *req);
void i915_gem_request_free(struct kref *req_ref);
int i915_gem_request_add_to_client(struct drm_i915_gem_request *req,
struct drm_file *file);
@@ -2487,6 +2501,7 @@ struct drm_i915_cmd_table {
__p; \
})
#define INTEL_INFO(p) (&__I915__(p)->info)
+#define INTEL_GEN(p) (INTEL_INFO(p)->gen)
#define INTEL_DEVID(p) (INTEL_INFO(p)->device_id)
#define INTEL_REVID(p) (__I915__(p)->dev->pdev->revision)
@@ -2613,8 +2628,9 @@ struct drm_i915_cmd_table {
#define HAS_VEBOX(dev) (INTEL_INFO(dev)->ring_mask & VEBOX_RING)
#define HAS_LLC(dev) (INTEL_INFO(dev)->has_llc)
#define HAS_SNOOP(dev) (INTEL_INFO(dev)->has_snoop)
+#define HAS_EDRAM(dev) (__I915__(dev)->edram_cap & EDRAM_ENABLED)
#define HAS_WT(dev) ((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \
- __I915__(dev)->ellc_size)
+ HAS_EDRAM(dev))
#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
#define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6)
@@ -2631,8 +2647,9 @@ struct drm_i915_cmd_table {
/* WaRsDisableCoarsePowerGating:skl,bxt */
#define NEEDS_WaRsDisableCoarsePowerGating(dev) (IS_BXT_REVID(dev, 0, BXT_REVID_A1) || \
- ((IS_SKL_GT3(dev) || IS_SKL_GT4(dev)) && \
- IS_SKL_REVID(dev, 0, SKL_REVID_F0)))
+ IS_SKL_GT3(dev) || \
+ IS_SKL_GT4(dev))
+
/*
* dp aux and gmbus irq on gen4 seems to be able to generate legacy interrupts
* even when in MSI mode. This results in spurious interrupt warnings if the
@@ -2667,7 +2684,7 @@ struct drm_i915_cmd_table {
#define HAS_RUNTIME_PM(dev) (IS_GEN6(dev) || IS_HASWELL(dev) || \
IS_BROADWELL(dev) || IS_VALLEYVIEW(dev) || \
IS_CHERRYVIEW(dev) || IS_SKYLAKE(dev) || \
- IS_KABYLAKE(dev))
+ IS_KABYLAKE(dev) || IS_BROXTON(dev))
#define HAS_RC6(dev) (INTEL_INFO(dev)->gen >= 6)
#define HAS_RC6p(dev) (INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
@@ -2791,6 +2808,8 @@ void intel_uncore_forcewake_get__locked(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
enum forcewake_domains domains);
+u64 intel_uncore_edram_size(struct drm_i915_private *dev_priv);
+
void assert_forcewakes_inactive(struct drm_i915_private *dev_priv);
static inline bool intel_vgpu_active(struct drm_device *dev)
{
@@ -2869,7 +2888,6 @@ int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_gem_execbuffer_move_to_active(struct list_head *vmas,
struct drm_i915_gem_request *req);
-void i915_gem_execbuffer_retire_commands(struct i915_execbuffer_params *params);
int i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
struct drm_i915_gem_execbuffer2 *args,
struct list_head *vmas);
@@ -3000,9 +3018,11 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
* pages and then returns a contiguous mapping of the backing storage into
* the kernel address space.
*
- * The caller must hold the struct_mutex.
+ * The caller must hold the struct_mutex, and is responsible for calling
+ * i915_gem_object_unpin_map() when the mapping is no longer required.
*
- * Returns the pointer through which to access the backing storage.
+ * Returns the pointer through which to access the mapped object, or an
+ * ERR_PTR() on error.
*/
void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj);
@@ -3069,23 +3089,45 @@ i915_gem_find_active_request(struct intel_engine_cs *engine);
bool i915_gem_retire_requests(struct drm_device *dev);
void i915_gem_retire_requests_ring(struct intel_engine_cs *engine);
-int __must_check i915_gem_check_wedge(struct i915_gpu_error *error,
- bool interruptible);
+
+static inline u32 i915_reset_counter(struct i915_gpu_error *error)
+{
+ return atomic_read(&error->reset_counter);
+}
+
+static inline bool __i915_reset_in_progress(u32 reset)
+{
+ return unlikely(reset & I915_RESET_IN_PROGRESS_FLAG);
+}
+
+static inline bool __i915_reset_in_progress_or_wedged(u32 reset)
+{
+ return unlikely(reset & (I915_RESET_IN_PROGRESS_FLAG | I915_WEDGED));
+}
+
+static inline bool __i915_terminally_wedged(u32 reset)
+{
+ return unlikely(reset & I915_WEDGED);
+}
static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
{
- return unlikely(atomic_read(&error->reset_counter)
- & (I915_RESET_IN_PROGRESS_FLAG | I915_WEDGED));
+ return __i915_reset_in_progress(i915_reset_counter(error));
+}
+
+static inline bool i915_reset_in_progress_or_wedged(struct i915_gpu_error *error)
+{
+ return __i915_reset_in_progress_or_wedged(i915_reset_counter(error));
}
static inline bool i915_terminally_wedged(struct i915_gpu_error *error)
{
- return atomic_read(&error->reset_counter) & I915_WEDGED;
+ return __i915_terminally_wedged(i915_reset_counter(error));
}
static inline u32 i915_reset_count(struct i915_gpu_error *error)
{
- return ((atomic_read(&error->reset_counter) & ~I915_WEDGED) + 1) / 2;
+ return ((i915_reset_counter(error) & ~I915_WEDGED) + 1) / 2;
}
static inline bool i915_stop_ring_allow_ban(struct drm_i915_private *dev_priv)
@@ -3118,7 +3160,6 @@ void __i915_add_request(struct drm_i915_gem_request *req,
#define i915_add_request_no_flush(req) \
__i915_add_request(req, NULL, false)
int __i915_wait_request(struct drm_i915_gem_request *req,
- unsigned reset_counter,
bool interruptible,
s64 *timeout,
struct intel_rps_client *rps);
@@ -3455,6 +3496,7 @@ extern int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
bool enable);
extern int intel_opregion_notify_adapter(struct drm_device *dev,
pci_power_t state);
+extern int intel_opregion_get_panel_type(struct drm_device *dev);
#else
static inline int intel_opregion_setup(struct drm_device *dev) { return 0; }
static inline void intel_opregion_init(struct drm_device *dev) { return; }
@@ -3470,6 +3512,10 @@ intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
{
return 0;
}
+static inline int intel_opregion_get_panel_type(struct drm_device *dev)
+{
+ return -ENODEV;
+}
#endif
/* intel_acpi.c */
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index f4abf3abd572..261a3ef72828 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -32,14 +32,13 @@
#include "i915_vgpu.h"
#include "i915_trace.h"
#include "intel_drv.h"
+#include "intel_mocs.h"
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/swap.h>
#include <linux/pci.h>
#include <linux/dma-buf.h>
-#define RQ_BUG_ON(expr)
-
static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
static void
@@ -85,9 +84,7 @@ i915_gem_wait_for_error(struct i915_gpu_error *error)
{
int ret;
-#define EXIT_COND (!i915_reset_in_progress(error) || \
- i915_terminally_wedged(error))
- if (EXIT_COND)
+ if (!i915_reset_in_progress(error))
return 0;
/*
@@ -96,17 +93,16 @@ i915_gem_wait_for_error(struct i915_gpu_error *error)
* we should simply try to bail out and fail as gracefully as possible.
*/
ret = wait_event_interruptible_timeout(error->reset_queue,
- EXIT_COND,
+ !i915_reset_in_progress(error),
10*HZ);
if (ret == 0) {
DRM_ERROR("Timed out waiting for the gpu reset to complete\n");
return -EIO;
} else if (ret < 0) {
return ret;
+ } else {
+ return 0;
}
-#undef EXIT_COND
-
- return 0;
}
int i915_mutex_lock_interruptible(struct drm_device *dev)
@@ -211,11 +207,10 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj)
BUG_ON(obj->madv == __I915_MADV_PURGED);
ret = i915_gem_object_set_to_cpu_domain(obj, true);
- if (ret) {
+ if (WARN_ON(ret)) {
/* In the event of a disaster, abandon all caches and
* hope for the best.
*/
- WARN_ON(ret != -EIO);
obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
}
@@ -1110,27 +1105,19 @@ put_rpm:
return ret;
}
-int
-i915_gem_check_wedge(struct i915_gpu_error *error,
- bool interruptible)
+static int
+i915_gem_check_wedge(unsigned reset_counter, bool interruptible)
{
- if (i915_reset_in_progress(error)) {
+ if (__i915_terminally_wedged(reset_counter))
+ return -EIO;
+
+ if (__i915_reset_in_progress(reset_counter)) {
/* Non-interruptible callers can't handle -EAGAIN, hence return
* -EIO unconditionally for these. */
if (!interruptible)
return -EIO;
- /* Recovery complete, but the reset failed ... */
- if (i915_terminally_wedged(error))
- return -EIO;
-
- /*
- * Check if GPU Reset is in progress - we need intel_ring_begin
- * to work properly to reinit the hw state while the gpu is
- * still marked as reset-in-progress. Handle this with a flag.
- */
- if (!error->reload_in_reset)
- return -EAGAIN;
+ return -EAGAIN;
}
return 0;
@@ -1224,7 +1211,6 @@ static int __i915_spin_request(struct drm_i915_gem_request *req, int state)
/**
* __i915_wait_request - wait until execution of request has finished
* @req: duh!
- * @reset_counter: reset sequence associated with the given request
* @interruptible: do an interruptible wait (normally yes)
* @timeout: in - how long to wait (NULL forever); out - how much time remaining
*
@@ -1239,7 +1225,6 @@ static int __i915_spin_request(struct drm_i915_gem_request *req, int state)
* errno with remaining time filled in timeout argument.
*/
int __i915_wait_request(struct drm_i915_gem_request *req,
- unsigned reset_counter,
bool interruptible,
s64 *timeout,
struct intel_rps_client *rps)
@@ -1300,13 +1285,14 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
prepare_to_wait(&engine->irq_queue, &wait, state);
/* We need to check whether any gpu reset happened in between
- * the caller grabbing the seqno and now ... */
- if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) {
- /* ... but upgrade the -EAGAIN to an -EIO if the gpu
- * is truely gone. */
- ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible);
- if (ret == 0)
- ret = -EAGAIN;
+ * the request being submitted and now. If a reset has occurred,
+ * the request is effectively complete (we either are in the
+ * process of or have discarded the rendering and completely
+ * reset the GPU. The results of the request are lost and we
+ * are free to continue on with the original operation.
+ */
+ if (req->reset_counter != i915_reset_counter(&dev_priv->gpu_error)) {
+ ret = 0;
break;
}
@@ -1458,26 +1444,15 @@ __i915_gem_request_retire__upto(struct drm_i915_gem_request *req)
int
i915_wait_request(struct drm_i915_gem_request *req)
{
- struct drm_device *dev;
- struct drm_i915_private *dev_priv;
+ struct drm_i915_private *dev_priv = req->i915;
bool interruptible;
int ret;
- BUG_ON(req == NULL);
-
- dev = req->engine->dev;
- dev_priv = dev->dev_private;
interruptible = dev_priv->mm.interruptible;
- BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-
- ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible);
- if (ret)
- return ret;
+ BUG_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
- ret = __i915_wait_request(req,
- atomic_read(&dev_priv->gpu_error.reset_counter),
- interruptible, NULL, NULL);
+ ret = __i915_wait_request(req, interruptible, NULL, NULL);
if (ret)
return ret;
@@ -1521,7 +1496,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
i915_gem_object_retire__read(obj, i);
}
- RQ_BUG_ON(obj->active);
+ GEM_BUG_ON(obj->active);
}
return 0;
@@ -1552,7 +1527,6 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_request *requests[I915_NUM_ENGINES];
- unsigned reset_counter;
int ret, i, n = 0;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
@@ -1561,12 +1535,6 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
if (!obj->active)
return 0;
- ret = i915_gem_check_wedge(&dev_priv->gpu_error, true);
- if (ret)
- return ret;
-
- reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
-
if (readonly) {
struct drm_i915_gem_request *req;
@@ -1588,9 +1556,9 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
}
mutex_unlock(&dev->struct_mutex);
+ ret = 0;
for (i = 0; ret == 0 && i < n; i++)
- ret = __i915_wait_request(requests[i], reset_counter, true,
- NULL, rps);
+ ret = __i915_wait_request(requests[i], true, NULL, rps);
mutex_lock(&dev->struct_mutex);
for (i = 0; i < n; i++) {
@@ -1964,11 +1932,27 @@ out:
void
i915_gem_release_mmap(struct drm_i915_gem_object *obj)
{
+ /* Serialisation between user GTT access and our code depends upon
+ * revoking the CPU's PTE whilst the mutex is held. The next user
+ * pagefault then has to wait until we release the mutex.
+ */
+ lockdep_assert_held(&obj->base.dev->struct_mutex);
+
if (!obj->fault_mappable)
return;
drm_vma_node_unmap(&obj->base.vma_node,
obj->base.dev->anon_inode->i_mapping);
+
+ /* Ensure that the CPU's PTE are revoked and there are not outstanding
+ * memory transactions from userspace before we return. The TLB
+ * flushing implied above by changing the PTE above *should* be
+ * sufficient, an extra barrier here just provides us with a bit
+ * of paranoid documentation about our requirement to serialise
+ * memory writes before touching registers / GSM.
+ */
+ wmb();
+
obj->fault_mappable = false;
}
@@ -2177,11 +2161,10 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
BUG_ON(obj->madv == __I915_MADV_PURGED);
ret = i915_gem_object_set_to_cpu_domain(obj, true);
- if (ret) {
+ if (WARN_ON(ret)) {
/* In the event of a disaster, abandon all caches and
* hope for the best.
*/
- WARN_ON(ret != -EIO);
i915_gem_clflush_object(obj, true);
obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU;
}
@@ -2470,8 +2453,8 @@ void i915_vma_move_to_active(struct i915_vma *vma,
static void
i915_gem_object_retire__write(struct drm_i915_gem_object *obj)
{
- RQ_BUG_ON(obj->last_write_req == NULL);
- RQ_BUG_ON(!(obj->active & intel_engine_flag(obj->last_write_req->engine)));
+ GEM_BUG_ON(obj->last_write_req == NULL);
+ GEM_BUG_ON(!(obj->active & intel_engine_flag(obj->last_write_req->engine)));
i915_gem_request_assign(&obj->last_write_req, NULL);
intel_fb_obj_flush(obj, true, ORIGIN_CS);
@@ -2482,8 +2465,8 @@ i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring)
{
struct i915_vma *vma;
- RQ_BUG_ON(obj->last_read_req[ring] == NULL);
- RQ_BUG_ON(!(obj->active & (1 << ring)));
+ GEM_BUG_ON(obj->last_read_req[ring] == NULL);
+ GEM_BUG_ON(!(obj->active & (1 << ring)));
list_del_init(&obj->engine_list[ring]);
i915_gem_request_assign(&obj->last_read_req[ring], NULL);
@@ -2743,6 +2726,7 @@ __i915_gem_request_alloc(struct intel_engine_cs *engine,
struct drm_i915_gem_request **req_out)
{
struct drm_i915_private *dev_priv = to_i915(engine->dev);
+ unsigned reset_counter = i915_reset_counter(&dev_priv->gpu_error);
struct drm_i915_gem_request *req;
int ret;
@@ -2751,6 +2735,14 @@ __i915_gem_request_alloc(struct intel_engine_cs *engine,
*req_out = NULL;
+ /* ABI: Before userspace accesses the GPU (e.g. execbuffer), report
+ * EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex
+ * and restart.
+ */
+ ret = i915_gem_check_wedge(reset_counter, dev_priv->mm.interruptible);
+ if (ret)
+ return ret;
+
req = kmem_cache_zalloc(dev_priv->requests, GFP_KERNEL);
if (req == NULL)
return -ENOMEM;
@@ -2762,6 +2754,7 @@ __i915_gem_request_alloc(struct intel_engine_cs *engine,
kref_init(&req->ref);
req->i915 = dev_priv;
req->engine = engine;
+ req->reset_counter = reset_counter;
req->ctx = ctx;
i915_gem_context_reference(req->ctx);
@@ -2791,7 +2784,8 @@ __i915_gem_request_alloc(struct intel_engine_cs *engine,
* fully prepared. Thus it can be cleaned up using the proper
* free code.
*/
- i915_gem_request_cancel(req);
+ intel_ring_reserved_space_cancel(req->ringbuf);
+ i915_gem_request_unreference(req);
return ret;
}
@@ -2828,13 +2822,6 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
return err ? ERR_PTR(err) : req;
}
-void i915_gem_request_cancel(struct drm_i915_gem_request *req)
-{
- intel_ring_reserved_space_cancel(req->ringbuf);
-
- i915_gem_request_unreference(req);
-}
-
struct drm_i915_gem_request *
i915_gem_find_active_request(struct intel_engine_cs *engine)
{
@@ -3140,11 +3127,9 @@ retire:
int
i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_wait *args = data;
struct drm_i915_gem_object *obj;
struct drm_i915_gem_request *req[I915_NUM_ENGINES];
- unsigned reset_counter;
int i, n = 0;
int ret;
@@ -3178,7 +3163,6 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
drm_gem_object_unreference(&obj->base);
- reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
for (i = 0; i < I915_NUM_ENGINES; i++) {
if (obj->last_read_req[i] == NULL)
@@ -3191,7 +3175,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
for (i = 0; i < n; i++) {
if (ret == 0)
- ret = __i915_wait_request(req[i], reset_counter, true,
+ ret = __i915_wait_request(req[i], true,
args->timeout_ns > 0 ? &args->timeout_ns : NULL,
to_rps_client(file));
i915_gem_request_unreference__unlocked(req[i]);
@@ -3223,7 +3207,6 @@ __i915_gem_object_sync(struct drm_i915_gem_object *obj,
if (!i915_semaphore_is_enabled(obj->base.dev)) {
struct drm_i915_private *i915 = to_i915(obj->base.dev);
ret = __i915_wait_request(from_req,
- atomic_read(&i915->gpu_error.reset_counter),
i915->mm.interruptible,
NULL,
&i915->rps.semaphores);
@@ -3344,9 +3327,6 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0)
return;
- /* Wait for any direct GTT access to complete */
- mb();
-
old_read_domains = obj->base.read_domains;
old_write_domain = obj->base.write_domain;
@@ -3451,12 +3431,9 @@ int i915_gpu_idle(struct drm_device *dev)
return PTR_ERR(req);
ret = i915_switch_context(req);
- if (ret) {
- i915_gem_request_cancel(req);
- return ret;
- }
-
i915_add_request_no_flush(req);
+ if (ret)
+ return ret;
}
ret = intel_engine_idle(engine);
@@ -4179,16 +4156,15 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
struct drm_i915_file_private *file_priv = file->driver_priv;
unsigned long recent_enough = jiffies - DRM_I915_THROTTLE_JIFFIES;
struct drm_i915_gem_request *request, *target = NULL;
- unsigned reset_counter;
int ret;
ret = i915_gem_wait_for_error(&dev_priv->gpu_error);
if (ret)
return ret;
- ret = i915_gem_check_wedge(&dev_priv->gpu_error, false);
- if (ret)
- return ret;
+ /* ABI: return -EIO if already wedged */
+ if (i915_terminally_wedged(&dev_priv->gpu_error))
+ return -EIO;
spin_lock(&file_priv->mm.lock);
list_for_each_entry(request, &file_priv->mm.request_list, client_list) {
@@ -4204,7 +4180,6 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
target = request;
}
- reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
if (target)
i915_gem_request_reference(target);
spin_unlock(&file_priv->mm.lock);
@@ -4212,7 +4187,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
if (target == NULL)
return 0;
- ret = __i915_wait_request(target, reset_counter, true, NULL, NULL);
+ ret = __i915_wait_request(target, true, NULL, NULL);
if (ret == 0)
queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0);
@@ -4372,7 +4347,6 @@ i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj,
{
struct i915_vma *vma = i915_gem_obj_to_ggtt_view(obj, view);
- BUG_ON(!vma);
WARN_ON(vma->pin_count == 0);
WARN_ON(!i915_gem_obj_ggtt_bound_view(obj, view));
@@ -4889,7 +4863,7 @@ i915_gem_init_hw(struct drm_device *dev)
/* Double layer security blanket, see i915_gem_init() */
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
- if (dev_priv->ellc_size)
+ if (HAS_EDRAM(dev) && INTEL_GEN(dev_priv) < 9)
I915_WRITE(HSW_IDICR, I915_READ(HSW_IDICR) | IDIHASHMSK(0xf));
if (IS_HASWELL(dev))
@@ -4933,6 +4907,8 @@ i915_gem_init_hw(struct drm_device *dev)
goto out;
}
+ intel_mocs_init_l3cc_table(dev);
+
/* We can't enable contexts until all firmware is loaded */
if (HAS_GUC_UCODE(dev)) {
ret = intel_guc_ucode_load(dev);
@@ -4958,34 +4934,33 @@ i915_gem_init_hw(struct drm_device *dev)
req = i915_gem_request_alloc(engine, NULL);
if (IS_ERR(req)) {
ret = PTR_ERR(req);
- i915_gem_cleanup_engines(dev);
- goto out;
+ break;
}
if (engine->id == RCS) {
- for (j = 0; j < NUM_L3_SLICES(dev); j++)
- i915_gem_l3_remap(req, j);
+ for (j = 0; j < NUM_L3_SLICES(dev); j++) {
+ ret = i915_gem_l3_remap(req, j);
+ if (ret)
+ goto err_request;
+ }
}
ret = i915_ppgtt_init_ring(req);
- if (ret && ret != -EIO) {
- DRM_ERROR("PPGTT enable %s failed %d\n",
- engine->name, ret);
- i915_gem_request_cancel(req);
- i915_gem_cleanup_engines(dev);
- goto out;
- }
+ if (ret)
+ goto err_request;
ret = i915_gem_context_enable(req);
- if (ret && ret != -EIO) {
- DRM_ERROR("Context enable %s failed %d\n",
+ if (ret)
+ goto err_request;
+
+err_request:
+ i915_add_request_no_flush(req);
+ if (ret) {
+ DRM_ERROR("Failed to enable %s, error=%d\n",
engine->name, ret);
- i915_gem_request_cancel(req);
i915_gem_cleanup_engines(dev);
- goto out;
+ break;
}
-
- i915_add_request_no_flush(req);
}
out:
diff --git a/drivers/gpu/drm/i915/i915_gem.h b/drivers/gpu/drm/i915/i915_gem.h
new file mode 100644
index 000000000000..8292e797d9b5
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2016 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.
+ *
+ */
+
+#ifndef __I915_GEM_H__
+#define __I915_GEM_H__
+
+#ifdef CONFIG_DRM_I915_DEBUG_GEM
+#define GEM_BUG_ON(expr) BUG_ON(expr)
+#else
+#define GEM_BUG_ON(expr)
+#endif
+
+#endif /* __I915_GEM_H__ */
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index fe580cb9501a..e5acc3916f75 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -342,7 +342,7 @@ void i915_gem_context_reset(struct drm_device *dev)
struct intel_context *ctx;
list_for_each_entry(ctx, &dev_priv->context_list, link)
- intel_lr_context_reset(dev, ctx);
+ intel_lr_context_reset(dev_priv, ctx);
}
for (i = 0; i < I915_NUM_ENGINES; i++) {
@@ -539,7 +539,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
len = 4;
if (INTEL_INFO(engine->dev)->gen >= 7)
- len += 2 + (num_rings ? 4*num_rings + 2 : 0);
+ len += 2 + (num_rings ? 4*num_rings + 6 : 0);
ret = intel_ring_begin(req, len);
if (ret)
@@ -579,6 +579,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
if (INTEL_INFO(engine->dev)->gen >= 7) {
if (num_rings) {
struct intel_engine_cs *signaller;
+ i915_reg_t last_reg = {}; /* keep gcc quiet */
intel_ring_emit(engine,
MI_LOAD_REGISTER_IMM(num_rings));
@@ -586,11 +587,19 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
if (signaller == engine)
continue;
- intel_ring_emit_reg(engine,
- RING_PSMI_CTL(signaller->mmio_base));
+ last_reg = RING_PSMI_CTL(signaller->mmio_base);
+ intel_ring_emit_reg(engine, last_reg);
intel_ring_emit(engine,
_MASKED_BIT_DISABLE(GEN6_PSMI_SLEEP_MSG_DISABLE));
}
+
+ /* Insert a delay before the next switch! */
+ intel_ring_emit(engine,
+ MI_STORE_REGISTER_MEM |
+ MI_SRM_LRM_GLOBAL_GTT);
+ intel_ring_emit_reg(engine, last_reg);
+ intel_ring_emit(engine, engine->scratch.gtt_offset);
+ intel_ring_emit(engine, MI_NOOP);
}
intel_ring_emit(engine, MI_ARB_ON_OFF | MI_ARB_ENABLE);
}
@@ -600,50 +609,48 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
return ret;
}
-static inline bool should_skip_switch(struct intel_engine_cs *engine,
- struct intel_context *from,
- struct intel_context *to)
+static inline bool skip_rcs_switch(struct intel_engine_cs *engine,
+ struct intel_context *to)
{
if (to->remap_slice)
return false;
- if (to->ppgtt && from == to &&
+ if (!to->legacy_hw_ctx.initialized)
+ return false;
+
+ if (to->ppgtt &&
!(intel_engine_flag(engine) & to->ppgtt->pd_dirty_rings))
- return true;
+ return false;
- return false;
+ return to == engine->last_context;
}
static bool
needs_pd_load_pre(struct intel_engine_cs *engine, struct intel_context *to)
{
- struct drm_i915_private *dev_priv = engine->dev->dev_private;
-
if (!to->ppgtt)
return false;
- if (INTEL_INFO(engine->dev)->gen < 8)
+ if (engine->last_context == to &&
+ !(intel_engine_flag(engine) & to->ppgtt->pd_dirty_rings))
+ return false;
+
+ if (engine->id != RCS)
return true;
- if (engine != &dev_priv->engine[RCS])
+ if (INTEL_INFO(engine->dev)->gen < 8)
return true;
return false;
}
static bool
-needs_pd_load_post(struct intel_engine_cs *engine, struct intel_context *to,
- u32 hw_flags)
+needs_pd_load_post(struct intel_context *to, u32 hw_flags)
{
- struct drm_i915_private *dev_priv = engine->dev->dev_private;
-
if (!to->ppgtt)
return false;
- if (!IS_GEN8(engine->dev))
- return false;
-
- if (engine != &dev_priv->engine[RCS])
+ if (!IS_GEN8(to->i915))
return false;
if (hw_flags & MI_RESTORE_INHIBIT)
@@ -652,60 +659,33 @@ needs_pd_load_post(struct intel_engine_cs *engine, struct intel_context *to,
return false;
}
-static int do_switch(struct drm_i915_gem_request *req)
+static int do_rcs_switch(struct drm_i915_gem_request *req)
{
struct intel_context *to = req->ctx;
struct intel_engine_cs *engine = req->engine;
- struct drm_i915_private *dev_priv = req->i915;
- struct intel_context *from = engine->last_context;
- u32 hw_flags = 0;
- bool uninitialized = false;
+ struct intel_context *from;
+ u32 hw_flags;
int ret, i;
- if (from != NULL && engine == &dev_priv->engine[RCS]) {
- BUG_ON(from->legacy_hw_ctx.rcs_state == NULL);
- BUG_ON(!i915_gem_obj_is_pinned(from->legacy_hw_ctx.rcs_state));
- }
-
- if (should_skip_switch(engine, from, to))
+ if (skip_rcs_switch(engine, to))
return 0;
/* Trying to pin first makes error handling easier. */
- if (engine == &dev_priv->engine[RCS]) {
- ret = i915_gem_obj_ggtt_pin(to->legacy_hw_ctx.rcs_state,
- get_context_alignment(engine->dev),
- 0);
- if (ret)
- return ret;
- }
+ ret = i915_gem_obj_ggtt_pin(to->legacy_hw_ctx.rcs_state,
+ get_context_alignment(engine->dev),
+ 0);
+ if (ret)
+ return ret;
/*
* Pin can switch back to the default context if we end up calling into
* evict_everything - as a last ditch gtt defrag effort that also
* switches to the default context. Hence we need to reload from here.
+ *
+ * XXX: Doing so is painfully broken!
*/
from = engine->last_context;
- if (needs_pd_load_pre(engine, to)) {
- /* Older GENs and non render rings still want the load first,
- * "PP_DCLV followed by PP_DIR_BASE register through Load
- * Register Immediate commands in Ring Buffer before submitting
- * a context."*/
- trace_switch_mm(engine, to);
- ret = to->ppgtt->switch_mm(to->ppgtt, req);
- if (ret)
- goto unpin_out;
-
- /* Doing a PD load always reloads the page dirs */
- to->ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
- }
-
- if (engine != &dev_priv->engine[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
@@ -718,53 +698,37 @@ static int do_switch(struct drm_i915_gem_request *req)
if (ret)
goto unpin_out;
- if (!to->legacy_hw_ctx.initialized || i915_gem_context_is_default(to)) {
- hw_flags |= MI_RESTORE_INHIBIT;
+ if (needs_pd_load_pre(engine, to)) {
+ /* Older GENs and non render rings still want the load first,
+ * "PP_DCLV followed by PP_DIR_BASE register through Load
+ * Register Immediate commands in Ring Buffer before submitting
+ * a context."*/
+ trace_switch_mm(engine, to);
+ ret = to->ppgtt->switch_mm(to->ppgtt, req);
+ if (ret)
+ goto unpin_out;
+ }
+
+ if (!to->legacy_hw_ctx.initialized || i915_gem_context_is_default(to))
/* NB: If we inhibit the restore, the context is not allowed to
* die because future work may end up depending on valid address
* space. This means we must enforce that a page table load
* occur when this occurs. */
- } else if (to->ppgtt &&
- (intel_engine_flag(engine) & to->ppgtt->pd_dirty_rings)) {
- hw_flags |= MI_FORCE_RESTORE;
- to->ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
- }
+ hw_flags = MI_RESTORE_INHIBIT;
+ else if (to->ppgtt &&
+ intel_engine_flag(engine) & to->ppgtt->pd_dirty_rings)
+ hw_flags = MI_FORCE_RESTORE;
+ else
+ hw_flags = 0;
/* We should never emit switch_mm more than once */
WARN_ON(needs_pd_load_pre(engine, to) &&
- needs_pd_load_post(engine, to, hw_flags));
-
- ret = mi_set_context(req, hw_flags);
- if (ret)
- goto unpin_out;
+ needs_pd_load_post(to, hw_flags));
- /* GEN8 does *not* require an explicit reload if the PDPs have been
- * setup, and we do not wish to move them.
- */
- if (needs_pd_load_post(engine, to, hw_flags)) {
- trace_switch_mm(engine, to);
- ret = to->ppgtt->switch_mm(to->ppgtt, req);
- /* The hardware context switch is emitted, but we haven't
- * actually changed the state - so it's probably safe to bail
- * here. Still, let the user know something dangerous has
- * happened.
- */
- if (ret) {
- DRM_ERROR("Failed to change address space on context switch\n");
- goto unpin_out;
- }
- }
-
- for (i = 0; i < MAX_L3_SLICES; i++) {
- if (!(to->remap_slice & (1<<i)))
- continue;
-
- ret = i915_gem_l3_remap(req, i);
- /* If it failed, try again next round */
+ if (to != from || (hw_flags & MI_FORCE_RESTORE)) {
+ ret = mi_set_context(req, hw_flags);
if (ret)
- DRM_DEBUG_DRIVER("L3 remapping failed\n");
- else
- to->remap_slice &= ~(1<<i);
+ goto unpin_out;
}
/* The backing object for the context is done after switching to the
@@ -789,27 +753,51 @@ static int do_switch(struct drm_i915_gem_request *req)
i915_gem_object_ggtt_unpin(from->legacy_hw_ctx.rcs_state);
i915_gem_context_unreference(from);
}
-
- uninitialized = !to->legacy_hw_ctx.initialized;
- to->legacy_hw_ctx.initialized = true;
-
-done:
i915_gem_context_reference(to);
engine->last_context = to;
- if (uninitialized) {
+ /* GEN8 does *not* require an explicit reload if the PDPs have been
+ * setup, and we do not wish to move them.
+ */
+ if (needs_pd_load_post(to, hw_flags)) {
+ trace_switch_mm(engine, to);
+ ret = to->ppgtt->switch_mm(to->ppgtt, req);
+ /* The hardware context switch is emitted, but we haven't
+ * actually changed the state - so it's probably safe to bail
+ * here. Still, let the user know something dangerous has
+ * happened.
+ */
+ if (ret)
+ return ret;
+ }
+
+ if (to->ppgtt)
+ to->ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
+
+ for (i = 0; i < MAX_L3_SLICES; i++) {
+ if (!(to->remap_slice & (1<<i)))
+ continue;
+
+ ret = i915_gem_l3_remap(req, i);
+ if (ret)
+ return ret;
+
+ to->remap_slice &= ~(1<<i);
+ }
+
+ if (!to->legacy_hw_ctx.initialized) {
if (engine->init_context) {
ret = engine->init_context(req);
if (ret)
- DRM_ERROR("ring init context: %d\n", ret);
+ return ret;
}
+ to->legacy_hw_ctx.initialized = true;
}
return 0;
unpin_out:
- if (engine->id == RCS)
- i915_gem_object_ggtt_unpin(to->legacy_hw_ctx.rcs_state);
+ i915_gem_object_ggtt_unpin(to->legacy_hw_ctx.rcs_state);
return ret;
}
@@ -834,17 +822,33 @@ int i915_switch_context(struct drm_i915_gem_request *req)
WARN_ON(i915.enable_execlists);
WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
- if (req->ctx->legacy_hw_ctx.rcs_state == NULL) { /* We have the fake context */
- if (req->ctx != engine->last_context) {
- i915_gem_context_reference(req->ctx);
+ if (engine->id != RCS ||
+ req->ctx->legacy_hw_ctx.rcs_state == NULL) {
+ struct intel_context *to = req->ctx;
+
+ if (needs_pd_load_pre(engine, to)) {
+ int ret;
+
+ trace_switch_mm(engine, to);
+ ret = to->ppgtt->switch_mm(to->ppgtt, req);
+ if (ret)
+ return ret;
+
+ /* Doing a PD load always reloads the page dirs */
+ to->ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
+ }
+
+ if (to != engine->last_context) {
+ i915_gem_context_reference(to);
if (engine->last_context)
i915_gem_context_unreference(engine->last_context);
- engine->last_context = req->ctx;
+ engine->last_context = to;
}
+
return 0;
}
- return do_switch(req);
+ return do_rcs_switch(req);
}
static bool contexts_enabled(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 6ee4f00f620c..6f4f2a6cdf93 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -1137,7 +1137,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
}
}
-void
+static void
i915_gem_execbuffer_retire_commands(struct i915_execbuffer_params *params)
{
/* Unconditionally force add_request to emit a full flush. */
@@ -1322,7 +1322,6 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
trace_i915_gem_ring_dispatch(params->request, params->dispatch_flags);
i915_gem_execbuffer_move_to_active(vmas, params->request);
- i915_gem_execbuffer_retire_commands(params);
return 0;
}
@@ -1624,7 +1623,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
ret = i915_gem_request_add_to_client(req, file);
if (ret)
- goto err_batch_unpin;
+ goto err_request;
/*
* Save assorted stuff away to pass through to *_submission().
@@ -1641,6 +1640,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
params->request = req;
ret = dev_priv->gt.execbuf_submit(params, args, &eb->vmas);
+err_request:
+ i915_gem_execbuffer_retire_commands(params);
err_batch_unpin:
/*
@@ -1657,14 +1658,6 @@ err:
i915_gem_context_unreference(ctx);
eb_destroy(eb);
- /*
- * If the request was created but not successfully submitted then it
- * must be freed again. If it was submitted then it is being tracked
- * on the active request list and no clean up is required here.
- */
- if (ret && !IS_ERR_OR_NULL(req))
- i915_gem_request_cancel(req);
-
mutex_unlock(&dev->struct_mutex);
pre_mutex_err:
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index c5cb04907525..0d666b3f7e9b 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -745,7 +745,7 @@ static void gen8_ppgtt_clear_pte_range(struct i915_address_space *vm,
num_entries--;
}
- kunmap_px(ppgtt, pt);
+ kunmap_px(ppgtt, pt_vaddr);
pte = 0;
if (++pde == I915_PDES) {
@@ -905,11 +905,10 @@ static int gen8_init_scratch(struct i915_address_space *vm)
static int gen8_ppgtt_notify_vgt(struct i915_hw_ppgtt *ppgtt, bool create)
{
enum vgt_g2v_type msg;
- struct drm_device *dev = ppgtt->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(ppgtt->base.dev);
int i;
- if (USES_FULL_48BIT_PPGTT(dev)) {
+ if (USES_FULL_48BIT_PPGTT(dev_priv)) {
u64 daddr = px_dma(&ppgtt->pml4);
I915_WRITE(vgtif_reg(pdp[0].lo), lower_32_bits(daddr));
@@ -3172,7 +3171,8 @@ int i915_ggtt_init_hw(struct drm_device *dev)
} else if (INTEL_INFO(dev)->gen < 8) {
ggtt->probe = gen6_gmch_probe;
ggtt->base.cleanup = gen6_gmch_remove;
- if (IS_HASWELL(dev) && dev_priv->ellc_size)
+
+ if (HAS_EDRAM(dev))
ggtt->base.pte_encode = iris_pte_encode;
else if (IS_HASWELL(dev))
ggtt->base.pte_encode = hsw_pte_encode;
diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c
index d46388f25e04..425e721aac58 100644
--- a/drivers/gpu/drm/i915/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c
@@ -70,6 +70,10 @@ static bool swap_available(void)
static bool can_release_pages(struct drm_i915_gem_object *obj)
{
+ /* Only shmemfs objects are backed by swap */
+ if (!obj->base.filp)
+ return false;
+
/* Only report true if by unbinding the object and putting its pages
* we can actually make forward progress towards freeing physical
* pages.
@@ -336,7 +340,7 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
container_of(nb, struct drm_i915_private, mm.oom_notifier);
struct shrinker_lock_uninterruptible slu;
struct drm_i915_gem_object *obj;
- unsigned long pinned, bound, unbound, freed_pages;
+ unsigned long unevictable, bound, unbound, freed_pages;
if (!i915_gem_shrinker_lock_uninterruptible(dev_priv, &slu, 5000))
return NOTIFY_DONE;
@@ -347,33 +351,28 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
* assert that there are no objects with pinned pages that are not
* being pointed to by hardware.
*/
- unbound = bound = pinned = 0;
+ unbound = bound = unevictable = 0;
list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
- if (!obj->base.filp) /* not backed by a freeable object */
- continue;
-
- if (obj->pages_pin_count)
- pinned += obj->base.size;
+ if (!can_release_pages(obj))
+ unevictable += obj->base.size >> PAGE_SHIFT;
else
- unbound += obj->base.size;
+ unbound += obj->base.size >> PAGE_SHIFT;
}
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
- if (!obj->base.filp)
- continue;
-
- if (obj->pages_pin_count)
- pinned += obj->base.size;
+ if (!can_release_pages(obj))
+ unevictable += obj->base.size >> PAGE_SHIFT;
else
- bound += obj->base.size;
+ bound += obj->base.size >> PAGE_SHIFT;
}
i915_gem_shrinker_unlock_uninterruptible(dev_priv, &slu);
if (freed_pages || unbound || bound)
- pr_info("Purging GPU memory, %lu bytes freed, %lu bytes still pinned.\n",
- freed_pages << PAGE_SHIFT, pinned);
+ pr_info("Purging GPU memory, %lu pages freed, "
+ "%lu pages still pinned.\n",
+ freed_pages, unevictable);
if (unbound || bound)
- pr_err("%lu and %lu bytes still available in the "
+ pr_err("%lu and %lu pages still available in the "
"bound and unbound GPU page lists.\n",
bound, unbound);
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index ea06da012d32..b7ce963fb8f8 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -95,9 +95,9 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
u32 base;
/* Almost universally we can find the Graphics Base of Stolen Memory
- * at offset 0x5c in the igfx configuration space. On a few (desktop)
- * machines this is also mirrored in the bridge device at different
- * locations, or in the MCHBAR.
+ * at register BSM (0x5c) in the igfx configuration space. On a few
+ * (desktop) machines this is also mirrored in the bridge device at
+ * different locations, or in the MCHBAR.
*
* On 865 we just check the TOUD register.
*
@@ -107,9 +107,11 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
*/
base = 0;
if (INTEL_INFO(dev)->gen >= 3) {
- /* Read Graphics Base of Stolen Memory directly */
- pci_read_config_dword(dev->pdev, 0x5c, &base);
- base &= ~((1<<20) - 1);
+ u32 bsm;
+
+ pci_read_config_dword(dev->pdev, BSM, &bsm);
+
+ base = bsm & BSM_MASK;
} else if (IS_I865G(dev)) {
u16 toud = 0;
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index 0f94b6c5c9cc..32d9726e38b1 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -34,7 +34,7 @@
struct i915_mm_struct {
struct mm_struct *mm;
- struct drm_device *dev;
+ struct drm_i915_private *i915;
struct i915_mmu_notifier *mn;
struct hlist_node node;
struct kref kref;
@@ -49,6 +49,7 @@ struct i915_mmu_notifier {
struct hlist_node node;
struct mmu_notifier mn;
struct rb_root objects;
+ struct workqueue_struct *wq;
};
struct i915_mmu_object {
@@ -60,6 +61,37 @@ struct i915_mmu_object {
bool attached;
};
+static void wait_rendering(struct drm_i915_gem_object *obj)
+{
+ struct drm_device *dev = obj->base.dev;
+ struct drm_i915_gem_request *requests[I915_NUM_ENGINES];
+ int i, n;
+
+ if (!obj->active)
+ return;
+
+ n = 0;
+ for (i = 0; i < I915_NUM_ENGINES; i++) {
+ struct drm_i915_gem_request *req;
+
+ req = obj->last_read_req[i];
+ if (req == NULL)
+ continue;
+
+ requests[n++] = i915_gem_request_reference(req);
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ for (i = 0; i < n; i++)
+ __i915_wait_request(requests[i], false, NULL, NULL);
+
+ mutex_lock(&dev->struct_mutex);
+
+ for (i = 0; i < n; i++)
+ i915_gem_request_unreference(requests[i]);
+}
+
static void cancel_userptr(struct work_struct *work)
{
struct i915_mmu_object *mo = container_of(work, typeof(*mo), work);
@@ -75,13 +107,13 @@ static void cancel_userptr(struct work_struct *work)
struct i915_vma *vma, *tmp;
bool was_interruptible;
+ wait_rendering(obj);
+
was_interruptible = dev_priv->mm.interruptible;
dev_priv->mm.interruptible = false;
- list_for_each_entry_safe(vma, tmp, &obj->vma_list, obj_link) {
- int ret = i915_vma_unbind(vma);
- WARN_ON(ret && ret != -EIO);
- }
+ list_for_each_entry_safe(vma, tmp, &obj->vma_list, obj_link)
+ WARN_ON(i915_vma_unbind(vma));
WARN_ON(i915_gem_object_put_pages(obj));
dev_priv->mm.interruptible = was_interruptible;
@@ -140,7 +172,7 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
*/
mo = container_of(it, struct i915_mmu_object, it);
if (kref_get_unless_zero(&mo->obj->base.refcount))
- schedule_work(&mo->work);
+ queue_work(mn->wq, &mo->work);
list_add(&mo->link, &cancelled);
it = interval_tree_iter_next(it, start, end);
@@ -148,6 +180,8 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
list_for_each_entry(mo, &cancelled, link)
del_object(mo);
spin_unlock(&mn->lock);
+
+ flush_workqueue(mn->wq);
}
static const struct mmu_notifier_ops i915_gem_userptr_notifier = {
@@ -167,10 +201,16 @@ i915_mmu_notifier_create(struct mm_struct *mm)
spin_lock_init(&mn->lock);
mn->mn.ops = &i915_gem_userptr_notifier;
mn->objects = RB_ROOT;
+ mn->wq = alloc_workqueue("i915-userptr-release", WQ_UNBOUND, 0);
+ if (mn->wq == NULL) {
+ kfree(mn);
+ return ERR_PTR(-ENOMEM);
+ }
/* Protected by mmap_sem (write-lock) */
ret = __mmu_notifier_register(&mn->mn, mm);
if (ret) {
+ destroy_workqueue(mn->wq);
kfree(mn);
return ERR_PTR(ret);
}
@@ -205,13 +245,13 @@ i915_mmu_notifier_find(struct i915_mm_struct *mm)
return mn;
down_write(&mm->mm->mmap_sem);
- mutex_lock(&to_i915(mm->dev)->mm_lock);
+ mutex_lock(&mm->i915->mm_lock);
if ((mn = mm->mn) == NULL) {
mn = i915_mmu_notifier_create(mm->mm);
if (!IS_ERR(mn))
mm->mn = mn;
}
- mutex_unlock(&to_i915(mm->dev)->mm_lock);
+ mutex_unlock(&mm->i915->mm_lock);
up_write(&mm->mm->mmap_sem);
return mn;
@@ -256,6 +296,7 @@ i915_mmu_notifier_free(struct i915_mmu_notifier *mn,
return;
mmu_notifier_unregister(&mn->mn, mm);
+ destroy_workqueue(mn->wq);
kfree(mn);
}
@@ -327,7 +368,7 @@ i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj)
}
kref_init(&mm->kref);
- mm->dev = obj->base.dev;
+ mm->i915 = to_i915(obj->base.dev);
mm->mm = current->mm;
atomic_inc(&current->mm->mm_count);
@@ -362,7 +403,7 @@ __i915_mm_struct_free(struct kref *kref)
/* Protected by dev_priv->mm_lock */
hash_del(&mm->node);
- mutex_unlock(&to_i915(mm->dev)->mm_lock);
+ mutex_unlock(&mm->i915->mm_lock);
INIT_WORK(&mm->work, __i915_mm_struct_free__worker);
schedule_work(&mm->work);
@@ -498,19 +539,24 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
if (pvec != NULL) {
struct mm_struct *mm = obj->userptr.mm->mm;
- down_read(&mm->mmap_sem);
- while (pinned < npages) {
- ret = get_user_pages_remote(work->task, mm,
- obj->userptr.ptr + pinned * PAGE_SIZE,
- npages - pinned,
- !obj->userptr.read_only, 0,
- pvec + pinned, NULL);
- if (ret < 0)
- break;
-
- pinned += ret;
+ ret = -EFAULT;
+ if (atomic_inc_not_zero(&mm->mm_users)) {
+ down_read(&mm->mmap_sem);
+ while (pinned < npages) {
+ ret = get_user_pages_remote
+ (work->task, mm,
+ obj->userptr.ptr + pinned * PAGE_SIZE,
+ npages - pinned,
+ !obj->userptr.read_only, 0,
+ pvec + pinned, NULL);
+ if (ret < 0)
+ break;
+
+ pinned += ret;
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm);
}
- up_read(&mm->mmap_sem);
}
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c
index da86bdbba275..d40c13fb6643 100644
--- a/drivers/gpu/drm/i915/i915_guc_submission.c
+++ b/drivers/gpu/drm/i915/i915_guc_submission.c
@@ -179,15 +179,11 @@ static void guc_init_doorbell(struct intel_guc *guc,
struct i915_guc_client *client)
{
struct guc_doorbell_info *doorbell;
- void *base;
- base = kmap_atomic(i915_gem_object_get_page(client->client_obj, 0));
- doorbell = base + client->doorbell_offset;
+ doorbell = client->client_base + client->doorbell_offset;
- doorbell->db_status = 1;
+ doorbell->db_status = GUC_DOORBELL_ENABLED;
doorbell->cookie = 0;
-
- kunmap_atomic(base);
}
static int guc_ring_doorbell(struct i915_guc_client *gc)
@@ -195,11 +191,9 @@ static int guc_ring_doorbell(struct i915_guc_client *gc)
struct guc_process_desc *desc;
union guc_doorbell_qw db_cmp, db_exc, db_ret;
union guc_doorbell_qw *db;
- void *base;
int attempt = 2, ret = -EAGAIN;
- base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, 0));
- desc = base + gc->proc_desc_offset;
+ desc = gc->client_base + gc->proc_desc_offset;
/* Update the tail so it is visible to GuC */
desc->tail = gc->wq_tail;
@@ -215,7 +209,7 @@ static int guc_ring_doorbell(struct i915_guc_client *gc)
db_exc.cookie = 1;
/* pointer of current doorbell cacheline */
- db = base + gc->doorbell_offset;
+ db = gc->client_base + gc->doorbell_offset;
while (attempt--) {
/* lets ring the doorbell */
@@ -244,10 +238,6 @@ static int guc_ring_doorbell(struct i915_guc_client *gc)
db_exc.cookie = 1;
}
- /* Finally, update the cached copy of the GuC's WQ head */
- gc->wq_head = desc->head;
-
- kunmap_atomic(base);
return ret;
}
@@ -256,16 +246,12 @@ static void guc_disable_doorbell(struct intel_guc *guc,
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
struct guc_doorbell_info *doorbell;
- void *base;
i915_reg_t drbreg = GEN8_DRBREGL(client->doorbell_id);
int value;
- base = kmap_atomic(i915_gem_object_get_page(client->client_obj, 0));
- doorbell = base + client->doorbell_offset;
-
- doorbell->db_status = 0;
+ doorbell = client->client_base + client->doorbell_offset;
- kunmap_atomic(base);
+ doorbell->db_status = GUC_DOORBELL_DISABLED;
I915_WRITE(drbreg, I915_READ(drbreg) & ~GEN8_DRB_VALID);
@@ -341,10 +327,8 @@ static void guc_init_proc_desc(struct intel_guc *guc,
struct i915_guc_client *client)
{
struct guc_process_desc *desc;
- void *base;
- base = kmap_atomic(i915_gem_object_get_page(client->client_obj, 0));
- desc = base + client->proc_desc_offset;
+ desc = client->client_base + client->proc_desc_offset;
memset(desc, 0, sizeof(*desc));
@@ -361,8 +345,6 @@ static void guc_init_proc_desc(struct intel_guc *guc,
desc->wq_size_bytes = client->wq_size;
desc->wq_status = WQ_STATUS_ACTIVE;
desc->priority = client->priority;
-
- kunmap_atomic(base);
}
/*
@@ -376,12 +358,14 @@ static void guc_init_proc_desc(struct intel_guc *guc,
static void guc_init_ctx_desc(struct intel_guc *guc,
struct i915_guc_client *client)
{
+ struct drm_i915_gem_object *client_obj = client->client_obj;
struct drm_i915_private *dev_priv = guc_to_i915(guc);
struct intel_engine_cs *engine;
struct intel_context *ctx = client->owner;
struct guc_context_desc desc;
struct sg_table *sg;
enum intel_engine_id id;
+ u32 gfx_addr;
memset(&desc, 0, sizeof(desc));
@@ -410,16 +394,17 @@ static void guc_init_ctx_desc(struct intel_guc *guc,
lrc->context_desc = (u32)ctx_desc;
/* The state page is after PPHWSP */
- lrc->ring_lcra = i915_gem_obj_ggtt_offset(obj) +
- LRC_STATE_PN * PAGE_SIZE;
+ gfx_addr = i915_gem_obj_ggtt_offset(obj);
+ lrc->ring_lcra = gfx_addr + LRC_STATE_PN * PAGE_SIZE;
lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) |
(engine->guc_id << GUC_ELC_ENGINE_OFFSET);
obj = ctx->engine[id].ringbuf->obj;
+ gfx_addr = i915_gem_obj_ggtt_offset(obj);
- lrc->ring_begin = i915_gem_obj_ggtt_offset(obj);
- lrc->ring_end = lrc->ring_begin + obj->base.size - 1;
- lrc->ring_next_free_location = lrc->ring_begin;
+ lrc->ring_begin = gfx_addr;
+ lrc->ring_end = gfx_addr + obj->base.size - 1;
+ lrc->ring_next_free_location = gfx_addr;
lrc->ring_current_tail_pointer_value = 0;
desc.engines_used |= (1 << engine->guc_id);
@@ -428,22 +413,17 @@ static void guc_init_ctx_desc(struct intel_guc *guc,
WARN_ON(desc.engines_used == 0);
/*
- * The CPU address is only needed at certain points, so kmap_atomic on
- * demand instead of storing it in the ctx descriptor.
- * XXX: May make debug easier to have it mapped
+ * The doorbell, process descriptor, and workqueue are all parts
+ * of the client object, which the GuC will reference via the GGTT
*/
- desc.db_trigger_cpu = 0;
- desc.db_trigger_uk = client->doorbell_offset +
- i915_gem_obj_ggtt_offset(client->client_obj);
- desc.db_trigger_phy = client->doorbell_offset +
- sg_dma_address(client->client_obj->pages->sgl);
-
- desc.process_desc = client->proc_desc_offset +
- i915_gem_obj_ggtt_offset(client->client_obj);
-
- desc.wq_addr = client->wq_offset +
- i915_gem_obj_ggtt_offset(client->client_obj);
-
+ gfx_addr = i915_gem_obj_ggtt_offset(client_obj);
+ desc.db_trigger_phy = sg_dma_address(client_obj->pages->sgl) +
+ client->doorbell_offset;
+ desc.db_trigger_cpu = (uintptr_t)client->client_base +
+ client->doorbell_offset;
+ desc.db_trigger_uk = gfx_addr + client->doorbell_offset;
+ desc.process_desc = gfx_addr + client->proc_desc_offset;
+ desc.wq_addr = gfx_addr + client->wq_offset;
desc.wq_size = client->wq_size;
/*
@@ -474,25 +454,16 @@ static void guc_fini_ctx_desc(struct intel_guc *guc,
int i915_guc_wq_check_space(struct i915_guc_client *gc)
{
struct guc_process_desc *desc;
- void *base;
u32 size = sizeof(struct guc_wq_item);
int ret = -ETIMEDOUT, timeout_counter = 200;
if (!gc)
return 0;
- /* Quickly return if wq space is available since last time we cache the
- * head position. */
- if (CIRC_SPACE(gc->wq_tail, gc->wq_head, gc->wq_size) >= size)
- return 0;
-
- base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, 0));
- desc = base + gc->proc_desc_offset;
+ desc = gc->client_base + gc->proc_desc_offset;
while (timeout_counter-- > 0) {
- gc->wq_head = desc->head;
-
- if (CIRC_SPACE(gc->wq_tail, gc->wq_head, gc->wq_size) >= size) {
+ if (CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size) >= size) {
ret = 0;
break;
}
@@ -501,19 +472,19 @@ int i915_guc_wq_check_space(struct i915_guc_client *gc)
usleep_range(1000, 2000);
};
- kunmap_atomic(base);
-
return ret;
}
static int guc_add_workqueue_item(struct i915_guc_client *gc,
struct drm_i915_gem_request *rq)
{
+ struct guc_process_desc *desc;
struct guc_wq_item *wqi;
void *base;
u32 tail, wq_len, wq_off, space;
- space = CIRC_SPACE(gc->wq_tail, gc->wq_head, gc->wq_size);
+ desc = gc->client_base + gc->proc_desc_offset;
+ space = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size);
if (WARN_ON(space < sizeof(struct guc_wq_item)))
return -ENOSPC; /* shouldn't happen */
@@ -661,21 +632,28 @@ static void guc_client_free(struct drm_device *dev,
if (!client)
return;
- if (client->doorbell_id != GUC_INVALID_DOORBELL_ID) {
- /*
- * First disable the doorbell, then tell the GuC we've
- * finished with it, finally deallocate it in our bitmap
- */
- guc_disable_doorbell(guc, client);
- host2guc_release_doorbell(guc, client);
- release_doorbell(guc, client->doorbell_id);
- }
-
/*
* XXX: wait for any outstanding submissions before freeing memory.
* Be sure to drop any locks
*/
+ if (client->client_base) {
+ /*
+ * If we got as far as setting up a doorbell, make sure
+ * we shut it down before unmapping & deallocating the
+ * memory. So first disable the doorbell, then tell the
+ * GuC that we've finished with it, finally deallocate
+ * it in our bitmap
+ */
+ if (client->doorbell_id != GUC_INVALID_DOORBELL_ID) {
+ guc_disable_doorbell(guc, client);
+ host2guc_release_doorbell(guc, client);
+ release_doorbell(guc, client->doorbell_id);
+ }
+
+ kunmap(kmap_to_page(client->client_base));
+ }
+
gem_release_guc_obj(client->client_obj);
if (client->ctx_index != GUC_INVALID_CTX_ID) {
@@ -696,7 +674,7 @@ static void guc_client_free(struct drm_device *dev,
* @ctx: the context that owns the client (we use the default render
* context)
*
- * Return: An i915_guc_client object if success.
+ * Return: An i915_guc_client object if success, else NULL.
*/
static struct i915_guc_client *guc_client_alloc(struct drm_device *dev,
uint32_t priority,
@@ -728,7 +706,9 @@ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev,
if (!obj)
goto err;
+ /* We'll keep just the first (doorbell/proc) page permanently kmap'd. */
client->client_obj = obj;
+ client->client_base = kmap(i915_gem_object_get_page(obj, 0));
client->wq_offset = GUC_DB_SIZE;
client->wq_size = GUC_WQ_SIZE;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 679f08c944ef..2f6fd33c07ba 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1264,18 +1264,17 @@ out:
mutex_unlock(&dev_priv->dev->struct_mutex);
}
-static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
+static void ivybridge_parity_error_irq_handler(struct drm_i915_private *dev_priv,
+ u32 iir)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- if (!HAS_L3_DPF(dev))
+ if (!HAS_L3_DPF(dev_priv))
return;
spin_lock(&dev_priv->irq_lock);
- gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev));
+ gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv));
spin_unlock(&dev_priv->irq_lock);
- iir &= GT_PARITY_ERROR(dev);
+ iir &= GT_PARITY_ERROR(dev_priv);
if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1)
dev_priv->l3_parity.which_slice |= 1 << 1;
@@ -1285,8 +1284,7 @@ static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
}
-static void ilk_gt_irq_handler(struct drm_device *dev,
- struct drm_i915_private *dev_priv,
+static void ilk_gt_irq_handler(struct drm_i915_private *dev_priv,
u32 gt_iir)
{
if (gt_iir &
@@ -1296,8 +1294,7 @@ static void ilk_gt_irq_handler(struct drm_device *dev,
notify_ring(&dev_priv->engine[VCS]);
}
-static void snb_gt_irq_handler(struct drm_device *dev,
- struct drm_i915_private *dev_priv,
+static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
u32 gt_iir)
{
@@ -1314,8 +1311,8 @@ static void snb_gt_irq_handler(struct drm_device *dev,
GT_RENDER_CS_MASTER_ERROR_INTERRUPT))
DRM_DEBUG("Command parser error, gt_iir 0x%08x\n", gt_iir);
- if (gt_iir & GT_PARITY_ERROR(dev))
- ivybridge_parity_error_irq_handler(dev, gt_iir);
+ if (gt_iir & GT_PARITY_ERROR(dev_priv))
+ ivybridge_parity_error_irq_handler(dev_priv, gt_iir);
}
static __always_inline void
@@ -1327,60 +1324,45 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
tasklet_schedule(&engine->irq_tasklet);
}
-static irqreturn_t gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
- u32 master_ctl)
+static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv,
+ u32 master_ctl,
+ u32 gt_iir[4])
{
irqreturn_t ret = IRQ_NONE;
if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
- u32 iir = I915_READ_FW(GEN8_GT_IIR(0));
- if (iir) {
- I915_WRITE_FW(GEN8_GT_IIR(0), iir);
+ gt_iir[0] = I915_READ_FW(GEN8_GT_IIR(0));
+ if (gt_iir[0]) {
+ I915_WRITE_FW(GEN8_GT_IIR(0), gt_iir[0]);
ret = IRQ_HANDLED;
-
- gen8_cs_irq_handler(&dev_priv->engine[RCS],
- iir, GEN8_RCS_IRQ_SHIFT);
-
- gen8_cs_irq_handler(&dev_priv->engine[BCS],
- iir, GEN8_BCS_IRQ_SHIFT);
} else
DRM_ERROR("The master control interrupt lied (GT0)!\n");
}
if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
- u32 iir = I915_READ_FW(GEN8_GT_IIR(1));
- if (iir) {
- I915_WRITE_FW(GEN8_GT_IIR(1), iir);
+ gt_iir[1] = I915_READ_FW(GEN8_GT_IIR(1));
+ if (gt_iir[1]) {
+ I915_WRITE_FW(GEN8_GT_IIR(1), gt_iir[1]);
ret = IRQ_HANDLED;
-
- gen8_cs_irq_handler(&dev_priv->engine[VCS],
- iir, GEN8_VCS1_IRQ_SHIFT);
-
- gen8_cs_irq_handler(&dev_priv->engine[VCS2],
- iir, GEN8_VCS2_IRQ_SHIFT);
} else
DRM_ERROR("The master control interrupt lied (GT1)!\n");
}
if (master_ctl & GEN8_GT_VECS_IRQ) {
- u32 iir = I915_READ_FW(GEN8_GT_IIR(3));
- if (iir) {
- I915_WRITE_FW(GEN8_GT_IIR(3), iir);
+ gt_iir[3] = I915_READ_FW(GEN8_GT_IIR(3));
+ if (gt_iir[3]) {
+ I915_WRITE_FW(GEN8_GT_IIR(3), gt_iir[3]);
ret = IRQ_HANDLED;
-
- gen8_cs_irq_handler(&dev_priv->engine[VECS],
- iir, GEN8_VECS_IRQ_SHIFT);
} else
DRM_ERROR("The master control interrupt lied (GT3)!\n");
}
if (master_ctl & GEN8_GT_PM_IRQ) {
- u32 iir = I915_READ_FW(GEN8_GT_IIR(2));
- if (iir & dev_priv->pm_rps_events) {
+ gt_iir[2] = I915_READ_FW(GEN8_GT_IIR(2));
+ if (gt_iir[2] & dev_priv->pm_rps_events) {
I915_WRITE_FW(GEN8_GT_IIR(2),
- iir & dev_priv->pm_rps_events);
+ gt_iir[2] & dev_priv->pm_rps_events);
ret = IRQ_HANDLED;
- gen6_rps_irq_handler(dev_priv, iir);
} else
DRM_ERROR("The master control interrupt lied (PM)!\n");
}
@@ -1388,6 +1370,31 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
return ret;
}
+static void gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
+ u32 gt_iir[4])
+{
+ if (gt_iir[0]) {
+ gen8_cs_irq_handler(&dev_priv->engine[RCS],
+ gt_iir[0], GEN8_RCS_IRQ_SHIFT);
+ gen8_cs_irq_handler(&dev_priv->engine[BCS],
+ gt_iir[0], GEN8_BCS_IRQ_SHIFT);
+ }
+
+ if (gt_iir[1]) {
+ gen8_cs_irq_handler(&dev_priv->engine[VCS],
+ gt_iir[1], GEN8_VCS1_IRQ_SHIFT);
+ gen8_cs_irq_handler(&dev_priv->engine[VCS2],
+ gt_iir[1], GEN8_VCS2_IRQ_SHIFT);
+ }
+
+ if (gt_iir[3])
+ gen8_cs_irq_handler(&dev_priv->engine[VECS],
+ gt_iir[3], GEN8_VECS_IRQ_SHIFT);
+
+ if (gt_iir[2] & dev_priv->pm_rps_events)
+ gen6_rps_irq_handler(dev_priv, gt_iir[2]);
+}
+
static bool bxt_port_hotplug_long_detect(enum port port, u32 val)
{
switch (port) {
@@ -1644,10 +1651,10 @@ static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
return true;
}
-static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
+static void valleyview_pipestat_irq_ack(struct drm_device *dev, u32 iir,
+ u32 pipe_stats[I915_MAX_PIPES])
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 pipe_stats[I915_MAX_PIPES] = { };
int pipe;
spin_lock(&dev_priv->irq_lock);
@@ -1701,6 +1708,13 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
I915_WRITE(reg, pipe_stats[pipe]);
}
spin_unlock(&dev_priv->irq_lock);
+}
+
+static void valleyview_pipestat_irq_handler(struct drm_device *dev,
+ u32 pipe_stats[I915_MAX_PIPES])
+{
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ enum pipe pipe;
for_each_pipe(dev_priv, pipe) {
if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
@@ -1723,21 +1737,20 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
gmbus_irq_handler(dev);
}
-static void i9xx_hpd_irq_handler(struct drm_device *dev)
+static u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
- u32 pin_mask = 0, long_mask = 0;
- if (!hotplug_status)
- return;
+ if (hotplug_status)
+ I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
- I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
- /*
- * Make sure hotplug status is cleared before we clear IIR, or else we
- * may miss hotplug events.
- */
- POSTING_READ(PORT_HOTPLUG_STAT);
+ return hotplug_status;
+}
+
+static void i9xx_hpd_irq_handler(struct drm_device *dev,
+ u32 hotplug_status)
+{
+ u32 pin_mask = 0, long_mask = 0;
if (IS_G4X(dev) || IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
@@ -1768,7 +1781,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
{
struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 iir, gt_iir, pm_iir;
irqreturn_t ret = IRQ_NONE;
if (!intel_irqs_enabled(dev_priv))
@@ -1777,40 +1789,72 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
disable_rpm_wakeref_asserts(dev_priv);
- while (true) {
- /* Find, clear, then process each source of interrupt */
+ do {
+ u32 iir, gt_iir, pm_iir;
+ u32 pipe_stats[I915_MAX_PIPES] = {};
+ u32 hotplug_status = 0;
+ u32 ier = 0;
gt_iir = I915_READ(GTIIR);
- if (gt_iir)
- I915_WRITE(GTIIR, gt_iir);
-
pm_iir = I915_READ(GEN6_PMIIR);
- if (pm_iir)
- I915_WRITE(GEN6_PMIIR, pm_iir);
-
iir = I915_READ(VLV_IIR);
- if (iir) {
- /* Consume port before clearing IIR or we'll miss events */
- if (iir & I915_DISPLAY_PORT_INTERRUPT)
- i9xx_hpd_irq_handler(dev);
- I915_WRITE(VLV_IIR, iir);
- }
if (gt_iir == 0 && pm_iir == 0 && iir == 0)
- goto out;
+ break;
ret = IRQ_HANDLED;
+ /*
+ * Theory on interrupt generation, based on empirical evidence:
+ *
+ * x = ((VLV_IIR & VLV_IER) ||
+ * (((GT_IIR & GT_IER) || (GEN6_PMIIR & GEN6_PMIER)) &&
+ * (VLV_MASTER_IER & MASTER_INTERRUPT_ENABLE)));
+ *
+ * A CPU interrupt will only be raised when 'x' has a 0->1 edge.
+ * Hence we clear MASTER_INTERRUPT_ENABLE and VLV_IER to
+ * guarantee the CPU interrupt will be raised again even if we
+ * don't end up clearing all the VLV_IIR, GT_IIR, GEN6_PMIIR
+ * bits this time around.
+ */
+ I915_WRITE(VLV_MASTER_IER, 0);
+ ier = I915_READ(VLV_IER);
+ I915_WRITE(VLV_IER, 0);
+
if (gt_iir)
- snb_gt_irq_handler(dev, dev_priv, gt_iir);
+ I915_WRITE(GTIIR, gt_iir);
if (pm_iir)
- gen6_rps_irq_handler(dev_priv, pm_iir);
+ I915_WRITE(GEN6_PMIIR, pm_iir);
+
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
+ hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+
/* Call regardless, as some status bits might not be
* signalled in iir */
- valleyview_pipestat_irq_handler(dev, iir);
- }
+ valleyview_pipestat_irq_ack(dev, iir, pipe_stats);
+
+ /*
+ * VLV_IIR is single buffered, and reflects the level
+ * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
+ */
+ if (iir)
+ I915_WRITE(VLV_IIR, iir);
+
+ I915_WRITE(VLV_IER, ier);
+ I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
+ POSTING_READ(VLV_MASTER_IER);
+
+ if (gt_iir)
+ snb_gt_irq_handler(dev_priv, gt_iir);
+ if (pm_iir)
+ gen6_rps_irq_handler(dev_priv, pm_iir);
+
+ if (hotplug_status)
+ i9xx_hpd_irq_handler(dev, hotplug_status);
+
+ valleyview_pipestat_irq_handler(dev, pipe_stats);
+ } while (0);
-out:
enable_rpm_wakeref_asserts(dev_priv);
return ret;
@@ -1820,7 +1864,6 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
{
struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 master_ctl, iir;
irqreturn_t ret = IRQ_NONE;
if (!intel_irqs_enabled(dev_priv))
@@ -1830,6 +1873,12 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
disable_rpm_wakeref_asserts(dev_priv);
do {
+ u32 master_ctl, iir;
+ u32 gt_iir[4] = {};
+ u32 pipe_stats[I915_MAX_PIPES] = {};
+ u32 hotplug_status = 0;
+ u32 ier = 0;
+
master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
iir = I915_READ(VLV_IIR);
@@ -1838,25 +1887,49 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
+ /*
+ * Theory on interrupt generation, based on empirical evidence:
+ *
+ * x = ((VLV_IIR & VLV_IER) ||
+ * ((GEN8_MASTER_IRQ & ~GEN8_MASTER_IRQ_CONTROL) &&
+ * (GEN8_MASTER_IRQ & GEN8_MASTER_IRQ_CONTROL)));
+ *
+ * A CPU interrupt will only be raised when 'x' has a 0->1 edge.
+ * Hence we clear GEN8_MASTER_IRQ_CONTROL and VLV_IER to
+ * guarantee the CPU interrupt will be raised again even if we
+ * don't end up clearing all the VLV_IIR and GEN8_MASTER_IRQ_CONTROL
+ * bits this time around.
+ */
I915_WRITE(GEN8_MASTER_IRQ, 0);
+ ier = I915_READ(VLV_IER);
+ I915_WRITE(VLV_IER, 0);
- /* Find, clear, then process each source of interrupt */
+ gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
- if (iir) {
- /* Consume port before clearing IIR or we'll miss events */
- if (iir & I915_DISPLAY_PORT_INTERRUPT)
- i9xx_hpd_irq_handler(dev);
- I915_WRITE(VLV_IIR, iir);
- }
-
- gen8_gt_irq_handler(dev_priv, master_ctl);
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
+ hotplug_status = i9xx_hpd_irq_ack(dev_priv);
/* Call regardless, as some status bits might not be
* signalled in iir */
- valleyview_pipestat_irq_handler(dev, iir);
+ valleyview_pipestat_irq_ack(dev, iir, pipe_stats);
- I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
+ /*
+ * VLV_IIR is single buffered, and reflects the level
+ * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
+ */
+ if (iir)
+ I915_WRITE(VLV_IIR, iir);
+
+ I915_WRITE(VLV_IER, ier);
+ I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
POSTING_READ(GEN8_MASTER_IRQ);
+
+ gen8_gt_irq_handler(dev_priv, gt_iir);
+
+ if (hotplug_status)
+ i9xx_hpd_irq_handler(dev, hotplug_status);
+
+ valleyview_pipestat_irq_handler(dev, pipe_stats);
} while (0);
enable_rpm_wakeref_asserts(dev_priv);
@@ -2217,9 +2290,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
I915_WRITE(GTIIR, gt_iir);
ret = IRQ_HANDLED;
if (INTEL_INFO(dev)->gen >= 6)
- snb_gt_irq_handler(dev, dev_priv, gt_iir);
+ snb_gt_irq_handler(dev_priv, gt_iir);
else
- ilk_gt_irq_handler(dev, dev_priv, gt_iir);
+ ilk_gt_irq_handler(dev_priv, gt_iir);
}
de_iir = I915_READ(DEIIR);
@@ -2419,6 +2492,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 master_ctl;
+ u32 gt_iir[4] = {};
irqreturn_t ret;
if (!intel_irqs_enabled(dev_priv))
@@ -2435,7 +2509,8 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
disable_rpm_wakeref_asserts(dev_priv);
/* Find, clear, then process each source of interrupt */
- ret = gen8_gt_irq_handler(dev_priv, master_ctl);
+ ret = gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
+ gen8_gt_irq_handler(dev_priv, gt_iir);
ret |= gen8_de_irq_handler(dev_priv, master_ctl);
I915_WRITE_FW(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
@@ -2483,7 +2558,6 @@ static void i915_error_wake_up(struct drm_i915_private *dev_priv,
static void i915_reset_and_wakeup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = to_i915(dev);
- struct i915_gpu_error *error = &dev_priv->gpu_error;
char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
@@ -2501,7 +2575,7 @@ static void i915_reset_and_wakeup(struct drm_device *dev)
* the reset in-progress bit is only ever set by code outside of this
* work we don't need to worry about any other races.
*/
- if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) {
+ if (i915_reset_in_progress(&dev_priv->gpu_error)) {
DRM_DEBUG_DRIVER("resetting chip\n");
kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
reset_event);
@@ -2529,25 +2603,9 @@ static void i915_reset_and_wakeup(struct drm_device *dev)
intel_runtime_pm_put(dev_priv);
- if (ret == 0) {
- /*
- * After all the gem state is reset, increment the reset
- * counter and wake up everyone waiting for the reset to
- * complete.
- *
- * Since unlock operations are a one-sided barrier only,
- * we need to insert a barrier here to order any seqno
- * updates before
- * the counter increment.
- */
- smp_mb__before_atomic();
- atomic_inc(&dev_priv->gpu_error.reset_counter);
-
+ if (ret == 0)
kobject_uevent_env(&dev->primary->kdev->kobj,
KOBJ_CHANGE, reset_done_event);
- } else {
- atomic_or(I915_WEDGED, &error->reset_counter);
- }
/*
* Note: The wake_up also serves as a memory barrier so that
@@ -3285,6 +3343,55 @@ static void gen5_gt_irq_reset(struct drm_device *dev)
GEN5_IRQ_RESET(GEN6_PM);
}
+static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
+{
+ enum pipe pipe;
+
+ if (IS_CHERRYVIEW(dev_priv))
+ I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
+ else
+ I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK);
+
+ i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0);
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+
+ for_each_pipe(dev_priv, pipe) {
+ I915_WRITE(PIPESTAT(pipe),
+ PIPE_FIFO_UNDERRUN_STATUS |
+ PIPESTAT_INT_STATUS_MASK);
+ dev_priv->pipestat_irq_mask[pipe] = 0;
+ }
+
+ GEN5_IRQ_RESET(VLV_);
+ dev_priv->irq_mask = ~0;
+}
+
+static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
+{
+ u32 pipestat_mask;
+ u32 enable_mask;
+ enum pipe pipe;
+
+ pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
+ PIPE_CRC_DONE_INTERRUPT_STATUS;
+
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
+ for_each_pipe(dev_priv, pipe)
+ i915_enable_pipestat(dev_priv, pipe, pipestat_mask);
+
+ enable_mask = I915_DISPLAY_PORT_INTERRUPT |
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+ if (IS_CHERRYVIEW(dev_priv))
+ enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
+
+ WARN_ON(dev_priv->irq_mask != ~0);
+
+ dev_priv->irq_mask = ~enable_mask;
+
+ GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask);
+}
+
/* drm_dma.h hooks
*/
static void ironlake_irq_reset(struct drm_device *dev)
@@ -3302,34 +3409,19 @@ static void ironlake_irq_reset(struct drm_device *dev)
ibx_irq_reset(dev);
}
-static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
-{
- enum pipe pipe;
-
- i915_hotplug_interrupt_update(dev_priv, 0xFFFFFFFF, 0);
- I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
-
- for_each_pipe(dev_priv, pipe)
- I915_WRITE(PIPESTAT(pipe), 0xffff);
-
- GEN5_IRQ_RESET(VLV_);
-}
-
static void valleyview_irq_preinstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- /* VLV magic */
- I915_WRITE(VLV_IMR, 0);
- I915_WRITE(RING_IMR(RENDER_RING_BASE), 0);
- I915_WRITE(RING_IMR(GEN6_BSD_RING_BASE), 0);
- I915_WRITE(RING_IMR(BLT_RING_BASE), 0);
+ I915_WRITE(VLV_MASTER_IER, 0);
+ POSTING_READ(VLV_MASTER_IER);
gen5_gt_irq_reset(dev);
- I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK);
-
- vlv_display_irq_reset(dev_priv);
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ vlv_display_irq_reset(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
}
static void gen8_gt_irq_reset(struct drm_i915_private *dev_priv)
@@ -3402,9 +3494,10 @@ static void cherryview_irq_preinstall(struct drm_device *dev)
GEN5_IRQ_RESET(GEN8_PCU_);
- I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
-
- vlv_display_irq_reset(dev_priv);
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ vlv_display_irq_reset(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
}
static u32 intel_hpd_enabled_irqs(struct drm_device *dev,
@@ -3651,74 +3744,6 @@ 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;
- enum pipe pipe;
-
- pipestat_mask = PIPESTAT_INT_STATUS_MASK |
- PIPE_FIFO_UNDERRUN_STATUS;
-
- for_each_pipe(dev_priv, pipe)
- I915_WRITE(PIPESTAT(pipe), 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, PIPE_GMBUS_INTERRUPT_STATUS);
- for_each_pipe(dev_priv, pipe)
- i915_enable_pipestat(dev_priv, pipe, pipestat_mask);
-
- iir_mask = I915_DISPLAY_PORT_INTERRUPT |
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
- if (IS_CHERRYVIEW(dev_priv))
- iir_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
- dev_priv->irq_mask &= ~iir_mask;
-
- I915_WRITE(VLV_IIR, iir_mask);
- I915_WRITE(VLV_IIR, iir_mask);
- I915_WRITE(VLV_IER, ~dev_priv->irq_mask);
- I915_WRITE(VLV_IMR, dev_priv->irq_mask);
- POSTING_READ(VLV_IMR);
-}
-
-static void valleyview_display_irqs_uninstall(struct drm_i915_private *dev_priv)
-{
- u32 pipestat_mask;
- u32 iir_mask;
- enum pipe pipe;
-
- iir_mask = I915_DISPLAY_PORT_INTERRUPT |
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
- if (IS_CHERRYVIEW(dev_priv))
- iir_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
-
- dev_priv->irq_mask |= iir_mask;
- I915_WRITE(VLV_IMR, dev_priv->irq_mask);
- I915_WRITE(VLV_IER, ~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, PIPE_GMBUS_INTERRUPT_STATUS);
- for_each_pipe(dev_priv, pipe)
- i915_disable_pipestat(dev_priv, pipe, pipestat_mask);
-
- pipestat_mask = PIPESTAT_INT_STATUS_MASK |
- PIPE_FIFO_UNDERRUN_STATUS;
-
- for_each_pipe(dev_priv, pipe)
- I915_WRITE(PIPESTAT(pipe), 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);
@@ -3728,8 +3753,10 @@ void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv)
dev_priv->display_irqs_enabled = true;
- if (intel_irqs_enabled(dev_priv))
- valleyview_display_irqs_install(dev_priv);
+ if (intel_irqs_enabled(dev_priv)) {
+ vlv_display_irq_reset(dev_priv);
+ vlv_display_irq_postinstall(dev_priv);
+ }
}
void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv)
@@ -3742,45 +3769,23 @@ void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv)
dev_priv->display_irqs_enabled = false;
if (intel_irqs_enabled(dev_priv))
- valleyview_display_irqs_uninstall(dev_priv);
+ vlv_display_irq_reset(dev_priv);
}
-static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
-{
- dev_priv->irq_mask = ~0;
-
- i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
- POSTING_READ(PORT_HOTPLUG_EN);
-
- I915_WRITE(VLV_IIR, 0xffffffff);
- I915_WRITE(VLV_IIR, 0xffffffff);
- I915_WRITE(VLV_IER, ~dev_priv->irq_mask);
- I915_WRITE(VLV_IMR, dev_priv->irq_mask);
- POSTING_READ(VLV_IMR);
-
- /* Interrupt setup is already guaranteed to be single-threaded, this is
- * just to make the assert_spin_locked check happy. */
- spin_lock_irq(&dev_priv->irq_lock);
- if (dev_priv->display_irqs_enabled)
- valleyview_display_irqs_install(dev_priv);
- spin_unlock_irq(&dev_priv->irq_lock);
-}
static int valleyview_irq_postinstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- vlv_display_irq_postinstall(dev_priv);
-
gen5_gt_irq_postinstall(dev);
- /* ack & enable invalid PTE error interrupts */
-#if 0 /* FIXME: add support to irq handler for checking these bits */
- I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK);
- I915_WRITE(DPINVGTT, DPINVGTT_EN_MASK);
-#endif
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ vlv_display_irq_postinstall(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
+ POSTING_READ(VLV_MASTER_IER);
return 0;
}
@@ -3791,7 +3796,6 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
uint32_t gt_interrupts[] = {
GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
- GT_RENDER_L3_PARITY_ERROR_INTERRUPT |
GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT |
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
@@ -3803,6 +3807,9 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT
};
+ if (HAS_L3_DPF(dev_priv))
+ gt_interrupts[0] |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
+
dev_priv->pm_irq_mask = 0xffffffff;
GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]);
GEN8_IRQ_INIT_NDX(GT, 1, ~gt_interrupts[1], gt_interrupts[1]);
@@ -3870,7 +3877,7 @@ static int gen8_irq_postinstall(struct drm_device *dev)
if (HAS_PCH_SPLIT(dev))
ibx_irq_postinstall(dev);
- I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
+ I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
POSTING_READ(GEN8_MASTER_IRQ);
return 0;
@@ -3880,11 +3887,14 @@ static int cherryview_irq_postinstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- vlv_display_irq_postinstall(dev_priv);
-
gen8_gt_irq_postinstall(dev_priv);
- I915_WRITE(GEN8_MASTER_IRQ, MASTER_INTERRUPT_ENABLE);
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ vlv_display_irq_postinstall(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
+
+ I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
POSTING_READ(GEN8_MASTER_IRQ);
return 0;
@@ -3900,20 +3910,6 @@ static void gen8_irq_uninstall(struct drm_device *dev)
gen8_irq_reset(dev);
}
-static void vlv_display_irq_uninstall(struct drm_i915_private *dev_priv)
-{
- /* Interrupt setup is already guaranteed to be single-threaded, this is
- * just to make the assert_spin_locked check happy. */
- spin_lock_irq(&dev_priv->irq_lock);
- if (dev_priv->display_irqs_enabled)
- valleyview_display_irqs_uninstall(dev_priv);
- spin_unlock_irq(&dev_priv->irq_lock);
-
- vlv_display_irq_reset(dev_priv);
-
- dev_priv->irq_mask = ~0;
-}
-
static void valleyview_irq_uninstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3922,12 +3918,16 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
return;
I915_WRITE(VLV_MASTER_IER, 0);
+ POSTING_READ(VLV_MASTER_IER);
gen5_gt_irq_reset(dev);
I915_WRITE(HWSTAM, 0xffffffff);
- vlv_display_irq_uninstall(dev_priv);
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ vlv_display_irq_reset(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
}
static void cherryview_irq_uninstall(struct drm_device *dev)
@@ -3944,7 +3944,10 @@ static void cherryview_irq_uninstall(struct drm_device *dev)
GEN5_IRQ_RESET(GEN8_PCU_);
- vlv_display_irq_uninstall(dev_priv);
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ vlv_display_irq_reset(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
}
static void ironlake_irq_uninstall(struct drm_device *dev)
@@ -4271,8 +4274,11 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
/* Consume port. Then clear IIR or we'll miss events */
if (I915_HAS_HOTPLUG(dev) &&
- iir & I915_DISPLAY_PORT_INTERRUPT)
- i9xx_hpd_irq_handler(dev);
+ iir & I915_DISPLAY_PORT_INTERRUPT) {
+ u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+ if (hotplug_status)
+ i9xx_hpd_irq_handler(dev, hotplug_status);
+ }
I915_WRITE(IIR, iir & ~flip_mask);
new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -4501,8 +4507,11 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
/* Consume port. Then clear IIR or we'll miss events */
- if (iir & I915_DISPLAY_PORT_INTERRUPT)
- i9xx_hpd_irq_handler(dev);
+ if (iir & I915_DISPLAY_PORT_INTERRUPT) {
+ u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+ if (hotplug_status)
+ i9xx_hpd_irq_handler(dev, hotplug_status);
+ }
I915_WRITE(IIR, iir & ~flip_mask);
new_iir = I915_READ(IIR); /* Flush posted writes */
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index cea5a390d8c9..58ac6c7c690b 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -79,6 +79,16 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
/* PCI config space */
+#define MCHBAR_I915 0x44
+#define MCHBAR_I965 0x48
+#define MCHBAR_SIZE (4 * 4096)
+
+#define DEVEN 0x54
+#define DEVEN_MCHBAR_EN (1 << 28)
+
+#define BSM 0x5c
+#define BSM_MASK (0xFFFF << 20)
+
#define HPLLCC 0xc0 /* 85x only */
#define GC_CLOCK_CONTROL_MASK (0x7 << 0)
#define GC_CLOCK_133_200 (0 << 0)
@@ -90,6 +100,16 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define GC_CLOCK_166_266 (6 << 0)
#define GC_CLOCK_166_250 (7 << 0)
+#define I915_GDRST 0xc0 /* PCI config register */
+#define GRDOM_FULL (0 << 2)
+#define GRDOM_RENDER (1 << 2)
+#define GRDOM_MEDIA (3 << 2)
+#define GRDOM_MASK (3 << 2)
+#define GRDOM_RESET_STATUS (1 << 1)
+#define GRDOM_RESET_ENABLE (1 << 0)
+
+#define GCDGMBUS 0xcc
+
#define GCFGC2 0xda
#define GCFGC 0xf0 /* 915+ only */
#define GC_LOW_FREQUENCY_ENABLE (1 << 7)
@@ -121,18 +141,16 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#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 GCDGMBUS 0xcc
-#define PCI_LBPC 0xf4 /* legacy/combination backlight modes, also called LBB */
+#define ASLE 0xe4
+#define ASLS 0xfc
+
+#define SWSCI 0xe8
+#define SWSCI_SCISEL (1 << 15)
+#define SWSCI_GSSCIE (1 << 0)
+
+#define LBPC 0xf4 /* legacy/combination backlight modes, also called LBB */
-/* Graphics reset regs */
-#define I915_GDRST 0xc0 /* PCI config register */
-#define GRDOM_FULL (0<<2)
-#define GRDOM_RENDER (1<<2)
-#define GRDOM_MEDIA (3<<2)
-#define GRDOM_MASK (3<<2)
-#define GRDOM_RESET_STATUS (1<<1)
-#define GRDOM_RESET_ENABLE (1<<0)
#define ILK_GDSR _MMIO(MCHBAR_MIRROR_BASE + 0x2ca4)
#define ILK_GRDOM_FULL (0<<1)
@@ -1375,14 +1393,10 @@ enum skl_disp_power_wells {
#define _PORT_REF_DW6_A 0x162198
#define _PORT_REF_DW6_BC 0x6C198
-/*
- * FIXME: BSpec/CHV ConfigDB disagrees on the following two fields, fix them
- * after testing.
- */
-#define GRC_CODE_SHIFT 23
-#define GRC_CODE_MASK (0x1FF << GRC_CODE_SHIFT)
+#define GRC_CODE_SHIFT 24
+#define GRC_CODE_MASK (0xFF << GRC_CODE_SHIFT)
#define GRC_CODE_FAST_SHIFT 16
-#define GRC_CODE_FAST_MASK (0x7F << GRC_CODE_FAST_SHIFT)
+#define GRC_CODE_FAST_MASK (0xFF << GRC_CODE_FAST_SHIFT)
#define GRC_CODE_SLOW_SHIFT 8
#define GRC_CODE_SLOW_MASK (0xFF << GRC_CODE_SLOW_SHIFT)
#define GRC_CODE_NOM_MASK 0xFF
@@ -2934,7 +2948,14 @@ enum skl_disp_power_wells {
#define GEN6_RP_STATE_CAP _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5998)
#define BXT_RP_STATE_CAP _MMIO(0x138170)
-#define INTERVAL_1_28_US(us) (((us) * 100) >> 7)
+/*
+ * Make these a multiple of magic 25 to avoid SNB (eg. Dell XPS
+ * 8300) freezing up around GPU hangs. Looks as if even
+ * scheduling/timer interrupts start misbehaving if the RPS
+ * EI/thresholds are "bad", leading to a very sluggish or even
+ * frozen machine.
+ */
+#define INTERVAL_1_28_US(us) roundup(((us) * 100) >> 7, 25)
#define INTERVAL_1_33_US(us) (((us) * 3) >> 2)
#define INTERVAL_0_833_US(us) (((us) * 6) / 5)
#define GT_INTERVAL_FROM_US(dev_priv, us) (IS_GEN9(dev_priv) ? \
@@ -2943,6 +2964,15 @@ enum skl_disp_power_wells {
INTERVAL_1_33_US(us)) : \
INTERVAL_1_28_US(us))
+#define INTERVAL_1_28_TO_US(interval) (((interval) << 7) / 100)
+#define INTERVAL_1_33_TO_US(interval) (((interval) << 2) / 3)
+#define INTERVAL_0_833_TO_US(interval) (((interval) * 5) / 6)
+#define GT_PM_INTERVAL_TO_US(dev_priv, interval) (IS_GEN9(dev_priv) ? \
+ (IS_BROXTON(dev_priv) ? \
+ INTERVAL_0_833_TO_US(interval) : \
+ INTERVAL_1_33_TO_US(interval)) : \
+ INTERVAL_1_28_TO_US(interval))
+
/*
* Logical Context regs
*/
@@ -6866,6 +6896,8 @@ enum skl_disp_power_wells {
#define VLV_SPAREG2H _MMIO(0xA194)
#define GTFIFODBG _MMIO(0x120000)
+#define GT_FIFO_SBDEDICATE_FREE_ENTRY_CHV (0x1f << 20)
+#define GT_FIFO_FREE_ENTRIES_CHV (0x7f << 13)
#define GT_FIFO_SBDROPERR (1<<6)
#define GT_FIFO_BLOBDROPERR (1<<5)
#define GT_FIFO_SB_READ_ABORTERR (1<<4)
@@ -6882,8 +6914,11 @@ enum skl_disp_power_wells {
#define HSW_IDICR _MMIO(0x9008)
#define IDIHASHMSK(x) (((x) & 0x3f) << 16)
-#define HSW_EDRAM_PRESENT _MMIO(0x120010)
+#define HSW_EDRAM_CAP _MMIO(0x120010)
#define EDRAM_ENABLED 0x1
+#define EDRAM_NUM_BANKS(cap) (((cap) >> 1) & 0xf)
+#define EDRAM_WAYS_IDX(cap) (((cap) >> 5) & 0x7)
+#define EDRAM_SETS_IDX(cap) (((cap) >> 8) & 0x3)
#define GEN6_UCGCTL1 _MMIO(0x9400)
# define GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE (1 << 16)
@@ -7161,6 +7196,7 @@ enum skl_disp_power_wells {
#define GEN9_HALF_SLICE_CHICKEN7 _MMIO(0xe194)
#define GEN9_ENABLE_YV12_BUGFIX (1<<4)
+#define GEN9_ENABLE_GPGPU_PREEMPTION (1<<2)
/* Audio */
#define G4X_AUD_VID_DID _MMIO(dev_priv->info.display_mmio_offset + 0x62020)
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index eb756c41d9e1..e72dd9a8d6bf 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -58,8 +58,6 @@
#define SLAVE_ADDR1 0x70
#define SLAVE_ADDR2 0x72
-static int panel_type;
-
/* Get BDB block size given a pointer to Block ID. */
static u32 _get_blocksize(const u8 *block_base)
{
@@ -205,17 +203,32 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
const struct lvds_dvo_timing *panel_dvo_timing;
const struct lvds_fp_timing *fp_timing;
struct drm_display_mode *panel_fixed_mode;
+ int panel_type;
int drrs_mode;
+ int ret;
lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
if (!lvds_options)
return;
dev_priv->vbt.lvds_dither = lvds_options->pixel_dither;
- if (lvds_options->panel_type == 0xff)
- return;
- panel_type = lvds_options->panel_type;
+ ret = intel_opregion_get_panel_type(dev_priv->dev);
+ if (ret >= 0) {
+ WARN_ON(ret > 0xf);
+ panel_type = ret;
+ DRM_DEBUG_KMS("Panel type: %d (OpRegion)\n", panel_type);
+ } else {
+ if (lvds_options->panel_type > 0xf) {
+ DRM_DEBUG_KMS("Invalid VBT panel type 0x%x\n",
+ lvds_options->panel_type);
+ return;
+ }
+ panel_type = lvds_options->panel_type;
+ DRM_DEBUG_KMS("Panel type: %d (VBT)\n", panel_type);
+ }
+
+ dev_priv->vbt.panel_type = panel_type;
drrs_mode = (lvds_options->dps_panel_type_bits
>> (panel_type * 2)) & MODE_MASK;
@@ -251,7 +264,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
lvds_lfp_data_ptrs,
- lvds_options->panel_type);
+ panel_type);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
if (!panel_fixed_mode)
@@ -266,7 +279,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data,
lvds_lfp_data_ptrs,
- lvds_options->panel_type);
+ panel_type);
if (fp_timing) {
/* check the resolution, just to be sure */
if (fp_timing->x_res == panel_fixed_mode->hdisplay &&
@@ -284,6 +297,7 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv,
{
const struct bdb_lfp_backlight_data *backlight_data;
const struct bdb_lfp_backlight_data_entry *entry;
+ int panel_type = dev_priv->vbt.panel_type;
backlight_data = find_section(bdb, BDB_LVDS_BACKLIGHT);
if (!backlight_data)
@@ -546,6 +560,7 @@ parse_edp(struct drm_i915_private *dev_priv, const struct bdb_header *bdb)
const struct bdb_edp *edp;
const struct edp_power_seq *edp_pps;
const struct edp_link_params *edp_link_params;
+ int panel_type = dev_priv->vbt.panel_type;
edp = find_section(bdb, BDB_EDP);
if (!edp) {
@@ -657,6 +672,7 @@ parse_psr(struct drm_i915_private *dev_priv, const struct bdb_header *bdb)
{
const struct bdb_psr *psr;
const struct psr_table *psr_table;
+ int panel_type = dev_priv->vbt.panel_type;
psr = find_section(bdb, BDB_PSR);
if (!psr) {
@@ -703,6 +719,7 @@ parse_mipi_config(struct drm_i915_private *dev_priv,
const struct bdb_mipi_config *start;
const struct mipi_config *config;
const struct mipi_pps_data *pps;
+ int panel_type = dev_priv->vbt.panel_type;
/* parse MIPI blocks only if LFP type is MIPI */
if (!intel_bios_is_dsi_present(dev_priv, NULL))
@@ -910,6 +927,7 @@ static void
parse_mipi_sequence(struct drm_i915_private *dev_priv,
const struct bdb_header *bdb)
{
+ int panel_type = dev_priv->vbt.panel_type;
const struct bdb_mipi_sequence *sequence;
const u8 *seq_data;
u32 seq_size;
diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c
index 3f57cb94d9ad..a34c23eceba0 100644
--- a/drivers/gpu/drm/i915/intel_csr.c
+++ b/drivers/gpu/drm/i915/intel_csr.c
@@ -50,6 +50,7 @@ MODULE_FIRMWARE(I915_CSR_SKL);
MODULE_FIRMWARE(I915_CSR_BXT);
#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 23)
+#define BXT_CSR_VERSION_REQUIRED CSR_VERSION(1, 7)
#define CSR_MAX_FW_SIZE 0x2FFF
#define CSR_DEFAULT_FW_OFFSET 0xFFFFFFFF
@@ -281,6 +282,7 @@ static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv,
uint32_t dmc_offset = CSR_DEFAULT_FW_OFFSET, readcount = 0, nbytes;
uint32_t i;
uint32_t *dmc_payload;
+ uint32_t required_min_version;
if (!fw)
return NULL;
@@ -296,15 +298,23 @@ static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv,
csr->version = css_header->version;
- if ((IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) &&
- csr->version < SKL_CSR_VERSION_REQUIRED) {
- DRM_INFO("Refusing to load old Skylake DMC firmware v%u.%u,"
+ if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
+ required_min_version = SKL_CSR_VERSION_REQUIRED;
+ } else if (IS_BROXTON(dev_priv)) {
+ required_min_version = BXT_CSR_VERSION_REQUIRED;
+ } else {
+ MISSING_CASE(INTEL_REVID(dev_priv));
+ required_min_version = 0;
+ }
+
+ if (csr->version < required_min_version) {
+ DRM_INFO("Refusing to load old DMC firmware v%u.%u,"
" please upgrade to v%u.%u or later"
" [" FIRMWARE_URL "].\n",
CSR_VERSION_MAJOR(csr->version),
CSR_VERSION_MINOR(csr->version),
- CSR_VERSION_MAJOR(SKL_CSR_VERSION_REQUIRED),
- CSR_VERSION_MINOR(SKL_CSR_VERSION_REQUIRED));
+ CSR_VERSION_MAJOR(required_min_version),
+ CSR_VERSION_MINOR(required_min_version));
return NULL;
}
@@ -457,10 +467,50 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv)
}
/**
+ * intel_csr_ucode_suspend() - prepare CSR firmware before system suspend
+ * @dev_priv: i915 drm device
+ *
+ * Prepare the DMC firmware before entering system suspend. This includes
+ * flushing pending work items and releasing any resources acquired during
+ * init.
+ */
+void intel_csr_ucode_suspend(struct drm_i915_private *dev_priv)
+{
+ if (!HAS_CSR(dev_priv))
+ return;
+
+ flush_work(&dev_priv->csr.work);
+
+ /* Drop the reference held in case DMC isn't loaded. */
+ if (!dev_priv->csr.dmc_payload)
+ intel_display_power_put(dev_priv, POWER_DOMAIN_INIT);
+}
+
+/**
+ * intel_csr_ucode_resume() - init CSR firmware during system resume
+ * @dev_priv: i915 drm device
+ *
+ * Reinitialize the DMC firmware during system resume, reacquiring any
+ * resources released in intel_csr_ucode_suspend().
+ */
+void intel_csr_ucode_resume(struct drm_i915_private *dev_priv)
+{
+ if (!HAS_CSR(dev_priv))
+ return;
+
+ /*
+ * Reacquire the reference to keep RPM disabled in case DMC isn't
+ * loaded.
+ */
+ if (!dev_priv->csr.dmc_payload)
+ intel_display_power_get(dev_priv, POWER_DOMAIN_INIT);
+}
+
+/**
* intel_csr_ucode_fini() - unload the CSR firmware.
* @dev_priv: i915 drm device.
*
- * Firmmware unloading includes freeing the internal momory and reset the
+ * Firmmware unloading includes freeing the internal memory and reset the
* firmware loading status.
*/
void intel_csr_ucode_fini(struct drm_i915_private *dev_priv)
@@ -468,7 +518,7 @@ void intel_csr_ucode_fini(struct drm_i915_private *dev_priv)
if (!HAS_CSR(dev_priv))
return;
- flush_work(&dev_priv->csr.work);
+ intel_csr_ucode_suspend(dev_priv);
kfree(dev_priv->csr.dmc_payload);
}
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 921edf183d22..e30e1781fd71 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -443,9 +443,17 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder)
} else if (IS_BROADWELL(dev_priv)) {
ddi_translations_fdi = bdw_ddi_translations_fdi;
ddi_translations_dp = bdw_ddi_translations_dp;
- ddi_translations_edp = bdw_ddi_translations_edp;
+
+ if (dev_priv->vbt.edp.low_vswing) {
+ ddi_translations_edp = bdw_ddi_translations_edp;
+ n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
+ } else {
+ ddi_translations_edp = bdw_ddi_translations_dp;
+ n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
+ }
+
ddi_translations_hdmi = bdw_ddi_translations_hdmi;
- n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
+
n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
hdmi_default_entry = 7;
@@ -1722,12 +1730,78 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
}
}
+static bool broxton_phy_is_enabled(struct drm_i915_private *dev_priv,
+ enum dpio_phy phy)
+{
+ if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & GT_DISPLAY_POWER_ON(phy)))
+ return false;
+
+ if ((I915_READ(BXT_PORT_CL1CM_DW0(phy)) &
+ (PHY_POWER_GOOD | PHY_RESERVED)) != PHY_POWER_GOOD) {
+ DRM_DEBUG_DRIVER("DDI PHY %d powered, but power hasn't settled\n",
+ phy);
+
+ return false;
+ }
+
+ if (phy == DPIO_PHY1 &&
+ !(I915_READ(BXT_PORT_REF_DW3(DPIO_PHY1)) & GRC_DONE)) {
+ DRM_DEBUG_DRIVER("DDI PHY 1 powered, but GRC isn't done\n");
+
+ return false;
+ }
+
+ if (!(I915_READ(BXT_PHY_CTL_FAMILY(phy)) & COMMON_RESET_DIS)) {
+ DRM_DEBUG_DRIVER("DDI PHY %d powered, but still in reset\n",
+ phy);
+
+ return false;
+ }
+
+ return true;
+}
+
+static u32 broxton_get_grc(struct drm_i915_private *dev_priv, enum dpio_phy phy)
+{
+ u32 val = I915_READ(BXT_PORT_REF_DW6(phy));
+
+ return (val & GRC_CODE_MASK) >> GRC_CODE_SHIFT;
+}
+
+static void broxton_phy_wait_grc_done(struct drm_i915_private *dev_priv,
+ enum dpio_phy phy)
+{
+ if (wait_for(I915_READ(BXT_PORT_REF_DW3(phy)) & GRC_DONE, 10))
+ DRM_ERROR("timeout waiting for PHY%d GRC\n", phy);
+}
+
+static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv,
+ enum dpio_phy phy);
+
static void broxton_phy_init(struct drm_i915_private *dev_priv,
enum dpio_phy phy)
{
enum port port;
u32 ports, val;
+ if (broxton_phy_is_enabled(dev_priv, phy)) {
+ /* Still read out the GRC value for state verification */
+ if (phy == DPIO_PHY0)
+ dev_priv->bxt_phy_grc = broxton_get_grc(dev_priv, phy);
+
+ if (broxton_phy_verify_state(dev_priv, phy)) {
+ DRM_DEBUG_DRIVER("DDI PHY %d already enabled, "
+ "won't reprogram it\n", phy);
+
+ return;
+ }
+
+ DRM_DEBUG_DRIVER("DDI PHY %d enabled with invalid state, "
+ "force reprogramming it\n", phy);
+ } else {
+ DRM_DEBUG_DRIVER("DDI PHY %d not enabled, enabling it\n", phy);
+ }
+
val = I915_READ(BXT_P_CR_GT_DISP_PWRON);
val |= GT_DISPLAY_POWER_ON(phy);
I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val);
@@ -1798,6 +1872,9 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv,
* enabled.
* TODO: port C is only connected on BXT-P, so on BXT0/1 we should
* power down the second channel on PHY0 as well.
+ *
+ * FIXME: Clarify programming of the following, the register is
+ * read-only with bit 6 fixed at 0 at least in stepping A.
*/
if (phy == DPIO_PHY1)
val |= OCL2_LDOFUSE_PWR_DIS;
@@ -1810,12 +1887,10 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv,
* the corresponding calibrated value from PHY1, and disable
* the automatic calibration on PHY0.
*/
- if (wait_for(I915_READ(BXT_PORT_REF_DW3(DPIO_PHY1)) & GRC_DONE,
- 10))
- DRM_ERROR("timeout waiting for PHY1 GRC\n");
+ broxton_phy_wait_grc_done(dev_priv, DPIO_PHY1);
- val = I915_READ(BXT_PORT_REF_DW6(DPIO_PHY1));
- val = (val & GRC_CODE_MASK) >> GRC_CODE_SHIFT;
+ val = dev_priv->bxt_phy_grc = broxton_get_grc(dev_priv,
+ DPIO_PHY1);
grc_code = val << GRC_CODE_FAST_SHIFT |
val << GRC_CODE_SLOW_SHIFT |
val;
@@ -1825,17 +1900,27 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv,
val |= GRC_DIS | GRC_RDY_OVRD;
I915_WRITE(BXT_PORT_REF_DW8(DPIO_PHY0), val);
}
+ /*
+ * During PHY1 init delay waiting for GRC calibration to finish, since
+ * it can happen in parallel with the subsequent PHY0 init.
+ */
val = I915_READ(BXT_PHY_CTL_FAMILY(phy));
val |= COMMON_RESET_DIS;
I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val);
}
-void broxton_ddi_phy_init(struct drm_device *dev)
+void broxton_ddi_phy_init(struct drm_i915_private *dev_priv)
{
/* Enable PHY1 first since it provides Rcomp for PHY0 */
- broxton_phy_init(dev->dev_private, DPIO_PHY1);
- broxton_phy_init(dev->dev_private, DPIO_PHY0);
+ broxton_phy_init(dev_priv, DPIO_PHY1);
+ broxton_phy_init(dev_priv, DPIO_PHY0);
+
+ /*
+ * If BIOS enabled only PHY0 and not PHY1, we skipped waiting for the
+ * PHY1 GRC calibration to finish, so wait for it here.
+ */
+ broxton_phy_wait_grc_done(dev_priv, DPIO_PHY1);
}
static void broxton_phy_uninit(struct drm_i915_private *dev_priv,
@@ -1846,17 +1931,126 @@ static void broxton_phy_uninit(struct drm_i915_private *dev_priv,
val = I915_READ(BXT_PHY_CTL_FAMILY(phy));
val &= ~COMMON_RESET_DIS;
I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val);
+
+ val = I915_READ(BXT_P_CR_GT_DISP_PWRON);
+ val &= ~GT_DISPLAY_POWER_ON(phy);
+ I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val);
}
-void broxton_ddi_phy_uninit(struct drm_device *dev)
+void broxton_ddi_phy_uninit(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
broxton_phy_uninit(dev_priv, DPIO_PHY1);
broxton_phy_uninit(dev_priv, DPIO_PHY0);
+}
+
+static bool __printf(6, 7)
+__phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy,
+ i915_reg_t reg, u32 mask, u32 expected,
+ const char *reg_fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ u32 val;
+
+ val = I915_READ(reg);
+ if ((val & mask) == expected)
+ return true;
+
+ va_start(args, reg_fmt);
+ vaf.fmt = reg_fmt;
+ vaf.va = &args;
- /* FIXME: do this in broxton_phy_uninit per phy */
- I915_WRITE(BXT_P_CR_GT_DISP_PWRON, 0);
+ DRM_DEBUG_DRIVER("DDI PHY %d reg %pV [%08x] state mismatch: "
+ "current %08x, expected %08x (mask %08x)\n",
+ phy, &vaf, reg.reg, val, (val & ~mask) | expected,
+ mask);
+
+ va_end(args);
+
+ return false;
+}
+
+static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv,
+ enum dpio_phy phy)
+{
+ enum port port;
+ u32 ports;
+ uint32_t mask;
+ bool ok;
+
+#define _CHK(reg, mask, exp, fmt, ...) \
+ __phy_reg_verify_state(dev_priv, phy, reg, mask, exp, fmt, \
+ ## __VA_ARGS__)
+
+ /* We expect the PHY to be always enabled */
+ if (!broxton_phy_is_enabled(dev_priv, phy))
+ return false;
+
+ ok = true;
+
+ if (phy == DPIO_PHY0)
+ ports = BIT(PORT_B) | BIT(PORT_C);
+ else
+ ports = BIT(PORT_A);
+
+ for_each_port_masked(port, ports) {
+ int lane;
+
+ for (lane = 0; lane < 4; lane++)
+ ok &= _CHK(BXT_PORT_TX_DW14_LN(port, lane),
+ LATENCY_OPTIM,
+ lane != 1 ? LATENCY_OPTIM : 0,
+ "BXT_PORT_TX_DW14_LN(%d, %d)", port, lane);
+ }
+
+ /* PLL Rcomp code offset */
+ ok &= _CHK(BXT_PORT_CL1CM_DW9(phy),
+ IREF0RC_OFFSET_MASK, 0xe4 << IREF0RC_OFFSET_SHIFT,
+ "BXT_PORT_CL1CM_DW9(%d)", phy);
+ ok &= _CHK(BXT_PORT_CL1CM_DW10(phy),
+ IREF1RC_OFFSET_MASK, 0xe4 << IREF1RC_OFFSET_SHIFT,
+ "BXT_PORT_CL1CM_DW10(%d)", phy);
+
+ /* Power gating */
+ mask = OCL1_POWER_DOWN_EN | DW28_OLDO_DYN_PWR_DOWN_EN | SUS_CLK_CONFIG;
+ ok &= _CHK(BXT_PORT_CL1CM_DW28(phy), mask, mask,
+ "BXT_PORT_CL1CM_DW28(%d)", phy);
+
+ if (phy == DPIO_PHY0)
+ ok &= _CHK(BXT_PORT_CL2CM_DW6_BC,
+ DW6_OLDO_DYN_PWR_DOWN_EN, DW6_OLDO_DYN_PWR_DOWN_EN,
+ "BXT_PORT_CL2CM_DW6_BC");
+
+ /*
+ * TODO: Verify BXT_PORT_CL1CM_DW30 bit OCL2_LDOFUSE_PWR_DIS,
+ * at least on stepping A this bit is read-only and fixed at 0.
+ */
+
+ if (phy == DPIO_PHY0) {
+ u32 grc_code = dev_priv->bxt_phy_grc;
+
+ grc_code = grc_code << GRC_CODE_FAST_SHIFT |
+ grc_code << GRC_CODE_SLOW_SHIFT |
+ grc_code;
+ mask = GRC_CODE_FAST_MASK | GRC_CODE_SLOW_MASK |
+ GRC_CODE_NOM_MASK;
+ ok &= _CHK(BXT_PORT_REF_DW6(DPIO_PHY0), mask, grc_code,
+ "BXT_PORT_REF_DW6(%d)", DPIO_PHY0);
+
+ mask = GRC_DIS | GRC_RDY_OVRD;
+ ok &= _CHK(BXT_PORT_REF_DW8(DPIO_PHY0), mask, mask,
+ "BXT_PORT_REF_DW8(%d)", DPIO_PHY0);
+ }
+
+ return ok;
+#undef _CHK
+}
+
+void broxton_ddi_phy_verify_state(struct drm_i915_private *dev_priv)
+{
+ if (!broxton_phy_verify_state(dev_priv, DPIO_PHY0) ||
+ !broxton_phy_verify_state(dev_priv, DPIO_PHY1))
+ i915_report_error(dev_priv, "DDI PHY state mismatch\n");
}
void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp)
@@ -2044,12 +2238,6 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
intel_ddi_clock_get(encoder, pipe_config);
}
-static void intel_ddi_destroy(struct drm_encoder *encoder)
-{
- /* HDMI has nothing special to destroy, so we can go with this. */
- intel_dp_encoder_destroy(encoder);
-}
-
static bool intel_ddi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config)
{
@@ -2068,7 +2256,8 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder,
}
static const struct drm_encoder_funcs intel_ddi_funcs = {
- .destroy = intel_ddi_destroy,
+ .reset = intel_dp_encoder_reset,
+ .destroy = intel_dp_encoder_destroy,
};
static struct intel_connector *
@@ -2167,6 +2356,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
intel_encoder->post_disable = intel_ddi_post_disable;
intel_encoder->get_hw_state = intel_ddi_get_hw_state;
intel_encoder->get_config = intel_ddi_get_config;
+ intel_encoder->suspend = intel_dp_encoder_suspend;
intel_dig_port->port = port;
intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 5d29b838d8d7..73299f9e77a8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -1530,45 +1530,47 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMID);
}
+static void _vlv_enable_pll(struct intel_crtc *crtc,
+ const struct intel_crtc_state *pipe_config)
+{
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ enum pipe pipe = crtc->pipe;
+
+ I915_WRITE(DPLL(pipe), pipe_config->dpll_hw_state.dpll);
+ POSTING_READ(DPLL(pipe));
+ udelay(150);
+
+ if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
+ DRM_ERROR("DPLL %d failed to lock\n", pipe);
+}
+
static void vlv_enable_pll(struct intel_crtc *crtc,
const struct intel_crtc_state *pipe_config)
{
- struct drm_device *dev = crtc->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
enum pipe pipe = crtc->pipe;
- i915_reg_t reg = DPLL(pipe);
- u32 dpll = pipe_config->dpll_hw_state.dpll;
assert_pipe_disabled(dev_priv, pipe);
/* PLL is protected by panel, make sure we can write it */
assert_panel_unlocked(dev_priv, pipe);
- I915_WRITE(reg, dpll);
- POSTING_READ(reg);
- udelay(150);
-
- if (wait_for(((I915_READ(reg) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
- DRM_ERROR("DPLL %d failed to lock\n", pipe);
+ if (pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE)
+ _vlv_enable_pll(crtc, pipe_config);
I915_WRITE(DPLL_MD(pipe), pipe_config->dpll_hw_state.dpll_md);
POSTING_READ(DPLL_MD(pipe));
}
-static void chv_enable_pll(struct intel_crtc *crtc,
- const struct intel_crtc_state *pipe_config)
+
+static void _chv_enable_pll(struct intel_crtc *crtc,
+ const struct intel_crtc_state *pipe_config)
{
- struct drm_device *dev = crtc->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
enum pipe pipe = crtc->pipe;
enum dpio_channel port = vlv_pipe_to_channel(pipe);
u32 tmp;
- assert_pipe_disabled(dev_priv, pipe);
-
- /* PLL is protected by panel, make sure we can write it */
- assert_panel_unlocked(dev_priv, pipe);
-
mutex_lock(&dev_priv->sb_lock);
/* Enable back the 10bit clock to display controller */
@@ -1589,6 +1591,21 @@ static void chv_enable_pll(struct intel_crtc *crtc,
/* Check PLL is locked */
if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
DRM_ERROR("PLL %d failed to lock\n", pipe);
+}
+
+static void chv_enable_pll(struct intel_crtc *crtc,
+ const struct intel_crtc_state *pipe_config)
+{
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ enum pipe pipe = crtc->pipe;
+
+ assert_pipe_disabled(dev_priv, pipe);
+
+ /* PLL is protected by panel, make sure we can write it */
+ assert_panel_unlocked(dev_priv, pipe);
+
+ if (pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE)
+ _chv_enable_pll(crtc, pipe_config);
if (pipe != PIPE_A) {
/*
@@ -3198,12 +3215,12 @@ void intel_finish_reset(struct drm_device *dev)
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 reset_counter;
bool pending;
- if (i915_reset_in_progress(&dev_priv->gpu_error) ||
- intel_crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
+ reset_counter = i915_reset_counter(&to_i915(dev)->gpu_error);
+ if (intel_crtc->reset_counter != reset_counter)
return false;
spin_lock_irq(&dev->event_lock);
@@ -3805,9 +3822,7 @@ static void page_flip_completed(struct intel_crtc *intel_crtc)
intel_crtc->unpin_work = NULL;
if (work->event)
- drm_send_vblank_event(intel_crtc->base.dev,
- intel_crtc->pipe,
- work->event);
+ drm_crtc_send_vblank_event(&intel_crtc->base, work->event);
drm_crtc_vblank_put(&intel_crtc->base);
@@ -4088,12 +4103,6 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
I915_WRITE(FDI_RX_TUSIZE1(pipe),
I915_READ(PIPE_DATA_M1(pipe)) & TU_SIZE_MASK);
- /*
- * Sometimes spurious CPU pipe underruns happen during FDI
- * training, at least with VGA+HDMI cloning. Suppress them.
- */
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
-
/* For PCH output, training FDI link */
dev_priv->display.fdi_link_train(crtc);
@@ -4128,8 +4137,6 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
intel_fdi_normal_train(crtc);
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
-
/* For PCH DP, enable TRANS_DP_CTL */
if (HAS_PCH_CPT(dev) && intel_crtc->config->has_dp_encoder) {
const struct drm_display_mode *adjusted_mode =
@@ -4732,6 +4739,18 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
if (WARN_ON(intel_crtc->active))
return;
+ /*
+ * Sometimes spurious CPU pipe underruns happen during FDI
+ * training, at least with VGA+HDMI cloning. Suppress them.
+ *
+ * On ILK we get an occasional spurious CPU pipe underruns
+ * between eDP port A enable and vdd enable. Also PCH port
+ * enable seems to result in the occasional CPU pipe underrun.
+ *
+ * Spurious PCH underruns also occur during PCH enabling.
+ */
+ if (intel_crtc->config->has_pch_encoder || IS_GEN5(dev_priv))
+ intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
if (intel_crtc->config->has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, false);
@@ -4753,8 +4772,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
intel_crtc->active = true;
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
-
for_each_encoder_on_crtc(dev, crtc, encoder)
if (encoder->pre_enable)
encoder->pre_enable(encoder);
@@ -4796,6 +4813,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
/* Must wait for vblank to avoid spurious PCH FIFO underruns */
if (intel_crtc->config->has_pch_encoder)
intel_wait_for_vblank(dev, pipe);
+ intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true);
}
@@ -4948,8 +4966,15 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- if (intel_crtc->config->has_pch_encoder)
+ /*
+ * Sometimes spurious CPU pipe underruns happen when the
+ * pipe is already disabled, but FDI RX/TX is still enabled.
+ * Happens at least with VGA+HDMI cloning. Suppress them.
+ */
+ if (intel_crtc->config->has_pch_encoder) {
+ intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, false);
+ }
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->disable(encoder);
@@ -4957,22 +4982,12 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
drm_crtc_vblank_off(crtc);
assert_vblank_disabled(crtc);
- /*
- * Sometimes spurious CPU pipe underruns happen when the
- * pipe is already disabled, but FDI RX/TX is still enabled.
- * Happens at least with VGA+HDMI cloning. Suppress them.
- */
- if (intel_crtc->config->has_pch_encoder)
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
-
intel_disable_pipe(intel_crtc);
ironlake_pfit_disable(intel_crtc, false);
- if (intel_crtc->config->has_pch_encoder) {
+ if (intel_crtc->config->has_pch_encoder)
ironlake_fdi_disable(crtc);
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
- }
for_each_encoder_on_crtc(dev, crtc, encoder)
if (encoder->post_disable)
@@ -5002,6 +5017,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
ironlake_fdi_pll_disable(intel_crtc);
}
+ intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true);
}
@@ -5329,9 +5345,8 @@ static void intel_update_cdclk(struct drm_device *dev)
intel_update_max_cdclk(dev);
}
-static void broxton_set_cdclk(struct drm_device *dev, int frequency)
+static void broxton_set_cdclk(struct drm_i915_private *dev_priv, int frequency)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t divider;
uint32_t ratio;
uint32_t current_freq;
@@ -5445,33 +5460,46 @@ static void broxton_set_cdclk(struct drm_device *dev, int frequency)
return;
}
- intel_update_cdclk(dev);
+ intel_update_cdclk(dev_priv->dev);
}
-void broxton_init_cdclk(struct drm_device *dev)
+static bool broxton_cdclk_is_enabled(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t val;
+ if (!(I915_READ(BXT_DE_PLL_ENABLE) & BXT_DE_PLL_PLL_ENABLE))
+ return false;
- /*
- * NDE_RSTWRN_OPT RST PCH Handshake En must always be 0b on BXT
- * or else the reset will hang because there is no PCH to respond.
- * Move the handshake programming to initialization sequence.
- * Previously was left up to BIOS.
- */
- val = I915_READ(HSW_NDE_RSTWRN_OPT);
- val &= ~RESET_PCH_HANDSHAKE_ENABLE;
- I915_WRITE(HSW_NDE_RSTWRN_OPT, val);
+ /* TODO: Check for a valid CDCLK rate */
+
+ if (!(I915_READ(DBUF_CTL) & DBUF_POWER_REQUEST)) {
+ DRM_DEBUG_DRIVER("CDCLK enabled, but DBUF power not requested\n");
+
+ return false;
+ }
+
+ if (!(I915_READ(DBUF_CTL) & DBUF_POWER_STATE)) {
+ DRM_DEBUG_DRIVER("CDCLK enabled, but DBUF power hasn't settled\n");
+
+ return false;
+ }
+
+ return true;
+}
- /* Enable PG1 for cdclk */
- intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS);
+bool broxton_cdclk_verify_state(struct drm_i915_private *dev_priv)
+{
+ return broxton_cdclk_is_enabled(dev_priv);
+}
+void broxton_init_cdclk(struct drm_i915_private *dev_priv)
+{
/* check if cd clock is enabled */
- if (I915_READ(BXT_DE_PLL_ENABLE) & BXT_DE_PLL_PLL_ENABLE) {
- DRM_DEBUG_KMS("Display already initialized\n");
+ if (broxton_cdclk_is_enabled(dev_priv)) {
+ DRM_DEBUG_KMS("CDCLK already enabled, won't reprogram it\n");
return;
}
+ DRM_DEBUG_KMS("CDCLK not enabled, enabling it\n");
+
/*
* FIXME:
* - The initial CDCLK needs to be read from VBT.
@@ -5479,7 +5507,7 @@ void broxton_init_cdclk(struct drm_device *dev)
* - check if setting the max (or any) cdclk freq is really necessary
* here, it belongs to modeset time
*/
- broxton_set_cdclk(dev, 624000);
+ broxton_set_cdclk(dev_priv, 624000);
I915_WRITE(DBUF_CTL, I915_READ(DBUF_CTL) | DBUF_POWER_REQUEST);
POSTING_READ(DBUF_CTL);
@@ -5490,10 +5518,8 @@ void broxton_init_cdclk(struct drm_device *dev)
DRM_ERROR("DBuf power enable timeout!\n");
}
-void broxton_uninit_cdclk(struct drm_device *dev)
+void broxton_uninit_cdclk(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
I915_WRITE(DBUF_CTL, I915_READ(DBUF_CTL) & ~DBUF_POWER_REQUEST);
POSTING_READ(DBUF_CTL);
@@ -5503,9 +5529,7 @@ void broxton_uninit_cdclk(struct drm_device *dev)
DRM_ERROR("DBuf power disable timeout!\n");
/* Set minimum (bypass) frequency, in effect turning off the DE PLL */
- broxton_set_cdclk(dev, 19200);
-
- intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
+ broxton_set_cdclk(dev_priv, 19200);
}
static const struct skl_cdclk_entry {
@@ -6072,14 +6096,12 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
if (encoder->pre_pll_enable)
encoder->pre_pll_enable(encoder);
- if (!intel_crtc->config->has_dsi_encoder) {
- if (IS_CHERRYVIEW(dev)) {
- chv_prepare_pll(intel_crtc, intel_crtc->config);
- chv_enable_pll(intel_crtc, intel_crtc->config);
- } else {
- vlv_prepare_pll(intel_crtc, intel_crtc->config);
- vlv_enable_pll(intel_crtc, intel_crtc->config);
- }
+ if (IS_CHERRYVIEW(dev)) {
+ chv_prepare_pll(intel_crtc, intel_crtc->config);
+ chv_enable_pll(intel_crtc, intel_crtc->config);
+ } else {
+ vlv_prepare_pll(intel_crtc, intel_crtc->config);
+ vlv_enable_pll(intel_crtc, intel_crtc->config);
}
for_each_encoder_on_crtc(dev, crtc, encoder)
@@ -6117,7 +6139,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
struct intel_encoder *encoder;
struct intel_crtc_state *pipe_config =
to_intel_crtc_state(crtc->state);
- int pipe = intel_crtc->pipe;
+ enum pipe pipe = intel_crtc->pipe;
if (WARN_ON(intel_crtc->active))
return;
@@ -7173,11 +7195,15 @@ static void vlv_compute_dpll(struct intel_crtc *crtc,
struct intel_crtc_state *pipe_config)
{
pipe_config->dpll_hw_state.dpll = DPLL_INTEGRATED_REF_CLK_VLV |
- DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS |
- DPLL_VCO_ENABLE | DPLL_EXT_BUFFER_ENABLE_VLV;
+ DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
if (crtc->pipe != PIPE_A)
pipe_config->dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+ /* DPLL not used with DSI, but still need the rest set up */
+ if (!pipe_config->has_dsi_encoder)
+ pipe_config->dpll_hw_state.dpll |= DPLL_VCO_ENABLE |
+ DPLL_EXT_BUFFER_ENABLE_VLV;
+
pipe_config->dpll_hw_state.dpll_md =
(pipe_config->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
}
@@ -7186,11 +7212,14 @@ static void chv_compute_dpll(struct intel_crtc *crtc,
struct intel_crtc_state *pipe_config)
{
pipe_config->dpll_hw_state.dpll = DPLL_SSC_REF_CLK_CHV |
- DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS |
- DPLL_VCO_ENABLE;
+ DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
if (crtc->pipe != PIPE_A)
pipe_config->dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+ /* DPLL not used with DSI, but still need the rest set up */
+ if (!pipe_config->has_dsi_encoder)
+ pipe_config->dpll_hw_state.dpll |= DPLL_VCO_ENABLE;
+
pipe_config->dpll_hw_state.dpll_md =
(pipe_config->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
}
@@ -7200,11 +7229,20 @@ static void vlv_prepare_pll(struct intel_crtc *crtc,
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- int pipe = crtc->pipe;
+ enum pipe pipe = crtc->pipe;
u32 mdiv;
u32 bestn, bestm1, bestm2, bestp1, bestp2;
u32 coreclk, reg_val;
+ /* Enable Refclk */
+ I915_WRITE(DPLL(pipe),
+ pipe_config->dpll_hw_state.dpll &
+ ~(DPLL_VCO_ENABLE | DPLL_EXT_BUFFER_ENABLE_VLV));
+
+ /* No need to actually set up the DPLL with DSI */
+ if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0)
+ return;
+
mutex_lock(&dev_priv->sb_lock);
bestn = pipe_config->dpll.n;
@@ -7291,14 +7329,21 @@ static void chv_prepare_pll(struct intel_crtc *crtc,
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- int pipe = crtc->pipe;
- i915_reg_t dpll_reg = DPLL(crtc->pipe);
+ enum pipe pipe = crtc->pipe;
enum dpio_channel port = vlv_pipe_to_channel(pipe);
u32 loopfilter, tribuf_calcntr;
u32 bestn, bestm1, bestm2, bestp1, bestp2, bestm2_frac;
u32 dpio_val;
int vco;
+ /* Enable Refclk and SSC */
+ I915_WRITE(DPLL(pipe),
+ pipe_config->dpll_hw_state.dpll & ~DPLL_VCO_ENABLE);
+
+ /* No need to actually set up the DPLL with DSI */
+ if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0)
+ return;
+
bestn = pipe_config->dpll.n;
bestm2_frac = pipe_config->dpll.m2 & 0x3fffff;
bestm1 = pipe_config->dpll.m1;
@@ -7309,12 +7354,6 @@ static void chv_prepare_pll(struct intel_crtc *crtc,
dpio_val = 0;
loopfilter = 0;
- /*
- * Enable Refclk and SSC
- */
- I915_WRITE(dpll_reg,
- pipe_config->dpll_hw_state.dpll & ~DPLL_VCO_ENABLE);
-
mutex_lock(&dev_priv->sb_lock);
/* p1 and p2 divider */
@@ -7929,9 +7968,6 @@ static int chv_crtc_compute_clock(struct intel_crtc *crtc,
memset(&crtc_state->dpll_hw_state, 0,
sizeof(crtc_state->dpll_hw_state));
- if (crtc_state->has_dsi_encoder)
- return 0;
-
if (!crtc_state->clock_set &&
!chv_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
refclk, NULL, &crtc_state->dpll)) {
@@ -7953,9 +7989,6 @@ static int vlv_crtc_compute_clock(struct intel_crtc *crtc,
memset(&crtc_state->dpll_hw_state, 0,
sizeof(crtc_state->dpll_hw_state));
- if (crtc_state->has_dsi_encoder)
- return 0;
-
if (!crtc_state->clock_set &&
!vlv_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
refclk, NULL, &crtc_state->dpll)) {
@@ -8008,8 +8041,8 @@ static void vlv_crtc_clock_get(struct intel_crtc *crtc,
u32 mdiv;
int refclk = 100000;
- /* In case of MIPI DPLL will not even be used */
- if (!(pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE))
+ /* In case of DSI, DPLL will not be used */
+ if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0)
return;
mutex_lock(&dev_priv->sb_lock);
@@ -8105,6 +8138,10 @@ static void chv_crtc_clock_get(struct intel_crtc *crtc,
u32 cmn_dw13, pll_dw0, pll_dw1, pll_dw2, pll_dw3;
int refclk = 100000;
+ /* In case of DSI, DPLL will not be used */
+ if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0)
+ return;
+
mutex_lock(&dev_priv->sb_lock);
cmn_dw13 = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW13(port));
pll_dw0 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW0(port));
@@ -9533,7 +9570,7 @@ static void broxton_modeset_commit_cdclk(struct drm_atomic_state *old_state)
to_intel_atomic_state(old_state);
unsigned int req_cdclk = old_intel_state->dev_cdclk;
- broxton_set_cdclk(dev, req_cdclk);
+ broxton_set_cdclk(to_i915(dev), req_cdclk);
}
/* compute the max rate for new configuration */
@@ -10903,9 +10940,10 @@ static bool page_flip_finished(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned reset_counter;
- if (i915_reset_in_progress(&dev_priv->gpu_error) ||
- crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
+ reset_counter = i915_reset_counter(&dev_priv->gpu_error);
+ if (crtc->reset_counter != reset_counter)
return true;
/*
@@ -11359,7 +11397,6 @@ static void intel_mmio_flip_work_func(struct work_struct *work)
if (mmio_flip->req) {
WARN_ON(__i915_wait_request(mmio_flip->req,
- mmio_flip->crtc->reset_counter,
false, NULL,
&mmio_flip->i915->rps.mmioflips));
i915_gem_request_unreference__unlocked(mmio_flip->req);
@@ -11567,8 +11604,13 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
if (ret)
goto cleanup;
+ intel_crtc->reset_counter = i915_reset_counter(&dev_priv->gpu_error);
+ if (__i915_reset_in_progress_or_wedged(intel_crtc->reset_counter)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
atomic_inc(&intel_crtc->unpin_work_count);
- intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
work->flip_count = I915_READ(PIPE_FLIPCOUNT_G4X(pipe)) + 1;
@@ -11654,7 +11696,7 @@ cleanup_unpin:
intel_unpin_fb_obj(fb, crtc->primary->state->rotation);
cleanup_pending:
if (!IS_ERR_OR_NULL(request))
- i915_gem_request_cancel(request);
+ i915_add_request_no_flush(request);
atomic_dec(&intel_crtc->unpin_work_count);
mutex_unlock(&dev->struct_mutex);
cleanup:
@@ -11704,7 +11746,7 @@ retry:
if (ret == 0 && event) {
spin_lock_irq(&dev->event_lock);
- drm_send_vblank_event(dev, pipe, event);
+ drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&dev->event_lock);
}
}
@@ -12686,7 +12728,7 @@ intel_pipe_config_compare(struct drm_device *dev,
PIPE_CONF_CHECK_X(gmch_pfit.control);
/* pfit ratios are autocomputed by the hw on gen4+ */
if (INTEL_INFO(dev)->gen < 4)
- PIPE_CONF_CHECK_I(gmch_pfit.pgm_ratios);
+ PIPE_CONF_CHECK_X(gmch_pfit.pgm_ratios);
PIPE_CONF_CHECK_X(gmch_pfit.lvds_border_bits);
if (!adjust) {
@@ -12721,6 +12763,9 @@ intel_pipe_config_compare(struct drm_device *dev,
PIPE_CONF_CHECK_X(dpll_hw_state.cfgcr1);
PIPE_CONF_CHECK_X(dpll_hw_state.cfgcr2);
+ PIPE_CONF_CHECK_X(dsi_pll.ctrl);
+ PIPE_CONF_CHECK_X(dsi_pll.div);
+
if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5)
PIPE_CONF_CHECK_I(pipe_bpp);
@@ -13401,6 +13446,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev,
}
for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ if (state->legacy_cursor_update)
+ continue;
+
ret = intel_crtc_wait_for_pending_flips(crtc);
if (ret)
return ret;
@@ -13414,12 +13462,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev,
return ret;
ret = drm_atomic_helper_prepare_planes(dev, state);
- if (!ret && !nonblock && !i915_reset_in_progress(&dev_priv->gpu_error)) {
- u32 reset_counter;
-
- reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&dev->struct_mutex);
+ if (!ret && !nonblock) {
for_each_plane_in_state(state, plane, plane_state, i) {
struct intel_plane_state *intel_plane_state =
to_intel_plane_state(plane_state);
@@ -13428,25 +13473,18 @@ static int intel_atomic_prepare_commit(struct drm_device *dev,
continue;
ret = __i915_wait_request(intel_plane_state->wait_req,
- reset_counter, true,
- NULL, NULL);
-
- /* Swallow -EIO errors to allow updates during hw lockup. */
- if (ret == -EIO)
- ret = 0;
-
- if (ret)
+ true, NULL, NULL);
+ if (ret) {
+ /* Any hang should be swallowed by the wait */
+ WARN_ON(ret == -EIO);
+ mutex_lock(&dev->struct_mutex);
+ drm_atomic_helper_cleanup_planes(dev, state);
+ mutex_unlock(&dev->struct_mutex);
break;
+ }
}
-
- if (!ret)
- return 0;
-
- mutex_lock(&dev->struct_mutex);
- drm_atomic_helper_cleanup_planes(dev, state);
}
- mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -13488,7 +13526,7 @@ static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
drm_crtc_vblank_count(crtc),
msecs_to_jiffies(50));
- WARN_ON(!lret);
+ WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
drm_crtc_vblank_put(crtc);
}
@@ -13790,10 +13828,11 @@ intel_prepare_plane_fb(struct drm_plane *plane,
*/
if (needs_modeset(crtc_state))
ret = i915_gem_object_wait_rendering(old_obj, true);
-
- /* Swallow -EIO errors to allow updates during hw lockup. */
- if (ret && ret != -EIO)
+ if (ret) {
+ /* GPU hangs should have been swallowed by the wait */
+ WARN_ON(ret == -EIO);
return ret;
+ }
}
/* For framebuffer backed by dmabuf, wait for fence */
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index b52676a032f9..f192f58708c2 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2215,6 +2215,15 @@ static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
POSTING_READ(DP_A);
udelay(500);
+ /*
+ * [DevILK] Work around required when enabling DP PLL
+ * while a pipe is enabled going to FDI:
+ * 1. Wait for the start of vertical blank on the enabled pipe going to FDI
+ * 2. Program DP PLL enable
+ */
+ if (IS_GEN5(dev_priv))
+ intel_wait_for_vblank_if_active(dev_priv->dev, !crtc->pipe);
+
intel_dp->DP |= DP_PLL_ENABLE;
I915_WRITE(DP_A, intel_dp->DP);
@@ -2630,7 +2639,6 @@ static void intel_enable_dp(struct intel_encoder *encoder)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
- enum port port = dp_to_dig_port(intel_dp)->port;
enum pipe pipe = crtc->pipe;
if (WARN_ON(dp_reg & DP_PORT_EN))
@@ -2641,35 +2649,12 @@ static void intel_enable_dp(struct intel_encoder *encoder)
if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
vlv_init_panel_power_sequencer(intel_dp);
- /*
- * We get an occasional spurious underrun between the port
- * enable and vdd enable, when enabling port A eDP.
- *
- * FIXME: Not sure if this applies to (PCH) port D eDP as well
- */
- if (port == PORT_A)
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
-
intel_dp_enable_port(intel_dp);
- if (port == PORT_A && IS_GEN5(dev_priv)) {
- /*
- * Underrun reporting for the other pipe was disabled in
- * g4x_pre_enable_dp(). The eDP PLL and port have now been
- * enabled, so it's now safe to re-enable underrun reporting.
- */
- intel_wait_for_vblank_if_active(dev_priv->dev, !pipe);
- intel_set_cpu_fifo_underrun_reporting(dev_priv, !pipe, true);
- intel_set_pch_fifo_underrun_reporting(dev_priv, !pipe, true);
- }
-
edp_panel_vdd_on(intel_dp);
edp_panel_on(intel_dp);
edp_panel_vdd_off(intel_dp, true);
- if (port == PORT_A)
- intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
-
pps_unlock(intel_dp);
if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
@@ -2711,26 +2696,11 @@ static void vlv_enable_dp(struct intel_encoder *encoder)
static void g4x_pre_enable_dp(struct intel_encoder *encoder)
{
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
enum port port = dp_to_dig_port(intel_dp)->port;
- enum pipe pipe = to_intel_crtc(encoder->base.crtc)->pipe;
intel_dp_prepare(encoder);
- if (port == PORT_A && IS_GEN5(dev_priv)) {
- /*
- * We get FIFO underruns on the other pipe when
- * enabling the CPU eDP PLL, and when enabling CPU
- * eDP port. We could potentially avoid the PLL
- * underrun with a vblank wait just prior to enabling
- * the PLL, but that doesn't appear to help the port
- * enable case. Just sweep it all under the rug.
- */
- intel_set_cpu_fifo_underrun_reporting(dev_priv, !pipe, false);
- intel_set_pch_fifo_underrun_reporting(dev_priv, !pipe, false);
- }
-
/* Only ilk+ has port A */
if (port == PORT_A)
ironlake_edp_pll_on(intel_dp);
@@ -3773,7 +3743,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
* downstream port information. So, an early return here saves
* time from performing other operations which are not required.
*/
- if (!intel_dp->sink_count)
+ if (!is_edp(intel_dp) && !intel_dp->sink_count)
return false;
/* Check if the panel supports PSR */
@@ -4304,6 +4274,9 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
if (!intel_dp_get_dpcd(intel_dp))
return connector_status_disconnected;
+ if (is_edp(intel_dp))
+ return connector_status_connected;
+
/* if there's no downstream port, we're done */
if (!(dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT))
return connector_status_connected;
@@ -4573,6 +4546,15 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
intel_dp->compliance_test_type = 0;
intel_dp->compliance_test_data = 0;
+ if (intel_dp->is_mst) {
+ DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n",
+ intel_dp->is_mst,
+ intel_dp->mst_mgr.mst_state);
+ intel_dp->is_mst = false;
+ drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+ intel_dp->is_mst);
+ }
+
goto out;
}
@@ -4630,20 +4612,9 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
}
out:
- if (status != connector_status_connected) {
+ if ((status != connector_status_connected) &&
+ (intel_dp->is_mst == false))
intel_dp_unset_edid(intel_dp);
- /*
- * If we were in MST mode, and device is not there,
- * get out of MST mode
- */
- if (intel_dp->is_mst) {
- DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n",
- intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
- intel_dp->is_mst = false;
- drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
- intel_dp->is_mst);
- }
- }
intel_display_power_put(to_i915(dev), power_domain);
return;
@@ -4816,6 +4787,11 @@ intel_dp_set_property(struct drm_connector *connector,
DRM_DEBUG_KMS("no scaling not supported\n");
return -EINVAL;
}
+ if (HAS_GMCH_DISPLAY(dev_priv) &&
+ val == DRM_MODE_SCALE_CENTER) {
+ DRM_DEBUG_KMS("centering not supported\n");
+ return -EINVAL;
+ }
if (intel_connector->panel.fitting_mode == val) {
/* the eDP scaling property is not changed */
@@ -4879,7 +4855,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
kfree(intel_dig_port);
}
-static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
+void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
@@ -4921,7 +4897,7 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
edp_panel_vdd_schedule_off(intel_dp);
}
-static void intel_dp_encoder_reset(struct drm_encoder *encoder)
+void intel_dp_encoder_reset(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp;
diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c
index 0bde6a4259fd..639bf0209c15 100644
--- a/drivers/gpu/drm/i915/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c
@@ -1295,17 +1295,9 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv,
uint32_t temp;
enum port port = (enum port)pll->id; /* 1:1 port->PLL mapping */
- temp = I915_READ(BXT_PORT_PLL_ENABLE(port));
- /*
- * Definition of each bit polarity has been changed
- * after A1 stepping
- */
- if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1))
- temp &= ~PORT_PLL_REF_SEL;
- else
- temp |= PORT_PLL_REF_SEL;
-
/* Non-SSC reference */
+ temp = I915_READ(BXT_PORT_PLL_ENABLE(port));
+ temp |= PORT_PLL_REF_SEL;
I915_WRITE(BXT_PORT_PLL_ENABLE(port), temp);
/* Disable 10 bit clock */
@@ -1652,10 +1644,7 @@ static void intel_ddi_pll_init(struct drm_device *dev)
DRM_DEBUG_KMS("Sanitized cdclk programmed by pre-os\n");
if (!(I915_READ(LCPLL1_CTL) & LCPLL_PLL_ENABLE))
DRM_ERROR("LCPLL1 is disabled\n");
- } else if (IS_BROXTON(dev)) {
- broxton_init_cdclk(dev);
- broxton_ddi_phy_init(dev);
- } else {
+ } else if (!IS_BROXTON(dev_priv)) {
/*
* The LCPLL register should be turned on by the BIOS. For now
* let's just check its state and print errors in case
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index e0fcfa1683cc..b9f1304439e2 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -497,6 +497,11 @@ struct intel_crtc_state {
/* Actual register state of the dpll, for shared dpll cross-checking. */
struct intel_dpll_hw_state dpll_hw_state;
+ /* DSI PLL registers */
+ struct {
+ u32 ctrl, div;
+ } dsi_pll;
+
int pipe_bpp;
struct intel_link_m_n dp_m_n;
@@ -1224,12 +1229,16 @@ void intel_prepare_reset(struct drm_device *dev);
void intel_finish_reset(struct drm_device *dev);
void hsw_enable_pc8(struct drm_i915_private *dev_priv);
void hsw_disable_pc8(struct drm_i915_private *dev_priv);
-void broxton_init_cdclk(struct drm_device *dev);
-void broxton_uninit_cdclk(struct drm_device *dev);
-void broxton_ddi_phy_init(struct drm_device *dev);
-void broxton_ddi_phy_uninit(struct drm_device *dev);
+void broxton_init_cdclk(struct drm_i915_private *dev_priv);
+void broxton_uninit_cdclk(struct drm_i915_private *dev_priv);
+bool broxton_cdclk_verify_state(struct drm_i915_private *dev_priv);
+void broxton_ddi_phy_init(struct drm_i915_private *dev_priv);
+void broxton_ddi_phy_uninit(struct drm_i915_private *dev_priv);
+void broxton_ddi_phy_verify_state(struct drm_i915_private *dev_priv);
+void gen9_sanitize_dc_state(struct drm_i915_private *dev_priv);
void bxt_enable_dc9(struct drm_i915_private *dev_priv);
void bxt_disable_dc9(struct drm_i915_private *dev_priv);
+void gen9_enable_dc5(struct drm_i915_private *dev_priv);
void skl_init_cdclk(struct drm_i915_private *dev_priv);
int skl_sanitize_cdclk(struct drm_i915_private *dev_priv);
void skl_uninit_cdclk(struct drm_i915_private *dev_priv);
@@ -1268,6 +1277,8 @@ u32 skl_plane_ctl_rotation(unsigned int rotation);
void intel_csr_ucode_init(struct drm_i915_private *);
void intel_csr_load_program(struct drm_i915_private *);
void intel_csr_ucode_fini(struct drm_i915_private *);
+void intel_csr_ucode_suspend(struct drm_i915_private *);
+void intel_csr_ucode_resume(struct drm_i915_private *);
/* intel_dp.c */
void intel_dp_init(struct drm_device *dev, i915_reg_t output_reg, enum port port);
@@ -1278,6 +1289,8 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp,
void intel_dp_start_link_train(struct intel_dp *intel_dp);
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_reset(struct drm_encoder *encoder);
+void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder);
void intel_dp_encoder_destroy(struct drm_encoder *encoder);
int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc);
bool intel_dp_compute_config(struct intel_encoder *encoder,
@@ -1462,8 +1475,8 @@ int intel_power_domains_init(struct drm_i915_private *);
void intel_power_domains_fini(struct drm_i915_private *);
void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume);
void intel_power_domains_suspend(struct drm_i915_private *dev_priv);
-void skl_pw1_misc_io_init(struct drm_i915_private *dev_priv);
-void skl_pw1_misc_io_fini(struct drm_i915_private *dev_priv);
+void bxt_display_core_init(struct drm_i915_private *dev_priv, bool resume);
+void bxt_display_core_uninit(struct drm_i915_private *dev_priv);
void intel_runtime_pm_enable(struct drm_i915_private *dev_priv);
const char *
intel_display_power_domain_str(enum intel_display_power_domain domain);
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index a1e0547484ae..2b22bb9bb86f 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -290,16 +290,26 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder,
struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi,
base);
struct intel_connector *intel_connector = intel_dsi->attached_connector;
- struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
+ struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
+ const struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
+ int ret;
DRM_DEBUG_KMS("\n");
pipe_config->has_dsi_encoder = true;
- if (fixed_mode)
+ if (fixed_mode) {
intel_fixed_panel_mode(fixed_mode, adjusted_mode);
+ if (HAS_GMCH_DISPLAY(dev_priv))
+ intel_gmch_panel_fitting(crtc, pipe_config,
+ intel_connector->panel.fitting_mode);
+ else
+ intel_pch_panel_fitting(crtc, pipe_config,
+ intel_connector->panel.fitting_mode);
+ }
+
/* DSI uses short packets for sync events, so clear mode flags for DSI */
adjusted_mode->flags = 0;
@@ -311,6 +321,12 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder,
pipe_config->cpu_transcoder = TRANSCODER_DSI_A;
}
+ ret = intel_compute_dsi_pll(encoder, pipe_config);
+ if (ret)
+ return false;
+
+ pipe_config->clock_set = true;
+
return true;
}
@@ -498,14 +514,19 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
- struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
- enum pipe pipe = intel_crtc->pipe;
+ struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
enum port port;
u32 tmp;
DRM_DEBUG_KMS("\n");
- intel_enable_dsi_pll(encoder);
+ /*
+ * The BIOS may leave the PLL in a wonky state where it doesn't
+ * lock. It needs to be fully powered down to fix it.
+ */
+ intel_disable_dsi_pll(encoder);
+ intel_enable_dsi_pll(encoder, crtc->config);
+
intel_dsi_prepare(encoder);
/* Panel Enable over CRC PMIC */
@@ -515,19 +536,7 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
msleep(intel_dsi->panel_on_delay);
if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
- /*
- * Disable DPOunit clock gating, can stall pipe
- * and we need DPLL REFA always enabled
- */
- tmp = I915_READ(DPLL(pipe));
- tmp |= DPLL_REF_CLK_ENABLE_VLV;
- I915_WRITE(DPLL(pipe), tmp);
-
- /* update the hw state for DPLL */
- intel_crtc->config->dpll_hw_state.dpll =
- DPLL_INTEGRATED_REF_CLK_VLV |
- DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
-
+ /* Disable DPOunit clock gating, can stall pipe */
tmp = I915_READ(DSPCLK_GATE_D);
tmp |= DPOUNIT_CLOCK_GATE_DISABLE;
I915_WRITE(DSPCLK_GATE_D, tmp);
@@ -679,11 +688,16 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder)
drm_panel_unprepare(intel_dsi->panel);
msleep(intel_dsi->panel_off_delay);
- msleep(intel_dsi->panel_pwr_cycle_delay);
/* Panel Disable over CRC PMIC */
if (intel_dsi->gpio_panel)
gpiod_set_value_cansleep(intel_dsi->gpio_panel, 0);
+
+ /*
+ * FIXME As we do with eDP, just make a note of the time here
+ * and perform the wait before the next panel power on.
+ */
+ msleep(intel_dsi->panel_pwr_cycle_delay);
}
static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
@@ -716,11 +730,12 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port);
bool enabled = I915_READ(ctrl_reg) & DPI_ENABLE;
- /* Due to some hardware limitations on BYT, MIPI Port C DPI
- * Enable bit does not get set. To check whether DSI Port C
- * was enabled in BIOS, check the Pipe B enable bit
+ /*
+ * Due to some hardware limitations on VLV/CHV, the DPI enable
+ * bit in port C control register does not get set. As a
+ * workaround, check pipe B conf instead.
*/
- if (IS_VALLEYVIEW(dev) && port == PORT_C)
+ if ((IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) && port == PORT_C)
enabled = I915_READ(PIPECONF(PIPE_B)) & PIPECONF_ENABLE;
/* Try command mode if video mode not enabled */
@@ -826,13 +841,8 @@ static void intel_dsi_get_config(struct intel_encoder *encoder,
if (IS_BROXTON(dev))
bxt_dsi_get_pipe_config(encoder, pipe_config);
- /*
- * DPLL_MD is not used in case of DSI, reading will get some default value
- * set dpll_md = 0
- */
- pipe_config->dpll_hw_state.dpll_md = 0;
-
- pclk = intel_dsi_get_pclk(encoder, pipe_config->pipe_bpp);
+ pclk = intel_dsi_get_pclk(encoder, pipe_config->pipe_bpp,
+ pipe_config);
if (!pclk)
return;
@@ -845,7 +855,7 @@ intel_dsi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_connector *intel_connector = to_intel_connector(connector);
- struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
+ const struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
DRM_DEBUG_KMS("\n");
@@ -1183,6 +1193,48 @@ static int intel_dsi_get_modes(struct drm_connector *connector)
return 1;
}
+static int intel_dsi_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct drm_crtc *crtc;
+ int ret;
+
+ ret = drm_object_property_set_value(&connector->base, property, val);
+ if (ret)
+ return ret;
+
+ if (property == dev->mode_config.scaling_mode_property) {
+ if (val == DRM_MODE_SCALE_NONE) {
+ DRM_DEBUG_KMS("no scaling not supported\n");
+ return -EINVAL;
+ }
+ if (HAS_GMCH_DISPLAY(dev) &&
+ val == DRM_MODE_SCALE_CENTER) {
+ DRM_DEBUG_KMS("centering not supported\n");
+ return -EINVAL;
+ }
+
+ if (intel_connector->panel.fitting_mode == val)
+ return 0;
+
+ intel_connector->panel.fitting_mode = val;
+ }
+
+ crtc = intel_attached_encoder(connector)->base.crtc;
+ if (crtc && crtc->state->enable) {
+ /*
+ * If the CRTC is enabled, the display will be changed
+ * according to the new panel fitting mode.
+ */
+ intel_crtc_restore_mode(crtc);
+ }
+
+ return 0;
+}
+
static void intel_dsi_connector_destroy(struct drm_connector *connector)
{
struct intel_connector *intel_connector = to_intel_connector(connector);
@@ -1225,11 +1277,25 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = {
.detect = intel_dsi_detect,
.destroy = intel_dsi_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = intel_dsi_set_property,
.atomic_get_property = intel_connector_atomic_get_property,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
};
+static void intel_dsi_add_properties(struct intel_connector *connector)
+{
+ struct drm_device *dev = connector->base.dev;
+
+ if (connector->panel.fixed_mode) {
+ drm_mode_create_scaling_mode_property(dev);
+ drm_object_attach_property(&connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_ASPECT);
+ connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
+ }
+}
+
void intel_dsi_init(struct drm_device *dev)
{
struct intel_dsi *intel_dsi;
@@ -1353,8 +1419,6 @@ void intel_dsi_init(struct drm_device *dev)
intel_connector_attach_encoder(intel_connector, intel_encoder);
- drm_connector_register(connector);
-
drm_panel_attach(intel_dsi->panel, connector);
mutex_lock(&dev->mode_config.mutex);
@@ -1373,6 +1437,11 @@ void intel_dsi_init(struct drm_device *dev)
}
intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
+
+ intel_dsi_add_properties(intel_connector);
+
+ drm_connector_register(connector);
+
intel_panel_setup_backlight(connector, INVALID_PIPE);
return;
diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h
index dabde19ee8aa..61a6957fc6c2 100644
--- a/drivers/gpu/drm/i915/intel_dsi.h
+++ b/drivers/gpu/drm/i915/intel_dsi.h
@@ -127,11 +127,15 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
}
bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv);
-extern void intel_enable_dsi_pll(struct intel_encoder *encoder);
-extern void intel_disable_dsi_pll(struct intel_encoder *encoder);
-extern u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp);
-extern void intel_dsi_reset_clocks(struct intel_encoder *encoder,
- enum port port);
+int intel_compute_dsi_pll(struct intel_encoder *encoder,
+ struct intel_crtc_state *config);
+void intel_enable_dsi_pll(struct intel_encoder *encoder,
+ const struct intel_crtc_state *config);
+void intel_disable_dsi_pll(struct intel_encoder *encoder);
+u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp,
+ struct intel_crtc_state *config);
+void intel_dsi_reset_clocks(struct intel_encoder *encoder,
+ enum port port);
struct drm_panel *vbt_panel_init(struct intel_dsi *intel_dsi, u16 panel_id);
enum mipi_dsi_pixel_format pixel_format_from_register_bits(u32 fmt);
diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c b/drivers/gpu/drm/i915/intel_dsi_pll.c
index 4e53fcf6e087..1765e6e18f2c 100644
--- a/drivers/gpu/drm/i915/intel_dsi_pll.c
+++ b/drivers/gpu/drm/i915/intel_dsi_pll.c
@@ -30,12 +30,7 @@
#include "i915_drv.h"
#include "intel_dsi.h"
-struct dsi_mnp {
- u32 dsi_pll_ctrl;
- u32 dsi_pll_div;
-};
-
-static const u32 lfsr_converts[] = {
+static const u16 lfsr_converts[] = {
426, 469, 234, 373, 442, 221, 110, 311, 411, /* 62 - 70 */
461, 486, 243, 377, 188, 350, 175, 343, 427, 213, /* 71 - 80 */
106, 53, 282, 397, 454, 227, 113, 56, 284, 142, /* 81 - 90 */
@@ -57,7 +52,8 @@ static u32 dsi_clk_from_pclk(u32 pclk, enum mipi_dsi_pixel_format fmt,
}
static int dsi_calc_mnp(struct drm_i915_private *dev_priv,
- struct dsi_mnp *dsi_mnp, int target_dsi_clk)
+ struct intel_crtc_state *config,
+ int target_dsi_clk)
{
unsigned int calc_m = 0, calc_p = 0;
unsigned int m_min, m_max, p_min = 2, p_max = 6;
@@ -103,8 +99,8 @@ static int dsi_calc_mnp(struct drm_i915_private *dev_priv,
/* register has log2(N1), this works fine for powers of two */
n = ffs(n) - 1;
m_seed = lfsr_converts[calc_m - 62];
- dsi_mnp->dsi_pll_ctrl = 1 << (DSI_PLL_P1_POST_DIV_SHIFT + calc_p - 2);
- dsi_mnp->dsi_pll_div = n << DSI_PLL_N1_DIV_SHIFT |
+ config->dsi_pll.ctrl = 1 << (DSI_PLL_P1_POST_DIV_SHIFT + calc_p - 2);
+ config->dsi_pll.div = n << DSI_PLL_N1_DIV_SHIFT |
m_seed << DSI_PLL_M1_DIV_SHIFT;
return 0;
@@ -114,54 +110,55 @@ static int dsi_calc_mnp(struct drm_i915_private *dev_priv,
* XXX: The muxing and gating is hard coded for now. Need to add support for
* sharing PLLs with two DSI outputs.
*/
-static void vlv_configure_dsi_pll(struct intel_encoder *encoder)
+static int vlv_compute_dsi_pll(struct intel_encoder *encoder,
+ struct intel_crtc_state *config)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
int ret;
- struct dsi_mnp dsi_mnp;
u32 dsi_clk;
dsi_clk = dsi_clk_from_pclk(intel_dsi->pclk, intel_dsi->pixel_format,
intel_dsi->lane_count);
- ret = dsi_calc_mnp(dev_priv, &dsi_mnp, dsi_clk);
+ ret = dsi_calc_mnp(dev_priv, config, dsi_clk);
if (ret) {
DRM_DEBUG_KMS("dsi_calc_mnp failed\n");
- return;
+ return ret;
}
if (intel_dsi->ports & (1 << PORT_A))
- dsi_mnp.dsi_pll_ctrl |= DSI_PLL_CLK_GATE_DSI0_DSIPLL;
+ config->dsi_pll.ctrl |= DSI_PLL_CLK_GATE_DSI0_DSIPLL;
if (intel_dsi->ports & (1 << PORT_C))
- dsi_mnp.dsi_pll_ctrl |= DSI_PLL_CLK_GATE_DSI1_DSIPLL;
+ config->dsi_pll.ctrl |= DSI_PLL_CLK_GATE_DSI1_DSIPLL;
+
+ config->dsi_pll.ctrl |= DSI_PLL_VCO_EN;
DRM_DEBUG_KMS("dsi pll div %08x, ctrl %08x\n",
- dsi_mnp.dsi_pll_div, dsi_mnp.dsi_pll_ctrl);
+ config->dsi_pll.div, config->dsi_pll.ctrl);
- vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, 0);
- vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_DIVIDER, dsi_mnp.dsi_pll_div);
- vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, dsi_mnp.dsi_pll_ctrl);
+ return 0;
}
-static void vlv_enable_dsi_pll(struct intel_encoder *encoder)
+static void vlv_enable_dsi_pll(struct intel_encoder *encoder,
+ const struct intel_crtc_state *config)
{
- struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
- u32 tmp;
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
DRM_DEBUG_KMS("\n");
mutex_lock(&dev_priv->sb_lock);
- vlv_configure_dsi_pll(encoder);
+ vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, 0);
+ vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_DIVIDER, config->dsi_pll.div);
+ vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL,
+ config->dsi_pll.ctrl & ~DSI_PLL_VCO_EN);
/* wait at least 0.5 us after ungating before enabling VCO */
usleep_range(1, 10);
- tmp = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL);
- tmp |= DSI_PLL_VCO_EN;
- vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, tmp);
+ vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, config->dsi_pll.ctrl);
if (wait_for(vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL) &
DSI_PLL_LOCK, 20)) {
@@ -177,7 +174,7 @@ static void vlv_enable_dsi_pll(struct intel_encoder *encoder)
static void vlv_disable_dsi_pll(struct intel_encoder *encoder)
{
- struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
u32 tmp;
DRM_DEBUG_KMS("\n");
@@ -224,7 +221,7 @@ static bool bxt_dsi_pll_is_enabled(struct drm_i915_private *dev_priv)
static void bxt_disable_dsi_pll(struct intel_encoder *encoder)
{
- struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
u32 val;
DRM_DEBUG_KMS("\n");
@@ -251,14 +248,15 @@ static void assert_bpp_mismatch(enum mipi_dsi_pixel_format fmt, int pipe_bpp)
bpp, pipe_bpp);
}
-static u32 vlv_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
+static u32 vlv_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp,
+ struct intel_crtc_state *config)
{
- struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
u32 dsi_clock, pclk;
u32 pll_ctl, pll_div;
u32 m = 0, p = 0, n;
- int refclk = 25000;
+ int refclk = IS_CHERRYVIEW(dev_priv) ? 100000 : 25000;
int i;
DRM_DEBUG_KMS("\n");
@@ -268,6 +266,9 @@ static u32 vlv_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
pll_div = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_DIVIDER);
mutex_unlock(&dev_priv->sb_lock);
+ config->dsi_pll.ctrl = pll_ctl & ~DSI_PLL_LOCK;
+ config->dsi_pll.div = pll_div;
+
/* mask out other bits and extract the P1 divisor */
pll_ctl &= DSI_PLL_P1_POST_DIV_MASK;
pll_ctl = pll_ctl >> (DSI_PLL_P1_POST_DIV_SHIFT - 2);
@@ -313,7 +314,8 @@ static u32 vlv_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
return pclk;
}
-static u32 bxt_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
+static u32 bxt_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp,
+ struct intel_crtc_state *config)
{
u32 pclk;
u32 dsi_clk;
@@ -327,15 +329,9 @@ static u32 bxt_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
return 0;
}
- dsi_ratio = I915_READ(BXT_DSI_PLL_CTL) &
- BXT_DSI_PLL_RATIO_MASK;
+ config->dsi_pll.ctrl = I915_READ(BXT_DSI_PLL_CTL);
- /* Invalid DSI ratio ? */
- if (dsi_ratio < BXT_DSI_PLL_RATIO_MIN ||
- dsi_ratio > BXT_DSI_PLL_RATIO_MAX) {
- DRM_ERROR("Invalid DSI pll ratio(%u) programmed\n", dsi_ratio);
- return 0;
- }
+ dsi_ratio = config->dsi_pll.ctrl & BXT_DSI_PLL_RATIO_MASK;
dsi_clk = (dsi_ratio * BXT_REF_CLOCK_KHZ) / 2;
@@ -348,12 +344,13 @@ static u32 bxt_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
return pclk;
}
-u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp)
+u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp,
+ struct intel_crtc_state *config)
{
if (IS_BROXTON(encoder->base.dev))
- return bxt_dsi_get_pclk(encoder, pipe_bpp);
+ return bxt_dsi_get_pclk(encoder, pipe_bpp, config);
else
- return vlv_dsi_get_pclk(encoder, pipe_bpp);
+ return vlv_dsi_get_pclk(encoder, pipe_bpp, config);
}
static void vlv_dsi_reset_clocks(struct intel_encoder *encoder, enum port port)
@@ -370,7 +367,8 @@ static void vlv_dsi_reset_clocks(struct intel_encoder *encoder, enum port port)
}
/* Program BXT Mipi clocks and dividers */
-static void bxt_dsi_program_clocks(struct drm_device *dev, enum port port)
+static void bxt_dsi_program_clocks(struct drm_device *dev, enum port port,
+ const struct intel_crtc_state *config)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp;
@@ -390,8 +388,7 @@ static void bxt_dsi_program_clocks(struct drm_device *dev, enum port port)
tmp &= ~(BXT_MIPI_RX_ESCLK_LOWER_FIXDIV_MASK(port));
/* Get the current DSI rate(actual) */
- pll_ratio = I915_READ(BXT_DSI_PLL_CTL) &
- BXT_DSI_PLL_RATIO_MASK;
+ pll_ratio = config->dsi_pll.ctrl & BXT_DSI_PLL_RATIO_MASK;
dsi_rate = (BXT_REF_CLOCK_KHZ * pll_ratio) / 2;
/*
@@ -427,16 +424,15 @@ static void bxt_dsi_program_clocks(struct drm_device *dev, enum port port)
I915_WRITE(BXT_MIPI_CLOCK_CTL, tmp);
}
-static bool bxt_configure_dsi_pll(struct intel_encoder *encoder)
+static int bxt_compute_dsi_pll(struct intel_encoder *encoder,
+ struct intel_crtc_state *config)
{
- struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
u8 dsi_ratio;
u32 dsi_clk;
- u32 val;
dsi_clk = dsi_clk_from_pclk(intel_dsi->pclk, intel_dsi->pixel_format,
- intel_dsi->lane_count);
+ intel_dsi->lane_count);
/*
* From clock diagram, to get PLL ratio divider, divide double of DSI
@@ -445,9 +441,9 @@ static bool bxt_configure_dsi_pll(struct intel_encoder *encoder)
*/
dsi_ratio = DIV_ROUND_UP(dsi_clk * 2, BXT_REF_CLOCK_KHZ);
if (dsi_ratio < BXT_DSI_PLL_RATIO_MIN ||
- dsi_ratio > BXT_DSI_PLL_RATIO_MAX) {
+ dsi_ratio > BXT_DSI_PLL_RATIO_MAX) {
DRM_ERROR("Cant get a suitable ratio from DSI PLL ratios\n");
- return false;
+ return -ECHRNG;
}
/*
@@ -455,27 +451,19 @@ static bool bxt_configure_dsi_pll(struct intel_encoder *encoder)
* Spec says both have to be programmed, even if one is not getting
* used. Configure MIPI_CLOCK_CTL dividers in modeset
*/
- val = I915_READ(BXT_DSI_PLL_CTL);
- val &= ~BXT_DSI_PLL_PVD_RATIO_MASK;
- val &= ~BXT_DSI_FREQ_SEL_MASK;
- val &= ~BXT_DSI_PLL_RATIO_MASK;
- val |= (dsi_ratio | BXT_DSIA_16X_BY2 | BXT_DSIC_16X_BY2);
+ config->dsi_pll.ctrl = dsi_ratio | BXT_DSIA_16X_BY2 | BXT_DSIC_16X_BY2;
/* As per recommendation from hardware team,
* Prog PVD ratio =1 if dsi ratio <= 50
*/
- if (dsi_ratio <= 50) {
- val &= ~BXT_DSI_PLL_PVD_RATIO_MASK;
- val |= BXT_DSI_PLL_PVD_RATIO_1;
- }
+ if (dsi_ratio <= 50)
+ config->dsi_pll.ctrl |= BXT_DSI_PLL_PVD_RATIO_1;
- I915_WRITE(BXT_DSI_PLL_CTL, val);
- POSTING_READ(BXT_DSI_PLL_CTL);
-
- return true;
+ return 0;
}
-static void bxt_enable_dsi_pll(struct intel_encoder *encoder)
+static void bxt_enable_dsi_pll(struct intel_encoder *encoder,
+ const struct intel_crtc_state *config)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
@@ -484,23 +472,13 @@ static void bxt_enable_dsi_pll(struct intel_encoder *encoder)
DRM_DEBUG_KMS("\n");
- val = I915_READ(BXT_DSI_PLL_ENABLE);
-
- if (val & BXT_DSI_PLL_DO_ENABLE) {
- WARN(1, "DSI PLL already enabled. Disabling it.\n");
- val &= ~BXT_DSI_PLL_DO_ENABLE;
- I915_WRITE(BXT_DSI_PLL_ENABLE, val);
- }
-
/* Configure PLL vales */
- if (!bxt_configure_dsi_pll(encoder)) {
- DRM_ERROR("Configure DSI PLL failed, abort PLL enable\n");
- return;
- }
+ I915_WRITE(BXT_DSI_PLL_CTL, config->dsi_pll.ctrl);
+ POSTING_READ(BXT_DSI_PLL_CTL);
/* Program TX, RX, Dphy clocks */
for_each_dsi_port(port, intel_dsi->ports)
- bxt_dsi_program_clocks(encoder->base.dev, port);
+ bxt_dsi_program_clocks(encoder->base.dev, port, config);
/* Enable DSI PLL */
val = I915_READ(BXT_DSI_PLL_ENABLE);
@@ -526,14 +504,28 @@ bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv)
return false;
}
-void intel_enable_dsi_pll(struct intel_encoder *encoder)
+int intel_compute_dsi_pll(struct intel_encoder *encoder,
+ struct intel_crtc_state *config)
+{
+ struct drm_device *dev = encoder->base.dev;
+
+ if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
+ return vlv_compute_dsi_pll(encoder, config);
+ else if (IS_BROXTON(dev))
+ return bxt_compute_dsi_pll(encoder, config);
+
+ return -ENODEV;
+}
+
+void intel_enable_dsi_pll(struct intel_encoder *encoder,
+ const struct intel_crtc_state *config)
{
struct drm_device *dev = encoder->base.dev;
if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
- vlv_enable_dsi_pll(encoder);
+ vlv_enable_dsi_pll(encoder, config);
else if (IS_BROXTON(dev))
- bxt_enable_dsi_pll(encoder);
+ bxt_enable_dsi_pll(encoder, config);
}
void intel_disable_dsi_pll(struct intel_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 73002e901ff2..9d79c4c3e256 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -27,8 +27,34 @@
#include "intel_guc_fwif.h"
#include "i915_guc_reg.h"
+struct drm_i915_gem_request;
+
+/*
+ * This structure primarily describes the GEM object shared with the GuC.
+ * The GEM object is held for the entire lifetime of our interaction with
+ * the GuC, being allocated before the GuC is loaded with its firmware.
+ * Because there's no way to update the address used by the GuC after
+ * initialisation, the shared object must stay pinned into the GGTT as
+ * long as the GuC is in use. We also keep the first page (only) mapped
+ * into kernel address space, as it includes shared data that must be
+ * updated on every request submission.
+ *
+ * The single GEM object described here is actually made up of several
+ * separate areas, as far as the GuC is concerned. The first page (kept
+ * kmap'd) includes the "process decriptor" which holds sequence data for
+ * the doorbell, and one cacheline which actually *is* the doorbell; a
+ * write to this will "ring the doorbell" (i.e. send an interrupt to the
+ * GuC). The subsequent pages of the client object constitute the work
+ * queue (a circular array of work items), again described in the process
+ * descriptor. Work queue pages are mapped momentarily as required.
+ *
+ * Finally, we also keep a few statistics here, including the number of
+ * submissions to each engine, and a record of the last submission failure
+ * (if any).
+ */
struct i915_guc_client {
struct drm_i915_gem_object *client_obj;
+ void *client_base; /* first page (only) of above */
struct intel_context *owner;
struct intel_guc *guc;
uint32_t priority;
@@ -43,13 +69,14 @@ struct i915_guc_client {
uint32_t wq_offset;
uint32_t wq_size;
uint32_t wq_tail;
- uint32_t wq_head;
+ uint32_t unused; /* Was 'wq_head' */
/* GuC submission statistics & status */
uint64_t submissions[GUC_MAX_ENGINES_NUM];
uint32_t q_fail;
uint32_t b_fail;
int retcode;
+ int spare; /* pad to 32 DWords */
};
enum intel_guc_fw_status {
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index b199ede08f72..2cdab73046f8 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1412,8 +1412,16 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
hdmi_to_dig_port(intel_hdmi));
}
- if (!live_status)
- DRM_DEBUG_KMS("Live status not up!");
+ if (!live_status) {
+ DRM_DEBUG_KMS("HDMI live status down\n");
+ /*
+ * Live status register is not reliable on all intel platforms.
+ * So consider live_status only for certain platforms, for
+ * others, read EDID to determine presence of sink.
+ */
+ if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv))
+ live_status = true;
+ }
intel_hdmi_unset_edid(connector);
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 6dbe73ecb41a..81de23098be7 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -571,15 +571,14 @@ clear_err:
goto out;
timeout:
- DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin %d\n",
- bus->adapter.name, bus->reg0 & 0xff);
+ DRM_DEBUG_KMS("GMBUS [%s] timed out, falling back to bit banging on pin %d\n",
+ bus->adapter.name, bus->reg0 & 0xff);
I915_WRITE(GMBUS0, 0);
/*
* Hardware may not support GMBUS over these pins? Try GPIO bitbanging
* instead. Use EAGAIN to have i2c core retry.
*/
- bus->force_bit = 1;
ret = -EAGAIN;
out:
@@ -597,10 +596,15 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
mutex_lock(&dev_priv->gmbus_mutex);
- if (bus->force_bit)
+ if (bus->force_bit) {
ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
- else
+ if (ret < 0)
+ bus->force_bit &= ~GMBUS_FORCE_BIT_RETRY;
+ } else {
ret = do_gmbus_xfer(adapter, msgs, num);
+ if (ret == -EAGAIN)
+ bus->force_bit |= GMBUS_FORCE_BIT_RETRY;
+ }
mutex_unlock(&dev_priv->gmbus_mutex);
intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
@@ -718,11 +722,16 @@ void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+
+ mutex_lock(&dev_priv->gmbus_mutex);
bus->force_bit += force_bit ? 1 : -1;
DRM_DEBUG_KMS("%sabling bit-banging on %s. force bit now %d\n",
force_bit ? "en" : "dis", adapter->name,
bus->force_bit);
+
+ mutex_unlock(&dev_priv->gmbus_mutex);
}
void intel_teardown_gmbus(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 0d6dc5ec4a46..6179b591ee84 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -229,9 +229,6 @@ enum {
static int intel_lr_context_pin(struct intel_context *ctx,
struct intel_engine_cs *engine);
-static void lrc_setup_hardware_status_page(struct intel_engine_cs *engine,
- struct drm_i915_gem_object *default_ctx_obj);
-
/**
* intel_sanitize_enable_execlists() - sanitize i915.enable_execlists
@@ -418,6 +415,7 @@ static void execlists_submit_requests(struct drm_i915_gem_request *rq0,
struct drm_i915_gem_request *rq1)
{
struct drm_i915_private *dev_priv = rq0->i915;
+ unsigned int fw_domains = rq0->engine->fw_domains;
execlists_update_context(rq0);
@@ -425,11 +423,11 @@ static void execlists_submit_requests(struct drm_i915_gem_request *rq0,
execlists_update_context(rq1);
spin_lock_irq(&dev_priv->uncore.lock);
- intel_uncore_forcewake_get__locked(dev_priv, FORCEWAKE_ALL);
+ intel_uncore_forcewake_get__locked(dev_priv, fw_domains);
execlists_elsp_write(rq0, rq1);
- intel_uncore_forcewake_put__locked(dev_priv, FORCEWAKE_ALL);
+ intel_uncore_forcewake_put__locked(dev_priv, fw_domains);
spin_unlock_irq(&dev_priv->uncore.lock);
}
@@ -552,7 +550,7 @@ static void intel_lrc_irq_handler(unsigned long data)
unsigned int csb_read = 0, i;
unsigned int submit_contexts = 0;
- intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+ intel_uncore_forcewake_get(dev_priv, engine->fw_domains);
status_pointer = I915_READ_FW(RING_CONTEXT_STATUS_PTR(engine));
@@ -577,7 +575,7 @@ static void intel_lrc_irq_handler(unsigned long data)
_MASKED_FIELD(GEN8_CSB_READ_PTR_MASK,
engine->next_context_status_buffer << 8));
- intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+ intel_uncore_forcewake_put(dev_priv, engine->fw_domains);
spin_lock(&engine->execlist_lock);
@@ -892,17 +890,8 @@ static int logical_ring_prepare(struct drm_i915_gem_request *req, int bytes)
*/
int intel_logical_ring_begin(struct drm_i915_gem_request *req, int num_dwords)
{
- struct drm_i915_private *dev_priv;
int ret;
- WARN_ON(req == NULL);
- dev_priv = req->i915;
-
- ret = i915_gem_check_wedge(&dev_priv->gpu_error,
- dev_priv->mm.interruptible);
- if (ret)
- return ret;
-
ret = logical_ring_prepare(req, num_dwords * sizeof(uint32_t));
if (ret)
return ret;
@@ -1016,7 +1005,6 @@ int intel_execlists_submission(struct i915_execbuffer_params *params,
trace_i915_gem_ring_dispatch(params->request, params->dispatch_flags);
i915_gem_execbuffer_move_to_active(vmas, params->request);
- i915_gem_execbuffer_retire_commands(params);
return 0;
}
@@ -1057,7 +1045,7 @@ void intel_logical_ring_stop(struct intel_engine_cs *engine)
return;
ret = intel_engine_idle(engine);
- if (ret && !i915_reset_in_progress(&to_i915(engine->dev)->gpu_error))
+ if (ret)
DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
engine->name, ret);
@@ -1093,8 +1081,8 @@ static int intel_lr_context_do_pin(struct intel_context *ctx,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *ctx_obj = ctx->engine[engine->id].state;
struct intel_ringbuffer *ringbuf = ctx->engine[engine->id].ringbuf;
- struct page *lrc_state_page;
- uint32_t *lrc_reg_state;
+ void *vaddr;
+ u32 *lrc_reg_state;
int ret;
WARN_ON(!mutex_is_locked(&engine->dev->struct_mutex));
@@ -1104,19 +1092,20 @@ static int intel_lr_context_do_pin(struct intel_context *ctx,
if (ret)
return ret;
- lrc_state_page = i915_gem_object_get_dirty_page(ctx_obj, LRC_STATE_PN);
- if (WARN_ON(!lrc_state_page)) {
- ret = -ENODEV;
+ vaddr = i915_gem_object_pin_map(ctx_obj);
+ if (IS_ERR(vaddr)) {
+ ret = PTR_ERR(vaddr);
goto unpin_ctx_obj;
}
+ lrc_reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
+
ret = intel_pin_and_map_ringbuffer_obj(engine->dev, ringbuf);
if (ret)
- goto unpin_ctx_obj;
+ goto unpin_map;
ctx->engine[engine->id].lrc_vma = i915_gem_obj_to_ggtt(ctx_obj);
intel_lr_context_descriptor_update(ctx, engine);
- lrc_reg_state = kmap(lrc_state_page);
lrc_reg_state[CTX_RING_BUFFER_START+1] = ringbuf->vma->node.start;
ctx->engine[engine->id].lrc_reg_state = lrc_reg_state;
ctx_obj->dirty = true;
@@ -1127,6 +1116,8 @@ static int intel_lr_context_do_pin(struct intel_context *ctx,
return ret;
+unpin_map:
+ i915_gem_object_unpin_map(ctx_obj);
unpin_ctx_obj:
i915_gem_object_ggtt_unpin(ctx_obj);
@@ -1159,7 +1150,7 @@ void intel_lr_context_unpin(struct intel_context *ctx,
WARN_ON(!mutex_is_locked(&ctx->i915->dev->struct_mutex));
if (--ctx->engine[engine->id].pin_count == 0) {
- kunmap(kmap_to_page(ctx->engine[engine->id].lrc_reg_state));
+ i915_gem_object_unpin_map(ctx_obj);
intel_unpin_ringbuffer_obj(ctx->engine[engine->id].ringbuf);
i915_gem_object_ggtt_unpin(ctx_obj);
ctx->engine[engine->id].lrc_vma = NULL;
@@ -1579,14 +1570,22 @@ out:
return ret;
}
+static void lrc_init_hws(struct intel_engine_cs *engine)
+{
+ struct drm_i915_private *dev_priv = engine->dev->dev_private;
+
+ I915_WRITE(RING_HWS_PGA(engine->mmio_base),
+ (u32)engine->status_page.gfx_addr);
+ POSTING_READ(RING_HWS_PGA(engine->mmio_base));
+}
+
static int gen8_init_common_ring(struct intel_engine_cs *engine)
{
struct drm_device *dev = engine->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned int next_context_status_buffer_hw;
- lrc_setup_hardware_status_page(engine,
- dev_priv->kernel_context->engine[engine->id].state);
+ lrc_init_hws(engine);
I915_WRITE_IMR(engine,
~(engine->irq_enable_mask | engine->irq_keep_mask));
@@ -1625,7 +1624,7 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine)
intel_engine_init_hangcheck(engine);
- return 0;
+ return intel_mocs_init_engine(engine);
}
static int gen8_init_render_ring(struct intel_engine_cs *engine)
@@ -1945,15 +1944,18 @@ static int gen8_emit_request_render(struct drm_i915_gem_request *request)
struct intel_ringbuffer *ringbuf = request->ringbuf;
int ret;
- ret = intel_logical_ring_begin(request, 6 + WA_TAIL_DWORDS);
+ ret = intel_logical_ring_begin(request, 8 + WA_TAIL_DWORDS);
if (ret)
return ret;
+ /* We're using qword write, seqno should be aligned to 8 bytes. */
+ BUILD_BUG_ON(I915_GEM_HWS_INDEX & 1);
+
/* w/a for post sync ops following a GPGPU operation we
* need a prior CS_STALL, which is emitted by the flush
* following the batch.
*/
- intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(5));
+ intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
intel_logical_ring_emit(ringbuf,
(PIPE_CONTROL_GLOBAL_GTT_IVB |
PIPE_CONTROL_CS_STALL |
@@ -1961,7 +1963,10 @@ static int gen8_emit_request_render(struct drm_i915_gem_request *request)
intel_logical_ring_emit(ringbuf, hws_seqno_address(request->engine));
intel_logical_ring_emit(ringbuf, 0);
intel_logical_ring_emit(ringbuf, i915_gem_request_get_seqno(request));
+ /* We're thrashing one dword of HWS. */
+ intel_logical_ring_emit(ringbuf, 0);
intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT);
+ intel_logical_ring_emit(ringbuf, MI_NOOP);
return intel_logical_ring_advance_and_submit(request);
}
@@ -2048,7 +2053,7 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine)
i915_gem_batch_pool_fini(&engine->batch_pool);
if (engine->status_page.obj) {
- kunmap(sg_page(engine->status_page.obj->pages->sgl));
+ i915_gem_object_unpin_map(engine->status_page.obj);
engine->status_page.obj = NULL;
}
@@ -2087,9 +2092,29 @@ logical_ring_default_irqs(struct intel_engine_cs *engine, unsigned shift)
}
static int
+lrc_setup_hws(struct intel_engine_cs *engine,
+ struct drm_i915_gem_object *dctx_obj)
+{
+ void *hws;
+
+ /* The HWSP is part of the default context object in LRC mode. */
+ engine->status_page.gfx_addr = i915_gem_obj_ggtt_offset(dctx_obj) +
+ LRC_PPHWSP_PN * PAGE_SIZE;
+ hws = i915_gem_object_pin_map(dctx_obj);
+ if (IS_ERR(hws))
+ return PTR_ERR(hws);
+ engine->status_page.page_addr = hws + LRC_PPHWSP_PN * PAGE_SIZE;
+ engine->status_page.obj = dctx_obj;
+
+ return 0;
+}
+
+static int
logical_ring_init(struct drm_device *dev, struct intel_engine_cs *engine)
{
- struct intel_context *dctx = to_i915(dev)->kernel_context;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct intel_context *dctx = dev_priv->kernel_context;
+ enum forcewake_domains fw_domains;
int ret;
/* Intentionally left blank. */
@@ -2111,6 +2136,20 @@ logical_ring_init(struct drm_device *dev, struct intel_engine_cs *engine)
logical_ring_init_platform_invariants(engine);
+ fw_domains = intel_uncore_forcewake_for_reg(dev_priv,
+ RING_ELSP(engine),
+ FW_REG_WRITE);
+
+ fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
+ RING_CONTEXT_STATUS_PTR(engine),
+ FW_REG_READ | FW_REG_WRITE);
+
+ fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
+ RING_CONTEXT_STATUS_BUF_BASE(engine),
+ FW_REG_READ);
+
+ engine->fw_domains = fw_domains;
+
ret = i915_cmd_parser_init_ring(engine);
if (ret)
goto error;
@@ -2128,6 +2167,13 @@ logical_ring_init(struct drm_device *dev, struct intel_engine_cs *engine)
goto error;
}
+ /* And setup the hardware status page. */
+ ret = lrc_setup_hws(engine, dctx->engine[engine->id].state);
+ if (ret) {
+ DRM_ERROR("Failed to set up hws %s: %d\n", engine->name, ret);
+ goto error;
+ }
+
return 0;
error:
@@ -2378,15 +2424,16 @@ static u32 intel_lr_indirect_ctx_offset(struct intel_engine_cs *engine)
}
static int
-populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_obj,
+populate_lr_context(struct intel_context *ctx,
+ struct drm_i915_gem_object *ctx_obj,
struct intel_engine_cs *engine,
struct intel_ringbuffer *ringbuf)
{
struct drm_device *dev = engine->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
- struct page *page;
- uint32_t *reg_state;
+ void *vaddr;
+ u32 *reg_state;
int ret;
if (!ppgtt)
@@ -2398,18 +2445,17 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o
return ret;
}
- ret = i915_gem_object_get_pages(ctx_obj);
- if (ret) {
- DRM_DEBUG_DRIVER("Could not get object pages\n");
+ vaddr = i915_gem_object_pin_map(ctx_obj);
+ if (IS_ERR(vaddr)) {
+ ret = PTR_ERR(vaddr);
+ DRM_DEBUG_DRIVER("Could not map object pages! (%d)\n", ret);
return ret;
}
-
- i915_gem_object_pin_pages(ctx_obj);
+ ctx_obj->dirty = true;
/* The second page of the context object contains some fields which must
* be set up prior to the first execution. */
- page = i915_gem_object_get_dirty_page(ctx_obj, LRC_STATE_PN);
- reg_state = kmap_atomic(page);
+ reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
/* A context is actually a big batch buffer with several MI_LOAD_REGISTER_IMM
* commands followed by (reg, value) pairs. The values we are setting here are
@@ -2514,8 +2560,7 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o
make_rpcs(dev));
}
- kunmap_atomic(reg_state);
- i915_gem_object_unpin_pages(ctx_obj);
+ i915_gem_object_unpin_map(ctx_obj);
return 0;
}
@@ -2542,6 +2587,7 @@ void intel_lr_context_free(struct intel_context *ctx)
if (ctx == ctx->i915->kernel_context) {
intel_unpin_ringbuffer_obj(ringbuf);
i915_gem_object_ggtt_unpin(ctx_obj);
+ i915_gem_object_unpin_map(ctx_obj);
}
WARN_ON(ctx->engine[i].pin_count);
@@ -2588,24 +2634,6 @@ uint32_t intel_lr_context_size(struct intel_engine_cs *engine)
return ret;
}
-static void lrc_setup_hardware_status_page(struct intel_engine_cs *engine,
- struct drm_i915_gem_object *default_ctx_obj)
-{
- struct drm_i915_private *dev_priv = engine->dev->dev_private;
- struct page *page;
-
- /* The HWSP is part of the default context object in LRC mode. */
- engine->status_page.gfx_addr = i915_gem_obj_ggtt_offset(default_ctx_obj)
- + LRC_PPHWSP_PN * PAGE_SIZE;
- page = i915_gem_object_get_page(default_ctx_obj, LRC_PPHWSP_PN);
- engine->status_page.page_addr = kmap(page);
- engine->status_page.obj = default_ctx_obj;
-
- I915_WRITE(RING_HWS_PGA(engine->mmio_base),
- (u32)engine->status_page.gfx_addr);
- POSTING_READ(RING_HWS_PGA(engine->mmio_base));
-}
-
/**
* intel_lr_context_deferred_alloc() - create the LRC specific bits of a context
* @ctx: LR context to create.
@@ -2669,13 +2697,12 @@ int intel_lr_context_deferred_alloc(struct intel_context *ctx,
}
ret = engine->init_context(req);
+ i915_add_request_no_flush(req);
if (ret) {
DRM_ERROR("ring init context: %d\n",
ret);
- i915_gem_request_cancel(req);
goto error_ringbuf;
}
- i915_add_request_no_flush(req);
}
return 0;
@@ -2688,10 +2715,9 @@ error_deref_obj:
return ret;
}
-void intel_lr_context_reset(struct drm_device *dev,
- struct intel_context *ctx)
+void intel_lr_context_reset(struct drm_i915_private *dev_priv,
+ struct intel_context *ctx)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *engine;
for_each_engine(engine, dev_priv) {
@@ -2699,23 +2725,23 @@ void intel_lr_context_reset(struct drm_device *dev,
ctx->engine[engine->id].state;
struct intel_ringbuffer *ringbuf =
ctx->engine[engine->id].ringbuf;
+ void *vaddr;
uint32_t *reg_state;
- struct page *page;
if (!ctx_obj)
continue;
- if (i915_gem_object_get_pages(ctx_obj)) {
- WARN(1, "Failed get_pages for context obj\n");
+ vaddr = i915_gem_object_pin_map(ctx_obj);
+ if (WARN_ON(IS_ERR(vaddr)))
continue;
- }
- page = i915_gem_object_get_dirty_page(ctx_obj, LRC_STATE_PN);
- reg_state = kmap_atomic(page);
+
+ reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
+ ctx_obj->dirty = true;
reg_state[CTX_RING_HEAD+1] = 0;
reg_state[CTX_RING_TAIL+1] = 0;
- kunmap_atomic(reg_state);
+ i915_gem_object_unpin_map(ctx_obj);
ringbuf->head = 0;
ringbuf->tail = 0;
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
index 0b0853eee91e..461f1ef9b5c1 100644
--- a/drivers/gpu/drm/i915/intel_lrc.h
+++ b/drivers/gpu/drm/i915/intel_lrc.h
@@ -24,6 +24,8 @@
#ifndef _INTEL_LRC_H_
#define _INTEL_LRC_H_
+#include "intel_ringbuffer.h"
+
#define GEN8_LR_CONTEXT_ALIGN 4096
/* Execlists regs */
@@ -34,6 +36,7 @@
#define CTX_CTRL_INHIBIT_SYN_CTX_SWITCH (1 << 3)
#define CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT (1 << 0)
#define CTX_CTRL_RS_CTX_ENABLE (1 << 1)
+#define RING_CONTEXT_STATUS_BUF_BASE(ring) _MMIO((ring)->mmio_base + 0x370)
#define RING_CONTEXT_STATUS_BUF_LO(ring, i) _MMIO((ring)->mmio_base + 0x370 + (i) * 8)
#define RING_CONTEXT_STATUS_BUF_HI(ring, i) _MMIO((ring)->mmio_base + 0x370 + (i) * 8 + 4)
#define RING_CONTEXT_STATUS_PTR(ring) _MMIO((ring)->mmio_base + 0x3a0)
@@ -103,8 +106,11 @@ int intel_lr_context_deferred_alloc(struct intel_context *ctx,
struct intel_engine_cs *engine);
void intel_lr_context_unpin(struct intel_context *ctx,
struct intel_engine_cs *engine);
-void intel_lr_context_reset(struct drm_device *dev,
- struct intel_context *ctx);
+
+struct drm_i915_private;
+
+void intel_lr_context_reset(struct drm_i915_private *dev_priv,
+ struct intel_context *ctx);
uint64_t intel_lr_context_descriptor(struct intel_context *ctx,
struct intel_engine_cs *engine);
diff --git a/drivers/gpu/drm/i915/intel_mocs.c b/drivers/gpu/drm/i915/intel_mocs.c
index 7c7ac0aa192a..23b8545ad6b0 100644
--- a/drivers/gpu/drm/i915/intel_mocs.c
+++ b/drivers/gpu/drm/i915/intel_mocs.c
@@ -128,9 +128,9 @@ static const struct drm_i915_mocs_entry broxton_mocs_table[] = {
/**
* get_mocs_settings()
- * @dev: DRM device.
+ * @dev_priv: i915 device.
* @table: Output table that will be made to point at appropriate
- * MOCS values for the device.
+ * MOCS values for the device.
*
* This function will return the values of the MOCS table that needs to
* be programmed for the platform. It will return the values that need
@@ -138,21 +138,21 @@ static const struct drm_i915_mocs_entry broxton_mocs_table[] = {
*
* Return: true if there are applicable MOCS settings for the device.
*/
-static bool get_mocs_settings(struct drm_device *dev,
+static bool get_mocs_settings(struct drm_i915_private *dev_priv,
struct drm_i915_mocs_table *table)
{
bool result = false;
- if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
+ if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
table->size = ARRAY_SIZE(skylake_mocs_table);
table->table = skylake_mocs_table;
result = true;
- } else if (IS_BROXTON(dev)) {
+ } else if (IS_BROXTON(dev_priv)) {
table->size = ARRAY_SIZE(broxton_mocs_table);
table->table = broxton_mocs_table;
result = true;
} else {
- WARN_ONCE(INTEL_INFO(dev)->gen >= 9,
+ WARN_ONCE(INTEL_INFO(dev_priv)->gen >= 9,
"Platform that should have a MOCS table does not.\n");
}
@@ -179,10 +179,49 @@ static i915_reg_t mocs_register(enum intel_engine_id ring, int index)
}
/**
+ * intel_mocs_init_engine() - emit the mocs control table
+ * @engine: The engine for whom to emit the registers.
+ *
+ * This function simply emits a MI_LOAD_REGISTER_IMM command for the
+ * given table starting at the given address.
+ *
+ * Return: 0 on success, otherwise the error status.
+ */
+int intel_mocs_init_engine(struct intel_engine_cs *engine)
+{
+ struct drm_i915_private *dev_priv = to_i915(engine->dev);
+ struct drm_i915_mocs_table table;
+ unsigned int index;
+
+ if (!get_mocs_settings(dev_priv, &table))
+ return 0;
+
+ if (WARN_ON(table.size > GEN9_NUM_MOCS_ENTRIES))
+ return -ENODEV;
+
+ for (index = 0; index < table.size; index++)
+ I915_WRITE(mocs_register(engine->id, index),
+ table.table[index].control_value);
+
+ /*
+ * Ok, now set the unused entries to uncached. These entries
+ * are officially undefined and no contract for the contents
+ * and settings is given for these entries.
+ *
+ * Entry 0 in the table is uncached - so we are just writing
+ * that value to all the used entries.
+ */
+ for (; index < GEN9_NUM_MOCS_ENTRIES; index++)
+ I915_WRITE(mocs_register(engine->id, index),
+ table.table[0].control_value);
+
+ return 0;
+}
+
+/**
* emit_mocs_control_table() - emit the mocs control table
* @req: Request to set up the MOCS table for.
* @table: The values to program into the control regs.
- * @ring: The engine for whom to emit the registers.
*
* This function simply emits a MI_LOAD_REGISTER_IMM command for the
* given table starting at the given address.
@@ -190,10 +229,10 @@ static i915_reg_t mocs_register(enum intel_engine_id ring, int index)
* Return: 0 on success, otherwise the error status.
*/
static int emit_mocs_control_table(struct drm_i915_gem_request *req,
- const struct drm_i915_mocs_table *table,
- enum intel_engine_id ring)
+ const struct drm_i915_mocs_table *table)
{
struct intel_ringbuffer *ringbuf = req->ringbuf;
+ enum intel_engine_id engine = req->engine->id;
unsigned int index;
int ret;
@@ -210,7 +249,8 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req,
MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES));
for (index = 0; index < table->size; index++) {
- intel_logical_ring_emit_reg(ringbuf, mocs_register(ring, index));
+ intel_logical_ring_emit_reg(ringbuf,
+ mocs_register(engine, index));
intel_logical_ring_emit(ringbuf,
table->table[index].control_value);
}
@@ -224,8 +264,10 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req,
* that value to all the used entries.
*/
for (; index < GEN9_NUM_MOCS_ENTRIES; index++) {
- intel_logical_ring_emit_reg(ringbuf, mocs_register(ring, index));
- intel_logical_ring_emit(ringbuf, table->table[0].control_value);
+ intel_logical_ring_emit_reg(ringbuf,
+ mocs_register(engine, index));
+ intel_logical_ring_emit(ringbuf,
+ table->table[0].control_value);
}
intel_logical_ring_emit(ringbuf, MI_NOOP);
@@ -234,6 +276,14 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req,
return 0;
}
+static inline u32 l3cc_combine(const struct drm_i915_mocs_table *table,
+ u16 low,
+ u16 high)
+{
+ return table->table[low].l3cc_value |
+ table->table[high].l3cc_value << 16;
+}
+
/**
* emit_mocs_l3cc_table() - emit the mocs control table
* @req: Request to set up the MOCS table for.
@@ -249,11 +299,7 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
const struct drm_i915_mocs_table *table)
{
struct intel_ringbuffer *ringbuf = req->ringbuf;
- unsigned int count;
unsigned int i;
- u32 value;
- u32 filler = (table->table[0].l3cc_value & 0xffff) |
- ((table->table[0].l3cc_value & 0xffff) << 16);
int ret;
if (WARN_ON(table->size > GEN9_NUM_MOCS_ENTRIES))
@@ -268,20 +314,18 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
intel_logical_ring_emit(ringbuf,
MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES / 2));
- for (i = 0, count = 0; i < table->size / 2; i++, count += 2) {
- value = (table->table[count].l3cc_value & 0xffff) |
- ((table->table[count + 1].l3cc_value & 0xffff) << 16);
-
+ for (i = 0; i < table->size/2; i++) {
intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i));
- intel_logical_ring_emit(ringbuf, value);
+ intel_logical_ring_emit(ringbuf,
+ l3cc_combine(table, 2*i, 2*i+1));
}
if (table->size & 0x01) {
/* Odd table size - 1 left over */
- value = (table->table[count].l3cc_value & 0xffff) |
- ((table->table[0].l3cc_value & 0xffff) << 16);
- } else
- value = filler;
+ intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i));
+ intel_logical_ring_emit(ringbuf, l3cc_combine(table, 2*i, 0));
+ i++;
+ }
/*
* Now set the rest of the table to uncached - use entry 0 as
@@ -290,9 +334,7 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
*/
for (; i < GEN9_NUM_MOCS_ENTRIES / 2; i++) {
intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i));
- intel_logical_ring_emit(ringbuf, value);
-
- value = filler;
+ intel_logical_ring_emit(ringbuf, l3cc_combine(table, 0, 0));
}
intel_logical_ring_emit(ringbuf, MI_NOOP);
@@ -302,6 +344,47 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
}
/**
+ * intel_mocs_init_l3cc_table() - program the mocs control table
+ * @dev: The the device to be programmed.
+ *
+ * This function simply programs the mocs registers for the given table
+ * starting at the given address. This register set is programmed in pairs.
+ *
+ * These registers may get programmed more than once, it is simpler to
+ * re-program 32 registers than maintain the state of when they were programmed.
+ * We are always reprogramming with the same values and this only on context
+ * start.
+ *
+ * Return: Nothing.
+ */
+void intel_mocs_init_l3cc_table(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct drm_i915_mocs_table table;
+ unsigned int i;
+
+ if (!get_mocs_settings(dev_priv, &table))
+ return;
+
+ for (i = 0; i < table.size/2; i++)
+ I915_WRITE(GEN9_LNCFCMOCS(i), l3cc_combine(&table, 2*i, 2*i+1));
+
+ /* Odd table size - 1 left over */
+ if (table.size & 0x01) {
+ I915_WRITE(GEN9_LNCFCMOCS(i), l3cc_combine(&table, 2*i, 0));
+ i++;
+ }
+
+ /*
+ * Now set the rest of the table to uncached - use entry 0 as
+ * this will be uncached. Leave the last pair as initialised as
+ * they are reserved by the hardware.
+ */
+ for (; i < (GEN9_NUM_MOCS_ENTRIES / 2); i++)
+ I915_WRITE(GEN9_LNCFCMOCS(i), l3cc_combine(&table, 0, 0));
+}
+
+/**
* intel_rcs_context_init_mocs() - program the MOCS register.
* @req: Request to set up the MOCS tables for.
*
@@ -322,17 +405,11 @@ int intel_rcs_context_init_mocs(struct drm_i915_gem_request *req)
struct drm_i915_mocs_table t;
int ret;
- if (get_mocs_settings(req->engine->dev, &t)) {
- struct drm_i915_private *dev_priv = req->i915;
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
-
- /* Program the control registers */
- for_each_engine_id(engine, dev_priv, id) {
- ret = emit_mocs_control_table(req, &t, id);
- if (ret)
- return ret;
- }
+ if (get_mocs_settings(req->i915, &t)) {
+ /* Program the RCS control registers */
+ ret = emit_mocs_control_table(req, &t);
+ if (ret)
+ return ret;
/* Now program the l3cc registers */
ret = emit_mocs_l3cc_table(req, &t);
diff --git a/drivers/gpu/drm/i915/intel_mocs.h b/drivers/gpu/drm/i915/intel_mocs.h
index 76e45b1748b3..4640299e04ec 100644
--- a/drivers/gpu/drm/i915/intel_mocs.h
+++ b/drivers/gpu/drm/i915/intel_mocs.h
@@ -53,5 +53,7 @@
#include "i915_drv.h"
int intel_rcs_context_init_mocs(struct drm_i915_gem_request *req);
+void intel_mocs_init_l3cc_table(struct drm_device *dev);
+int intel_mocs_init_engine(struct intel_engine_cs *ring);
#endif
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index c15718b4862a..99e26034ae8d 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -34,12 +34,6 @@
#include "i915_drv.h"
#include "intel_drv.h"
-#define PCI_ASLE 0xe4
-#define PCI_ASLS 0xfc
-#define PCI_SWSCI 0xe8
-#define PCI_SWSCI_SCISEL (1 << 15)
-#define PCI_SWSCI_GSSCIE (1 << 0)
-
#define OPREGION_HEADER_OFFSET 0
#define OPREGION_ACPI_OFFSET 0x100
#define ACPI_CLID 0x01ac /* current lid state indicator */
@@ -246,13 +240,12 @@ struct opregion_asle_ext {
#define MAX_DSLP 1500
-#ifdef CONFIG_ACPI
static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct opregion_swsci *swsci = dev_priv->opregion.swsci;
u32 main_function, sub_function, scic;
- u16 pci_swsci;
+ u16 swsci_val;
u32 dslp;
if (!swsci)
@@ -300,16 +293,16 @@ static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out)
swsci->scic = scic;
/* Ensure SCI event is selected and event trigger is cleared. */
- pci_read_config_word(dev->pdev, PCI_SWSCI, &pci_swsci);
- if (!(pci_swsci & PCI_SWSCI_SCISEL) || (pci_swsci & PCI_SWSCI_GSSCIE)) {
- pci_swsci |= PCI_SWSCI_SCISEL;
- pci_swsci &= ~PCI_SWSCI_GSSCIE;
- pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
+ pci_read_config_word(dev->pdev, SWSCI, &swsci_val);
+ if (!(swsci_val & SWSCI_SCISEL) || (swsci_val & SWSCI_GSSCIE)) {
+ swsci_val |= SWSCI_SCISEL;
+ swsci_val &= ~SWSCI_GSSCIE;
+ pci_write_config_word(dev->pdev, SWSCI, swsci_val);
}
/* Use event trigger to tell bios to check the mail. */
- pci_swsci |= PCI_SWSCI_GSSCIE;
- pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
+ swsci_val |= SWSCI_GSSCIE;
+ pci_write_config_word(dev->pdev, SWSCI, swsci_val);
/* Poll for the result. */
#define C (((scic = swsci->scic) & SWSCI_SCIC_INDICATOR) == 0)
@@ -905,9 +898,6 @@ static void swsci_setup(struct drm_device *dev)
opregion->swsci_gbda_sub_functions,
opregion->swsci_sbcb_sub_functions);
}
-#else /* CONFIG_ACPI */
-static inline void swsci_setup(struct drm_device *dev) {}
-#endif /* CONFIG_ACPI */
static int intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
{
@@ -943,16 +933,14 @@ int intel_opregion_setup(struct drm_device *dev)
BUILD_BUG_ON(sizeof(struct opregion_asle) != 0x100);
BUILD_BUG_ON(sizeof(struct opregion_asle_ext) != 0x400);
- pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
+ pci_read_config_dword(dev->pdev, ASLS, &asls);
DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
if (asls == 0) {
DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
return -ENOTSUPP;
}
-#ifdef CONFIG_ACPI
INIT_WORK(&opregion->asle_work, asle_work);
-#endif
base = memremap(asls, OPREGION_SIZE, MEMREMAP_WB);
if (!base)
@@ -1024,3 +1012,31 @@ err_out:
memunmap(base);
return err;
}
+
+int
+intel_opregion_get_panel_type(struct drm_device *dev)
+{
+ u32 panel_details;
+ int ret;
+
+ ret = swsci(dev, SWSCI_GBDA_PANEL_DETAILS, 0x0, &panel_details);
+ if (ret) {
+ DRM_DEBUG_KMS("Failed to get panel details from OpRegion (%d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = (panel_details >> 8) & 0xff;
+ if (ret > 0x10) {
+ DRM_DEBUG_KMS("Invalid OpRegion panel type 0x%x\n", ret);
+ return -EINVAL;
+ }
+
+ /* fall back to VBT panel type? */
+ if (ret == 0x0) {
+ DRM_DEBUG_KMS("No panel type in OpRegion\n");
+ return -ENODEV;
+ }
+
+ return ret - 1;
+}
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index 6694e9230cd5..bcc3b6a016d8 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -247,7 +247,7 @@ static int intel_overlay_on(struct intel_overlay *overlay)
ret = intel_ring_begin(req, 4);
if (ret) {
- i915_gem_request_cancel(req);
+ i915_add_request_no_flush(req);
return ret;
}
@@ -290,7 +290,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
ret = intel_ring_begin(req, 2);
if (ret) {
- i915_gem_request_cancel(req);
+ i915_add_request_no_flush(req);
return ret;
}
@@ -356,7 +356,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
ret = intel_ring_begin(req, 6);
if (ret) {
- i915_gem_request_cancel(req);
+ i915_add_request_no_flush(req);
return ret;
}
@@ -431,7 +431,7 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
ret = intel_ring_begin(req, 2);
if (ret) {
- i915_gem_request_cancel(req);
+ i915_add_request_no_flush(req);
return ret;
}
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 8c8996fcbaf5..a0788763757b 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -504,7 +504,7 @@ static u32 i9xx_get_backlight(struct intel_connector *connector)
if (panel->backlight.combination_mode) {
u8 lbpc;
- pci_read_config_byte(dev_priv->dev->pdev, PCI_LBPC, &lbpc);
+ pci_read_config_byte(dev_priv->dev->pdev, LBPC, &lbpc);
val *= lbpc;
}
@@ -592,7 +592,7 @@ static void i9xx_set_backlight(struct intel_connector *connector, u32 level)
lbpc = level * 0xfe / panel->backlight.max + 1;
level /= lbpc;
- pci_write_config_byte(dev_priv->dev->pdev, PCI_LBPC, lbpc);
+ pci_write_config_byte(dev_priv->dev->pdev, LBPC, lbpc);
}
if (IS_GEN4(dev_priv)) {
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 43b24a1f5ee6..695a464a5e64 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2483,7 +2483,7 @@ static void ilk_wm_merge(struct drm_device *dev,
/* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */
if ((INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) &&
config->num_pipes_active > 1)
- return;
+ last_enabled_level = 0;
/* ILK: FBC WM must be disabled always */
merged->fbc_wm_enabled = INTEL_INFO(dev)->gen >= 6;
@@ -4587,7 +4587,7 @@ void intel_set_rps(struct drm_device *dev, u8 val)
gen6_set_rps(dev, val);
}
-static void gen9_disable_rps(struct drm_device *dev)
+static void gen9_disable_rc6(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4595,12 +4595,20 @@ static void gen9_disable_rps(struct drm_device *dev)
I915_WRITE(GEN9_PG_ENABLE, 0);
}
+static void gen9_disable_rps(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(GEN6_RP_CONTROL, 0);
+}
+
static void gen6_disable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(GEN6_RC_CONTROL, 0);
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
+ I915_WRITE(GEN6_RP_CONTROL, 0);
}
static void cherryview_disable_rps(struct drm_device *dev)
@@ -4804,6 +4812,16 @@ static void gen9_enable_rps(struct drm_device *dev)
/* WaGsvDisableTurbo: Workaround to disable turbo on BXT A* */
if (IS_BXT_REVID(dev, 0, BXT_REVID_A1)) {
+ /*
+ * BIOS could leave the Hw Turbo enabled, so need to explicitly
+ * clear out the Control register just to avoid inconsitency
+ * with debugfs interface, which will show Turbo as enabled
+ * only and that is not expected by the User after adding the
+ * WaGsvDisableTurbo. Apart from this there is no problem even
+ * if the Turbo is left enabled in the Control register, as the
+ * Up/Down interrupts would remain masked.
+ */
+ gen9_disable_rps(dev);
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
return;
}
@@ -4997,7 +5015,8 @@ static void gen6_enable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RC_STATE, 0);
/* Clear the DBG now so we don't confuse earlier errors */
- if ((gtfifodbg = I915_READ(GTFIFODBG))) {
+ gtfifodbg = I915_READ(GTFIFODBG);
+ if (gtfifodbg) {
DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg);
I915_WRITE(GTFIFODBG, gtfifodbg);
}
@@ -5528,7 +5547,8 @@ static void cherryview_enable_rps(struct drm_device *dev)
WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
- gtfifodbg = I915_READ(GTFIFODBG);
+ gtfifodbg = I915_READ(GTFIFODBG) & ~(GT_FIFO_SBDEDICATE_FREE_ENTRY_CHV |
+ GT_FIFO_FREE_ENTRIES_CHV);
if (gtfifodbg) {
DRM_DEBUG_DRIVER("GT fifo had a previous error %x\n",
gtfifodbg);
@@ -5627,7 +5647,8 @@ static void valleyview_enable_rps(struct drm_device *dev)
valleyview_check_pctx(dev_priv);
- if ((gtfifodbg = I915_READ(GTFIFODBG))) {
+ gtfifodbg = I915_READ(GTFIFODBG);
+ if (gtfifodbg) {
DRM_DEBUG_DRIVER("GT fifo had a previous error %x\n",
gtfifodbg);
I915_WRITE(GTFIFODBG, gtfifodbg);
@@ -6265,9 +6286,10 @@ void intel_disable_gt_powersave(struct drm_device *dev)
intel_suspend_gt_powersave(dev);
mutex_lock(&dev_priv->rps.hw_lock);
- if (INTEL_INFO(dev)->gen >= 9)
+ if (INTEL_INFO(dev)->gen >= 9) {
+ gen9_disable_rc6(dev);
gen9_disable_rps(dev);
- else if (IS_CHERRYVIEW(dev))
+ } else if (IS_CHERRYVIEW(dev))
cherryview_disable_rps(dev);
else if (IS_VALLEYVIEW(dev))
valleyview_disable_rps(dev);
@@ -6882,23 +6904,10 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
gen6_check_mch_setup(dev);
}
-static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv)
-{
- I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
-
- /*
- * Disable trickle feed and enable pnd deadline calculation
- */
- I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
- I915_WRITE(CBR1_VLV, 0);
-}
-
static void valleyview_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- vlv_init_display_clock_gating(dev_priv);
-
/* WaDisableEarlyCull:vlv */
I915_WRITE(_3D_CHICKEN3,
_MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL));
@@ -6981,8 +6990,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- vlv_init_display_clock_gating(dev_priv);
-
/* WaVSRefCountFullforceMissDisable:chv */
/* WaDSRefCountFullforceMissDisable:chv */
I915_WRITE(GEN7_FF_THREAD_MODE,
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 41b604e69db7..245386e20c52 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -959,9 +959,10 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
}
/* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt */
- if (IS_SKL_REVID(dev, SKL_REVID_C0, REVID_FOREVER) || IS_BROXTON(dev))
- WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
- GEN9_ENABLE_YV12_BUGFIX);
+ /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt */
+ WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
+ GEN9_ENABLE_YV12_BUGFIX |
+ GEN9_ENABLE_GPGPU_PREEMPTION);
/* Wa4x4STCOptimizationDisable:skl,bxt */
/* WaDisablePartialResolveInVc:skl,bxt */
@@ -980,7 +981,7 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
/* WaForceContextSaveRestoreNonCoherent:skl,bxt */
tmp = HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT;
- if (IS_SKL_REVID(dev, SKL_REVID_F0, SKL_REVID_F0) ||
+ if (IS_SKL_REVID(dev, SKL_REVID_F0, REVID_FOREVER) ||
IS_BXT_REVID(dev, BXT_REVID_B0, REVID_FOREVER))
tmp |= HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE;
WA_SET_BIT_MASKED(HDC_CHICKEN0, tmp);
@@ -1097,7 +1098,8 @@ static int skl_init_workarounds(struct intel_engine_cs *engine)
WA_SET_BIT_MASKED(HIZ_CHICKEN,
BDW_HIZ_POWER_COMPILER_CLOCK_GATING_DISABLE);
- if (IS_SKL_REVID(dev, 0, SKL_REVID_F0)) {
+ /* This is tied to WaForceContextSaveRestoreNonCoherent */
+ if (IS_SKL_REVID(dev, 0, REVID_FOREVER)) {
/*
*Use Force Non-Coherent whenever executing a 3D context. This
* is a workaround for a possible hang in the unlikely event
@@ -2086,6 +2088,7 @@ void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf)
i915_gem_object_unpin_map(ringbuf->obj);
else
iounmap(ringbuf->virtual_start);
+ ringbuf->virtual_start = NULL;
ringbuf->vma = NULL;
i915_gem_object_ggtt_unpin(ringbuf->obj);
}
@@ -2096,10 +2099,13 @@ int intel_pin_and_map_ringbuffer_obj(struct drm_device *dev,
struct drm_i915_private *dev_priv = to_i915(dev);
struct i915_ggtt *ggtt = &dev_priv->ggtt;
struct drm_i915_gem_object *obj = ringbuf->obj;
+ /* Ring wraparound at offset 0 sometimes hangs. No idea why. */
+ unsigned flags = PIN_OFFSET_BIAS | 4096;
+ void *addr;
int ret;
if (HAS_LLC(dev_priv) && !obj->stolen) {
- ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, 0);
+ ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, flags);
if (ret)
return ret;
@@ -2107,13 +2113,14 @@ int intel_pin_and_map_ringbuffer_obj(struct drm_device *dev,
if (ret)
goto err_unpin;
- ringbuf->virtual_start = i915_gem_object_pin_map(obj);
- if (ringbuf->virtual_start == NULL) {
- ret = -ENOMEM;
+ addr = i915_gem_object_pin_map(obj);
+ if (IS_ERR(addr)) {
+ ret = PTR_ERR(addr);
goto err_unpin;
}
} else {
- ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, PIN_MAPPABLE);
+ ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE,
+ flags | PIN_MAPPABLE);
if (ret)
return ret;
@@ -2124,14 +2131,15 @@ int intel_pin_and_map_ringbuffer_obj(struct drm_device *dev,
/* Access through the GTT requires the device to be awake. */
assert_rpm_wakelock_held(dev_priv);
- ringbuf->virtual_start = ioremap_wc(ggtt->mappable_base +
- i915_gem_obj_ggtt_offset(obj), ringbuf->size);
- if (ringbuf->virtual_start == NULL) {
+ addr = ioremap_wc(ggtt->mappable_base +
+ i915_gem_obj_ggtt_offset(obj), ringbuf->size);
+ if (addr == NULL) {
ret = -ENOMEM;
goto err_unpin;
}
}
+ ringbuf->virtual_start = addr;
ringbuf->vma = i915_gem_obj_to_ggtt(obj);
return 0;
@@ -2363,8 +2371,7 @@ int intel_engine_idle(struct intel_engine_cs *engine)
/* Make sure we do not trigger any retires */
return __i915_wait_request(req,
- atomic_read(&to_i915(engine->dev)->gpu_error.reset_counter),
- to_i915(engine->dev)->mm.interruptible,
+ req->i915->mm.interruptible,
NULL, NULL);
}
@@ -2486,19 +2493,9 @@ static int __intel_ring_prepare(struct intel_engine_cs *engine, int bytes)
int intel_ring_begin(struct drm_i915_gem_request *req,
int num_dwords)
{
- struct intel_engine_cs *engine;
- struct drm_i915_private *dev_priv;
+ struct intel_engine_cs *engine = req->engine;
int ret;
- WARN_ON(req == NULL);
- engine = req->engine;
- dev_priv = req->i915;
-
- ret = i915_gem_check_wedge(&dev_priv->gpu_error,
- dev_priv->mm.interruptible);
- if (ret)
- return ret;
-
ret = __intel_ring_prepare(engine, num_dwords * sizeof(uint32_t));
if (ret)
return ret;
@@ -3189,7 +3186,7 @@ intel_stop_engine(struct intel_engine_cs *engine)
return;
ret = intel_engine_idle(engine);
- if (ret && !i915_reset_in_progress(&to_i915(engine->dev)->gpu_error))
+ if (ret)
DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
engine->name, ret);
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 78dc46864a10..2ade194bbea9 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -270,6 +270,7 @@ struct intel_engine_cs {
spinlock_t execlist_lock; /* used inside tasklet, use spin_lock_bh */
struct list_head execlist_queue;
struct list_head execlist_retired_req_list;
+ unsigned int fw_domains;
unsigned int next_context_status_buffer;
unsigned int idle_lite_restore_wa;
bool disable_lite_restore_wa;
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index 80e8bd4b43b5..7fb1da4e7fc3 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -397,11 +397,6 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,
BIT(POWER_DOMAIN_MODESET) | \
BIT(POWER_DOMAIN_AUX_A) | \
BIT(POWER_DOMAIN_INIT))
-#define SKL_DISPLAY_ALWAYS_ON_POWER_DOMAINS ( \
- (POWER_DOMAIN_MASK & ~( \
- SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS | \
- SKL_DISPLAY_DC_OFF_POWER_DOMAINS)) | \
- BIT(POWER_DOMAIN_INIT))
#define BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS ( \
BIT(POWER_DOMAIN_TRANSCODER_A) | \
@@ -419,39 +414,21 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,
BIT(POWER_DOMAIN_VGA) | \
BIT(POWER_DOMAIN_GMBUS) | \
BIT(POWER_DOMAIN_INIT))
-#define BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS ( \
- BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS | \
- BIT(POWER_DOMAIN_PIPE_A) | \
- BIT(POWER_DOMAIN_TRANSCODER_EDP) | \
- BIT(POWER_DOMAIN_TRANSCODER_DSI_A) | \
- BIT(POWER_DOMAIN_TRANSCODER_DSI_C) | \
- BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \
- BIT(POWER_DOMAIN_PORT_DDI_A_LANES) | \
- BIT(POWER_DOMAIN_PORT_DSI) | \
- BIT(POWER_DOMAIN_AUX_A) | \
- BIT(POWER_DOMAIN_PLLS) | \
- BIT(POWER_DOMAIN_INIT))
#define BXT_DISPLAY_DC_OFF_POWER_DOMAINS ( \
BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS | \
BIT(POWER_DOMAIN_MODESET) | \
BIT(POWER_DOMAIN_AUX_A) | \
BIT(POWER_DOMAIN_INIT))
-#define BXT_DISPLAY_ALWAYS_ON_POWER_DOMAINS ( \
- (POWER_DOMAIN_MASK & ~(BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS | \
- BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS)) | \
- BIT(POWER_DOMAIN_INIT))
static void assert_can_enable_dc9(struct drm_i915_private *dev_priv)
{
- struct drm_device *dev = dev_priv->dev;
-
- WARN(!IS_BROXTON(dev), "Platform doesn't support DC9.\n");
- WARN((I915_READ(DC_STATE_EN) & DC_STATE_EN_DC9),
- "DC9 already programmed to be enabled.\n");
- WARN(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5,
- "DC5 still not disabled to enable DC9.\n");
- WARN(I915_READ(HSW_PWR_WELL_DRIVER), "Power well on.\n");
- WARN(intel_irqs_enabled(dev_priv), "Interrupts not disabled yet.\n");
+ WARN_ONCE((I915_READ(DC_STATE_EN) & DC_STATE_EN_DC9),
+ "DC9 already programmed to be enabled.\n");
+ WARN_ONCE(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5,
+ "DC5 still not disabled to enable DC9.\n");
+ WARN_ONCE(I915_READ(HSW_PWR_WELL_DRIVER), "Power well on.\n");
+ WARN_ONCE(intel_irqs_enabled(dev_priv),
+ "Interrupts not disabled yet.\n");
/*
* TODO: check for the following to verify the conditions to enter DC9
@@ -464,9 +441,10 @@ static void assert_can_enable_dc9(struct drm_i915_private *dev_priv)
static void assert_can_disable_dc9(struct drm_i915_private *dev_priv)
{
- WARN(intel_irqs_enabled(dev_priv), "Interrupts not disabled yet.\n");
- WARN(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5,
- "DC5 still not disabled.\n");
+ WARN_ONCE(intel_irqs_enabled(dev_priv),
+ "Interrupts not disabled yet.\n");
+ WARN_ONCE(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5,
+ "DC5 still not disabled.\n");
/*
* TODO: check for the following to verify DC9 state was indeed
@@ -514,10 +492,9 @@ static void gen9_write_dc_state(struct drm_i915_private *dev_priv,
state, rewrites);
}
-static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)
+static u32 gen9_dc_mask(struct drm_i915_private *dev_priv)
{
- uint32_t val;
- uint32_t mask;
+ u32 mask;
mask = DC_STATE_EN_UPTO_DC5;
if (IS_BROXTON(dev_priv))
@@ -525,10 +502,30 @@ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)
else
mask |= DC_STATE_EN_UPTO_DC6;
+ return mask;
+}
+
+void gen9_sanitize_dc_state(struct drm_i915_private *dev_priv)
+{
+ u32 val;
+
+ val = I915_READ(DC_STATE_EN) & gen9_dc_mask(dev_priv);
+
+ DRM_DEBUG_KMS("Resetting DC state tracking from %02x to %02x\n",
+ dev_priv->csr.dc_state, val);
+ dev_priv->csr.dc_state = val;
+}
+
+static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)
+{
+ uint32_t val;
+ uint32_t mask;
+
if (WARN_ON_ONCE(state & ~dev_priv->csr.allowed_dc_mask))
state &= dev_priv->csr.allowed_dc_mask;
val = I915_READ(DC_STATE_EN);
+ mask = gen9_dc_mask(dev_priv);
DRM_DEBUG_KMS("Setting DC state from %02x to %02x\n",
val & mask, state);
@@ -573,13 +570,9 @@ static void assert_csr_loaded(struct drm_i915_private *dev_priv)
static void assert_can_enable_dc5(struct drm_i915_private *dev_priv)
{
- struct drm_device *dev = dev_priv->dev;
bool pg2_enabled = intel_display_power_well_is_enabled(dev_priv,
SKL_DISP_PW_2);
- WARN_ONCE(!IS_SKYLAKE(dev) && !IS_KABYLAKE(dev),
- "Platform doesn't support DC5.\n");
- WARN_ONCE(!HAS_RUNTIME_PM(dev), "Runtime PM not enabled.\n");
WARN_ONCE(pg2_enabled, "PG2 not disabled to enable DC5.\n");
WARN_ONCE((I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5),
@@ -589,7 +582,7 @@ static void assert_can_enable_dc5(struct drm_i915_private *dev_priv)
assert_csr_loaded(dev_priv);
}
-static void gen9_enable_dc5(struct drm_i915_private *dev_priv)
+void gen9_enable_dc5(struct drm_i915_private *dev_priv)
{
assert_can_enable_dc5(dev_priv);
@@ -600,11 +593,6 @@ static void gen9_enable_dc5(struct drm_i915_private *dev_priv)
static void assert_can_enable_dc6(struct drm_i915_private *dev_priv)
{
- struct drm_device *dev = dev_priv->dev;
-
- WARN_ONCE(!IS_SKYLAKE(dev) && !IS_KABYLAKE(dev),
- "Platform doesn't support DC6.\n");
- WARN_ONCE(!HAS_RUNTIME_PM(dev), "Runtime PM not enabled.\n");
WARN_ONCE(I915_READ(UTIL_PIN_CTL) & UTIL_PIN_ENABLE,
"Backlight is not disabled.\n");
WARN_ONCE((I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC6),
@@ -630,6 +618,45 @@ void skl_disable_dc6(struct drm_i915_private *dev_priv)
gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
}
+static void
+gen9_sanitize_power_well_requests(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ enum skl_disp_power_wells power_well_id = power_well->data;
+ u32 val;
+ u32 mask;
+
+ mask = SKL_POWER_WELL_REQ(power_well_id);
+
+ val = I915_READ(HSW_PWR_WELL_KVMR);
+ if (WARN_ONCE(val & mask, "Clearing unexpected KVMR request for %s\n",
+ power_well->name))
+ I915_WRITE(HSW_PWR_WELL_KVMR, val & ~mask);
+
+ val = I915_READ(HSW_PWR_WELL_BIOS);
+ val |= I915_READ(HSW_PWR_WELL_DEBUG);
+
+ if (!(val & mask))
+ return;
+
+ /*
+ * DMC is known to force on the request bits for power well 1 on SKL
+ * and BXT and the misc IO power well on SKL but we don't expect any
+ * other request bits to be set, so WARN for those.
+ */
+ if (power_well_id == SKL_DISP_PW_1 ||
+ ((IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) &&
+ power_well_id == SKL_DISP_PW_MISC_IO))
+ DRM_DEBUG_DRIVER("Clearing auxiliary requests for %s forced on "
+ "by DMC\n", power_well->name);
+ else
+ WARN_ONCE(1, "Clearing unexpected auxiliary requests for %s\n",
+ power_well->name);
+
+ I915_WRITE(HSW_PWR_WELL_BIOS, val & ~mask);
+ I915_WRITE(HSW_PWR_WELL_DEBUG, val & ~mask);
+}
+
static void skl_set_power_well(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well, bool enable)
{
@@ -684,10 +711,6 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv,
if (!is_enabled) {
DRM_DEBUG_KMS("Enabling %s\n", power_well->name);
- if (wait_for((I915_READ(HSW_PWR_WELL_DRIVER) &
- state_mask), 1))
- DRM_ERROR("%s enable timeout\n",
- power_well->name);
check_fuse_status = true;
}
} else {
@@ -696,8 +719,16 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv,
POSTING_READ(HSW_PWR_WELL_DRIVER);
DRM_DEBUG_KMS("Disabling %s\n", power_well->name);
}
+
+ if (IS_GEN9(dev_priv))
+ gen9_sanitize_power_well_requests(dev_priv, power_well);
}
+ if (wait_for(!!(I915_READ(HSW_PWR_WELL_DRIVER) & state_mask) == enable,
+ 1))
+ DRM_ERROR("%s %s timeout\n",
+ power_well->name, enable ? "enable" : "disable");
+
if (check_fuse_status) {
if (power_well->data == SKL_DISP_PW_1) {
if (wait_for((I915_READ(SKL_FUSE_STATUS) &
@@ -779,11 +810,19 @@ static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well)
{
gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+ if (IS_BROXTON(dev_priv)) {
+ broxton_cdclk_verify_state(dev_priv);
+ broxton_ddi_phy_verify_state(dev_priv);
+ }
}
static void gen9_dc_off_power_well_disable(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well)
{
+ if (!dev_priv->csr.dmc_payload)
+ return;
+
if (dev_priv->csr.allowed_dc_mask & DC_STATE_EN_UPTO_DC6)
skl_enable_dc6(dev_priv);
else if (dev_priv->csr.allowed_dc_mask & DC_STATE_EN_UPTO_DC5)
@@ -900,6 +939,17 @@ static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv,
return enabled;
}
+static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv)
+{
+ I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
+
+ /*
+ * Disable trickle feed and enable pnd deadline calculation
+ */
+ I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
+ I915_WRITE(CBR1_VLV, 0);
+}
+
static void vlv_display_power_well_init(struct drm_i915_private *dev_priv)
{
enum pipe pipe;
@@ -922,6 +972,8 @@ static void vlv_display_power_well_init(struct drm_i915_private *dev_priv)
I915_WRITE(DPLL(pipe), val);
}
+ vlv_init_display_clock_gating(dev_priv);
+
spin_lock_irq(&dev_priv->irq_lock);
valleyview_enable_display_irqs(dev_priv);
spin_unlock_irq(&dev_priv->irq_lock);
@@ -1560,34 +1612,56 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,
intel_runtime_pm_put(dev_priv);
}
-#define HSW_ALWAYS_ON_POWER_DOMAINS ( \
- BIT(POWER_DOMAIN_PIPE_A) | \
- BIT(POWER_DOMAIN_TRANSCODER_EDP) | \
- BIT(POWER_DOMAIN_PORT_DDI_A_LANES) | \
+#define HSW_DISPLAY_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_B) | \
+ BIT(POWER_DOMAIN_PIPE_C) | \
+ BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_TRANSCODER_A) | \
+ BIT(POWER_DOMAIN_TRANSCODER_B) | \
+ BIT(POWER_DOMAIN_TRANSCODER_C) | \
BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \
BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \
BIT(POWER_DOMAIN_PORT_DDI_D_LANES) | \
- BIT(POWER_DOMAIN_PORT_CRT) | \
- BIT(POWER_DOMAIN_PLLS) | \
- BIT(POWER_DOMAIN_AUX_A) | \
- BIT(POWER_DOMAIN_AUX_B) | \
- BIT(POWER_DOMAIN_AUX_C) | \
- BIT(POWER_DOMAIN_AUX_D) | \
- BIT(POWER_DOMAIN_GMBUS) | \
- BIT(POWER_DOMAIN_INIT))
-#define HSW_DISPLAY_POWER_DOMAINS ( \
- (POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS) | \
+ BIT(POWER_DOMAIN_PORT_CRT) | /* DDI E */ \
+ BIT(POWER_DOMAIN_VGA) | \
+ BIT(POWER_DOMAIN_AUDIO) | \
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) | \
+#define BDW_DISPLAY_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_B) | \
+ BIT(POWER_DOMAIN_PIPE_C) | \
+ BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_TRANSCODER_A) | \
+ BIT(POWER_DOMAIN_TRANSCODER_B) | \
+ BIT(POWER_DOMAIN_TRANSCODER_C) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_D_LANES) | \
+ BIT(POWER_DOMAIN_PORT_CRT) | /* DDI E */ \
+ BIT(POWER_DOMAIN_VGA) | \
+ BIT(POWER_DOMAIN_AUDIO) | \
BIT(POWER_DOMAIN_INIT))
-#define VLV_ALWAYS_ON_POWER_DOMAINS BIT(POWER_DOMAIN_INIT)
-#define VLV_DISPLAY_POWER_DOMAINS POWER_DOMAIN_MASK
+#define VLV_DISPLAY_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_A) | \
+ BIT(POWER_DOMAIN_PIPE_B) | \
+ BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_TRANSCODER_A) | \
+ BIT(POWER_DOMAIN_TRANSCODER_B) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DSI) | \
+ BIT(POWER_DOMAIN_PORT_CRT) | \
+ BIT(POWER_DOMAIN_VGA) | \
+ BIT(POWER_DOMAIN_AUDIO) | \
+ BIT(POWER_DOMAIN_AUX_B) | \
+ BIT(POWER_DOMAIN_AUX_C) | \
+ BIT(POWER_DOMAIN_GMBUS) | \
+ BIT(POWER_DOMAIN_INIT))
#define VLV_DPIO_CMN_BC_POWER_DOMAINS ( \
BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \
@@ -1617,6 +1691,28 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,
BIT(POWER_DOMAIN_AUX_C) | \
BIT(POWER_DOMAIN_INIT))
+#define CHV_DISPLAY_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_A) | \
+ BIT(POWER_DOMAIN_PIPE_B) | \
+ BIT(POWER_DOMAIN_PIPE_C) | \
+ BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \
+ BIT(POWER_DOMAIN_TRANSCODER_A) | \
+ BIT(POWER_DOMAIN_TRANSCODER_B) | \
+ BIT(POWER_DOMAIN_TRANSCODER_C) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_D_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DSI) | \
+ BIT(POWER_DOMAIN_VGA) | \
+ BIT(POWER_DOMAIN_AUDIO) | \
+ BIT(POWER_DOMAIN_AUX_B) | \
+ BIT(POWER_DOMAIN_AUX_C) | \
+ BIT(POWER_DOMAIN_AUX_D) | \
+ BIT(POWER_DOMAIN_GMBUS) | \
+ BIT(POWER_DOMAIN_INIT))
+
#define CHV_DPIO_CMN_BC_POWER_DOMAINS ( \
BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \
BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \
@@ -1684,7 +1780,7 @@ static struct i915_power_well hsw_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
- .domains = HSW_ALWAYS_ON_POWER_DOMAINS,
+ .domains = POWER_DOMAIN_MASK,
.ops = &i9xx_always_on_power_well_ops,
},
{
@@ -1698,7 +1794,7 @@ static struct i915_power_well bdw_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
- .domains = BDW_ALWAYS_ON_POWER_DOMAINS,
+ .domains = POWER_DOMAIN_MASK,
.ops = &i9xx_always_on_power_well_ops,
},
{
@@ -1733,7 +1829,7 @@ static struct i915_power_well vlv_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
- .domains = VLV_ALWAYS_ON_POWER_DOMAINS,
+ .domains = POWER_DOMAIN_MASK,
.ops = &i9xx_always_on_power_well_ops,
.data = PUNIT_POWER_WELL_ALWAYS_ON,
},
@@ -1791,7 +1887,7 @@ static struct i915_power_well chv_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
- .domains = VLV_ALWAYS_ON_POWER_DOMAINS,
+ .domains = POWER_DOMAIN_MASK,
.ops = &i9xx_always_on_power_well_ops,
},
{
@@ -1801,7 +1897,7 @@ static struct i915_power_well chv_power_wells[] = {
* power wells don't actually exist. Pipe A power well is
* required for any pipe to work.
*/
- .domains = VLV_DISPLAY_POWER_DOMAINS,
+ .domains = CHV_DISPLAY_POWER_DOMAINS,
.data = PIPE_A,
.ops = &chv_pipe_power_well_ops,
},
@@ -1835,7 +1931,7 @@ static struct i915_power_well skl_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
- .domains = SKL_DISPLAY_ALWAYS_ON_POWER_DOMAINS,
+ .domains = POWER_DOMAIN_MASK,
.ops = &i9xx_always_on_power_well_ops,
.data = SKL_DISP_PW_ALWAYS_ON,
},
@@ -1891,44 +1987,16 @@ static struct i915_power_well skl_power_wells[] = {
},
};
-void skl_pw1_misc_io_init(struct drm_i915_private *dev_priv)
-{
- struct i915_power_well *well;
-
- if (!(IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)))
- return;
-
- well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
- intel_power_well_enable(dev_priv, well);
-
- well = lookup_power_well(dev_priv, SKL_DISP_PW_MISC_IO);
- intel_power_well_enable(dev_priv, well);
-}
-
-void skl_pw1_misc_io_fini(struct drm_i915_private *dev_priv)
-{
- struct i915_power_well *well;
-
- if (!(IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)))
- return;
-
- well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
- intel_power_well_disable(dev_priv, well);
-
- well = lookup_power_well(dev_priv, SKL_DISP_PW_MISC_IO);
- intel_power_well_disable(dev_priv, well);
-}
-
static struct i915_power_well bxt_power_wells[] = {
{
.name = "always-on",
.always_on = 1,
- .domains = BXT_DISPLAY_ALWAYS_ON_POWER_DOMAINS,
+ .domains = POWER_DOMAIN_MASK,
.ops = &i9xx_always_on_power_well_ops,
},
{
.name = "power well 1",
- .domains = BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS,
+ .domains = 0,
.ops = &skl_power_well_ops,
.data = SKL_DISP_PW_1,
},
@@ -1953,11 +2021,6 @@ sanitize_disable_power_well_option(const struct drm_i915_private *dev_priv,
if (disable_power_well >= 0)
return !!disable_power_well;
- if (IS_BROXTON(dev_priv)) {
- DRM_DEBUG_KMS("Disabling display power well support\n");
- return 0;
- }
-
return 1;
}
@@ -2109,9 +2172,10 @@ static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv)
}
static void skl_display_core_init(struct drm_i915_private *dev_priv,
- bool resume)
+ bool resume)
{
struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ struct i915_power_well *well;
uint32_t val;
gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
@@ -2122,7 +2186,13 @@ static void skl_display_core_init(struct drm_i915_private *dev_priv,
/* enable PG1 and Misc I/O */
mutex_lock(&power_domains->lock);
- skl_pw1_misc_io_init(dev_priv);
+
+ well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
+ intel_power_well_enable(dev_priv, well);
+
+ well = lookup_power_well(dev_priv, SKL_DISP_PW_MISC_IO);
+ intel_power_well_enable(dev_priv, well);
+
mutex_unlock(&power_domains->lock);
if (!resume)
@@ -2137,6 +2207,7 @@ static void skl_display_core_init(struct drm_i915_private *dev_priv,
static void skl_display_core_uninit(struct drm_i915_private *dev_priv)
{
struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ struct i915_power_well *well;
gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
@@ -2144,8 +2215,73 @@ static void skl_display_core_uninit(struct drm_i915_private *dev_priv)
/* The spec doesn't call for removing the reset handshake flag */
/* disable PG1 and Misc I/O */
+
mutex_lock(&power_domains->lock);
- skl_pw1_misc_io_fini(dev_priv);
+
+ well = lookup_power_well(dev_priv, SKL_DISP_PW_MISC_IO);
+ intel_power_well_disable(dev_priv, well);
+
+ well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
+ intel_power_well_disable(dev_priv, well);
+
+ mutex_unlock(&power_domains->lock);
+}
+
+void bxt_display_core_init(struct drm_i915_private *dev_priv,
+ bool resume)
+{
+ struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ struct i915_power_well *well;
+ uint32_t val;
+
+ gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+ /*
+ * NDE_RSTWRN_OPT RST PCH Handshake En must always be 0b on BXT
+ * or else the reset will hang because there is no PCH to respond.
+ * Move the handshake programming to initialization sequence.
+ * Previously was left up to BIOS.
+ */
+ val = I915_READ(HSW_NDE_RSTWRN_OPT);
+ val &= ~RESET_PCH_HANDSHAKE_ENABLE;
+ I915_WRITE(HSW_NDE_RSTWRN_OPT, val);
+
+ /* Enable PG1 */
+ mutex_lock(&power_domains->lock);
+
+ well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
+ intel_power_well_enable(dev_priv, well);
+
+ mutex_unlock(&power_domains->lock);
+
+ broxton_init_cdclk(dev_priv);
+ broxton_ddi_phy_init(dev_priv);
+
+ broxton_cdclk_verify_state(dev_priv);
+ broxton_ddi_phy_verify_state(dev_priv);
+
+ if (resume && dev_priv->csr.dmc_payload)
+ intel_csr_load_program(dev_priv);
+}
+
+void bxt_display_core_uninit(struct drm_i915_private *dev_priv)
+{
+ struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ struct i915_power_well *well;
+
+ gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+ broxton_ddi_phy_uninit(dev_priv);
+ broxton_uninit_cdclk(dev_priv);
+
+ /* The spec doesn't call for removing the reset handshake flag */
+
+ /* Disable PG1 */
+ mutex_lock(&power_domains->lock);
+
+ well = lookup_power_well(dev_priv, SKL_DISP_PW_1);
+ intel_power_well_disable(dev_priv, well);
+
mutex_unlock(&power_domains->lock);
}
@@ -2280,6 +2416,8 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume)
if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
skl_display_core_init(dev_priv, resume);
+ } else if (IS_BROXTON(dev)) {
+ bxt_display_core_init(dev_priv, resume);
} else if (IS_CHERRYVIEW(dev)) {
mutex_lock(&power_domains->lock);
chv_phy_control_init(dev_priv);
@@ -2317,6 +2455,8 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv)
if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
skl_display_core_uninit(dev_priv);
+ else if (IS_BROXTON(dev_priv))
+ bxt_display_core_uninit(dev_priv);
}
/**
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index ac2ac07b505b..4f1dfe616856 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -60,7 +60,11 @@ fw_domain_reset(const struct intel_uncore_forcewake_domain *d)
static inline void
fw_domain_arm_timer(struct intel_uncore_forcewake_domain *d)
{
- mod_timer_pinned(&d->timer, jiffies + 1);
+ d->wake_count++;
+ hrtimer_start_range_ns(&d->timer,
+ ktime_set(0, NSEC_PER_MSEC),
+ NSEC_PER_MSEC,
+ HRTIMER_MODE_REL);
}
static inline void
@@ -107,22 +111,22 @@ static void
fw_domains_get(struct drm_i915_private *dev_priv, enum forcewake_domains fw_domains)
{
struct intel_uncore_forcewake_domain *d;
- enum forcewake_domain_id id;
- for_each_fw_domain_mask(d, fw_domains, dev_priv, id) {
+ for_each_fw_domain_masked(d, fw_domains, dev_priv) {
fw_domain_wait_ack_clear(d);
fw_domain_get(d);
- fw_domain_wait_ack(d);
}
+
+ for_each_fw_domain_masked(d, fw_domains, dev_priv)
+ fw_domain_wait_ack(d);
}
static void
fw_domains_put(struct drm_i915_private *dev_priv, enum forcewake_domains fw_domains)
{
struct intel_uncore_forcewake_domain *d;
- enum forcewake_domain_id id;
- for_each_fw_domain_mask(d, fw_domains, dev_priv, id) {
+ for_each_fw_domain_masked(d, fw_domains, dev_priv) {
fw_domain_put(d);
fw_domain_posting_read(d);
}
@@ -132,10 +136,9 @@ static void
fw_domains_posting_read(struct drm_i915_private *dev_priv)
{
struct intel_uncore_forcewake_domain *d;
- enum forcewake_domain_id id;
/* No need to do for all, just do for first found */
- for_each_fw_domain(d, dev_priv, id) {
+ for_each_fw_domain(d, dev_priv) {
fw_domain_posting_read(d);
break;
}
@@ -145,12 +148,11 @@ static void
fw_domains_reset(struct drm_i915_private *dev_priv, enum forcewake_domains fw_domains)
{
struct intel_uncore_forcewake_domain *d;
- enum forcewake_domain_id id;
if (dev_priv->uncore.fw_domains == 0)
return;
- for_each_fw_domain_mask(d, fw_domains, dev_priv, id)
+ for_each_fw_domain_masked(d, fw_domains, dev_priv)
fw_domain_reset(d);
fw_domains_posting_read(dev_priv);
@@ -224,9 +226,11 @@ static int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
return ret;
}
-static void intel_uncore_fw_release_timer(unsigned long arg)
+static enum hrtimer_restart
+intel_uncore_fw_release_timer(struct hrtimer *timer)
{
- struct intel_uncore_forcewake_domain *domain = (void *)arg;
+ struct intel_uncore_forcewake_domain *domain =
+ container_of(timer, struct intel_uncore_forcewake_domain, timer);
unsigned long irqflags;
assert_rpm_device_not_suspended(domain->i915);
@@ -240,6 +244,8 @@ static void intel_uncore_fw_release_timer(unsigned long arg)
1 << domain->id);
spin_unlock_irqrestore(&domain->i915->uncore.lock, irqflags);
+
+ return HRTIMER_NORESTART;
}
void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
@@ -248,7 +254,6 @@ void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
unsigned long irqflags;
struct intel_uncore_forcewake_domain *domain;
int retry_count = 100;
- enum forcewake_domain_id id;
enum forcewake_domains fw = 0, active_domains;
/* Hold uncore.lock across reset to prevent any register access
@@ -258,18 +263,18 @@ void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
while (1) {
active_domains = 0;
- for_each_fw_domain(domain, dev_priv, id) {
- if (del_timer_sync(&domain->timer) == 0)
+ for_each_fw_domain(domain, dev_priv) {
+ if (hrtimer_cancel(&domain->timer) == 0)
continue;
- intel_uncore_fw_release_timer((unsigned long)domain);
+ intel_uncore_fw_release_timer(&domain->timer);
}
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
- for_each_fw_domain(domain, dev_priv, id) {
- if (timer_pending(&domain->timer))
- active_domains |= (1 << id);
+ for_each_fw_domain(domain, dev_priv) {
+ if (hrtimer_active(&domain->timer))
+ active_domains |= domain->mask;
}
if (active_domains == 0)
@@ -286,9 +291,9 @@ void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
WARN_ON(active_domains);
- for_each_fw_domain(domain, dev_priv, id)
+ for_each_fw_domain(domain, dev_priv)
if (domain->wake_count)
- fw |= 1 << id;
+ fw |= domain->mask;
if (fw)
dev_priv->uncore.funcs.force_wake_put(dev_priv, fw);
@@ -310,21 +315,49 @@ void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
-static void intel_uncore_ellc_detect(struct drm_device *dev)
+static u64 gen9_edram_size(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ const unsigned int ways[8] = { 4, 8, 12, 16, 16, 16, 16, 16 };
+ const unsigned int sets[4] = { 1, 1, 2, 2 };
+ const u32 cap = dev_priv->edram_cap;
+
+ return EDRAM_NUM_BANKS(cap) *
+ ways[EDRAM_WAYS_IDX(cap)] *
+ sets[EDRAM_SETS_IDX(cap)] *
+ 1024 * 1024;
+}
+
+u64 intel_uncore_edram_size(struct drm_i915_private *dev_priv)
+{
+ if (!HAS_EDRAM(dev_priv))
+ return 0;
+
+ /* The needed capability bits for size calculation
+ * are not there with pre gen9 so return 128MB always.
+ */
+ if (INTEL_GEN(dev_priv) < 9)
+ return 128 * 1024 * 1024;
+
+ return gen9_edram_size(dev_priv);
+}
+
+static void intel_uncore_edram_detect(struct drm_i915_private *dev_priv)
+{
+ if (IS_HASWELL(dev_priv) ||
+ IS_BROADWELL(dev_priv) ||
+ INTEL_GEN(dev_priv) >= 9) {
+ dev_priv->edram_cap = __raw_i915_read32(dev_priv,
+ HSW_EDRAM_CAP);
- if ((IS_HASWELL(dev) || IS_BROADWELL(dev) ||
- INTEL_INFO(dev)->gen >= 9) &&
- (__raw_i915_read32(dev_priv, HSW_EDRAM_PRESENT) & EDRAM_ENABLED)) {
- /* The docs do not explain exactly how the calculation can be
- * made. It is somewhat guessable, but for now, it's always
- * 128MB.
- * NB: We can't write IDICR yet because we do not have gt funcs
+ /* NB: We can't write IDICR yet because we do not have gt funcs
* set up */
- dev_priv->ellc_size = 128;
- DRM_INFO("Found %zuMB of eLLC\n", dev_priv->ellc_size);
+ } else {
+ dev_priv->edram_cap = 0;
}
+
+ if (HAS_EDRAM(dev_priv))
+ DRM_INFO("Found %lluMB of eDRAM\n",
+ intel_uncore_edram_size(dev_priv) / (1024 * 1024));
}
static bool
@@ -410,16 +443,15 @@ static void __intel_uncore_forcewake_get(struct drm_i915_private *dev_priv,
enum forcewake_domains fw_domains)
{
struct intel_uncore_forcewake_domain *domain;
- enum forcewake_domain_id id;
if (!dev_priv->uncore.funcs.force_wake_get)
return;
fw_domains &= dev_priv->uncore.fw_domains;
- for_each_fw_domain_mask(domain, fw_domains, dev_priv, id) {
+ for_each_fw_domain_masked(domain, fw_domains, dev_priv) {
if (domain->wake_count++)
- fw_domains &= ~(1 << id);
+ fw_domains &= ~domain->mask;
}
if (fw_domains)
@@ -477,21 +509,19 @@ static void __intel_uncore_forcewake_put(struct drm_i915_private *dev_priv,
enum forcewake_domains fw_domains)
{
struct intel_uncore_forcewake_domain *domain;
- enum forcewake_domain_id id;
if (!dev_priv->uncore.funcs.force_wake_put)
return;
fw_domains &= dev_priv->uncore.fw_domains;
- for_each_fw_domain_mask(domain, fw_domains, dev_priv, id) {
+ for_each_fw_domain_masked(domain, fw_domains, dev_priv) {
if (WARN_ON(domain->wake_count == 0))
continue;
if (--domain->wake_count)
continue;
- domain->wake_count++;
fw_domain_arm_timer(domain);
}
}
@@ -539,18 +569,27 @@ void intel_uncore_forcewake_put__locked(struct drm_i915_private *dev_priv,
void assert_forcewakes_inactive(struct drm_i915_private *dev_priv)
{
struct intel_uncore_forcewake_domain *domain;
- enum forcewake_domain_id id;
if (!dev_priv->uncore.funcs.force_wake_get)
return;
- for_each_fw_domain(domain, dev_priv, id)
+ for_each_fw_domain(domain, dev_priv)
WARN_ON(domain->wake_count);
}
/* We give fast paths for the really cool registers */
#define NEEDS_FORCE_WAKE(reg) ((reg) < 0x40000)
+#define __gen6_reg_read_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd; \
+ if (NEEDS_FORCE_WAKE(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else \
+ __fwd = 0; \
+ __fwd; \
+})
+
#define REG_RANGE(reg, start, end) ((reg) >= (start) && (reg) < (end))
#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \
@@ -564,6 +603,48 @@ void assert_forcewakes_inactive(struct drm_i915_private *dev_priv)
REG_RANGE((reg), 0x22000, 0x24000) || \
REG_RANGE((reg), 0x30000, 0x40000))
+#define __vlv_reg_read_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd = 0; \
+ if (!NEEDS_FORCE_WAKE(offset)) \
+ __fwd = 0; \
+ else if (FORCEWAKE_VLV_RENDER_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else if (FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_MEDIA; \
+ __fwd; \
+})
+
+static const i915_reg_t gen8_shadowed_regs[] = {
+ GEN6_RPNSWREQ,
+ GEN6_RC_VIDEO_FREQ,
+ RING_TAIL(RENDER_RING_BASE),
+ RING_TAIL(GEN6_BSD_RING_BASE),
+ RING_TAIL(VEBOX_RING_BASE),
+ RING_TAIL(BLT_RING_BASE),
+ /* TODO: Other registers are not yet used */
+};
+
+static bool is_gen8_shadowed(u32 offset)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(gen8_shadowed_regs); i++)
+ if (offset == gen8_shadowed_regs[i].reg)
+ return true;
+
+ return false;
+}
+
+#define __gen8_reg_write_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd; \
+ if (NEEDS_FORCE_WAKE(offset) && !is_gen8_shadowed(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else \
+ __fwd = 0; \
+ __fwd; \
+})
+
#define FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg) \
(REG_RANGE((reg), 0x2000, 0x4000) || \
REG_RANGE((reg), 0x5200, 0x8000) || \
@@ -586,6 +667,34 @@ void assert_forcewakes_inactive(struct drm_i915_private *dev_priv)
REG_RANGE((reg), 0x9000, 0xB000) || \
REG_RANGE((reg), 0xF000, 0x10000))
+#define __chv_reg_read_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd = 0; \
+ if (!NEEDS_FORCE_WAKE(offset)) \
+ __fwd = 0; \
+ else if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_MEDIA; \
+ else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
+ __fwd; \
+})
+
+#define __chv_reg_write_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd = 0; \
+ if (!NEEDS_FORCE_WAKE(offset) || is_gen8_shadowed(offset)) \
+ __fwd = 0; \
+ else if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_MEDIA; \
+ else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
+ __fwd; \
+})
+
#define FORCEWAKE_GEN9_UNCORE_RANGE_OFFSET(reg) \
REG_RANGE((reg), 0xB00, 0x2000)
@@ -618,6 +727,61 @@ void assert_forcewakes_inactive(struct drm_i915_private *dev_priv)
!FORCEWAKE_GEN9_MEDIA_RANGE_OFFSET(reg) && \
!FORCEWAKE_GEN9_COMMON_RANGE_OFFSET(reg))
+#define SKL_NEEDS_FORCE_WAKE(reg) \
+ ((reg) < 0x40000 && !FORCEWAKE_GEN9_UNCORE_RANGE_OFFSET(reg))
+
+#define __gen9_reg_read_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd; \
+ if (!SKL_NEEDS_FORCE_WAKE(offset)) \
+ __fwd = 0; \
+ else if (FORCEWAKE_GEN9_RENDER_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else if (FORCEWAKE_GEN9_MEDIA_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_MEDIA; \
+ else if (FORCEWAKE_GEN9_COMMON_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
+ else \
+ __fwd = FORCEWAKE_BLITTER; \
+ __fwd; \
+})
+
+static const i915_reg_t gen9_shadowed_regs[] = {
+ RING_TAIL(RENDER_RING_BASE),
+ RING_TAIL(GEN6_BSD_RING_BASE),
+ RING_TAIL(VEBOX_RING_BASE),
+ RING_TAIL(BLT_RING_BASE),
+ GEN6_RPNSWREQ,
+ GEN6_RC_VIDEO_FREQ,
+ /* TODO: Other registers are not yet used */
+};
+
+static bool is_gen9_shadowed(u32 offset)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(gen9_shadowed_regs); i++)
+ if (offset == gen9_shadowed_regs[i].reg)
+ return true;
+
+ return false;
+}
+
+#define __gen9_reg_write_fw_domains(offset) \
+({ \
+ enum forcewake_domains __fwd; \
+ if (!SKL_NEEDS_FORCE_WAKE(offset) || is_gen9_shadowed(offset)) \
+ __fwd = 0; \
+ else if (FORCEWAKE_GEN9_RENDER_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER; \
+ else if (FORCEWAKE_GEN9_MEDIA_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_MEDIA; \
+ else if (FORCEWAKE_GEN9_COMMON_RANGE_OFFSET(offset)) \
+ __fwd = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
+ else \
+ __fwd = FORCEWAKE_BLITTER; \
+ __fwd; \
+})
+
static void
ilk_dummy_write(struct drm_i915_private *dev_priv)
{
@@ -633,15 +797,6 @@ __unclaimed_reg_debug(struct drm_i915_private *dev_priv,
const bool read,
const bool before)
{
- /* XXX. We limit the auto arming traces for mmio
- * debugs on these platforms. There are just too many
- * revealed by these and CI/Bat suffers from the noise.
- * Please fix and then re-enable the automatic traces.
- */
- if (i915.mmio_debug < 2 &&
- (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)))
- return;
-
if (WARN(check_for_unclaimed_mmio(dev_priv),
"Unclaimed register detected %s %s register 0x%x\n",
before ? "before" : "after",
@@ -720,19 +875,17 @@ static inline void __force_wake_auto(struct drm_i915_private *dev_priv,
enum forcewake_domains fw_domains)
{
struct intel_uncore_forcewake_domain *domain;
- enum forcewake_domain_id id;
if (WARN_ON(!fw_domains))
return;
/* Ideally GCC would be constant-fold and eliminate this loop */
- for_each_fw_domain_mask(domain, fw_domains, dev_priv, id) {
+ for_each_fw_domain_masked(domain, fw_domains, dev_priv) {
if (domain->wake_count) {
- fw_domains &= ~(1 << id);
+ fw_domains &= ~domain->mask;
continue;
}
- domain->wake_count++;
fw_domain_arm_timer(domain);
}
@@ -743,9 +896,11 @@ static inline void __force_wake_auto(struct drm_i915_private *dev_priv,
#define __gen6_read(x) \
static u##x \
gen6_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { \
+ enum forcewake_domains fw_engine; \
GEN6_READ_HEADER(x); \
- if (NEEDS_FORCE_WAKE(offset)) \
- __force_wake_auto(dev_priv, FORCEWAKE_RENDER); \
+ fw_engine = __gen6_reg_read_fw_domains(offset); \
+ if (fw_engine) \
+ __force_wake_auto(dev_priv, fw_engine); \
val = __raw_i915_read##x(dev_priv, reg); \
GEN6_READ_FOOTER; \
}
@@ -753,14 +908,9 @@ gen6_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { \
#define __vlv_read(x) \
static u##x \
vlv_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { \
- enum forcewake_domains fw_engine = 0; \
+ enum forcewake_domains fw_engine; \
GEN6_READ_HEADER(x); \
- if (!NEEDS_FORCE_WAKE(offset)) \
- fw_engine = 0; \
- else if (FORCEWAKE_VLV_RENDER_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER; \
- else if (FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_MEDIA; \
+ fw_engine = __vlv_reg_read_fw_domains(offset); \
if (fw_engine) \
__force_wake_auto(dev_priv, fw_engine); \
val = __raw_i915_read##x(dev_priv, reg); \
@@ -770,40 +920,21 @@ vlv_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { \
#define __chv_read(x) \
static u##x \
chv_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { \
- enum forcewake_domains fw_engine = 0; \
+ enum forcewake_domains fw_engine; \
GEN6_READ_HEADER(x); \
- if (!NEEDS_FORCE_WAKE(offset)) \
- fw_engine = 0; \
- else if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER; \
- else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_MEDIA; \
- else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
+ fw_engine = __chv_reg_read_fw_domains(offset); \
if (fw_engine) \
__force_wake_auto(dev_priv, fw_engine); \
val = __raw_i915_read##x(dev_priv, reg); \
GEN6_READ_FOOTER; \
}
-#define SKL_NEEDS_FORCE_WAKE(reg) \
- ((reg) < 0x40000 && !FORCEWAKE_GEN9_UNCORE_RANGE_OFFSET(reg))
-
#define __gen9_read(x) \
static u##x \
gen9_read##x(struct drm_i915_private *dev_priv, i915_reg_t reg, bool trace) { \
enum forcewake_domains fw_engine; \
GEN6_READ_HEADER(x); \
- if (!SKL_NEEDS_FORCE_WAKE(offset)) \
- fw_engine = 0; \
- else if (FORCEWAKE_GEN9_RENDER_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER; \
- else if (FORCEWAKE_GEN9_MEDIA_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_MEDIA; \
- else if (FORCEWAKE_GEN9_COMMON_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
- else \
- fw_engine = FORCEWAKE_BLITTER; \
+ fw_engine = __gen9_reg_read_fw_domains(offset); \
if (fw_engine) \
__force_wake_auto(dev_priv, fw_engine); \
val = __raw_i915_read##x(dev_priv, reg); \
@@ -942,34 +1073,14 @@ hsw_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool t
GEN6_WRITE_FOOTER; \
}
-static const i915_reg_t gen8_shadowed_regs[] = {
- FORCEWAKE_MT,
- GEN6_RPNSWREQ,
- GEN6_RC_VIDEO_FREQ,
- RING_TAIL(RENDER_RING_BASE),
- RING_TAIL(GEN6_BSD_RING_BASE),
- RING_TAIL(VEBOX_RING_BASE),
- RING_TAIL(BLT_RING_BASE),
- /* TODO: Other registers are not yet used */
-};
-
-static bool is_gen8_shadowed(struct drm_i915_private *dev_priv,
- i915_reg_t reg)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(gen8_shadowed_regs); i++)
- if (i915_mmio_reg_equal(reg, gen8_shadowed_regs[i]))
- return true;
-
- return false;
-}
-
#define __gen8_write(x) \
static void \
gen8_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool trace) { \
+ enum forcewake_domains fw_engine; \
GEN6_WRITE_HEADER; \
- if (NEEDS_FORCE_WAKE(offset) && !is_gen8_shadowed(dev_priv, reg)) \
- __force_wake_auto(dev_priv, FORCEWAKE_RENDER); \
+ fw_engine = __gen8_reg_write_fw_domains(offset); \
+ if (fw_engine) \
+ __force_wake_auto(dev_priv, fw_engine); \
__raw_i915_write##x(dev_priv, reg, val); \
GEN6_WRITE_FOOTER; \
}
@@ -977,64 +1088,22 @@ gen8_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool
#define __chv_write(x) \
static void \
chv_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool trace) { \
- enum forcewake_domains fw_engine = 0; \
+ enum forcewake_domains fw_engine; \
GEN6_WRITE_HEADER; \
- if (!NEEDS_FORCE_WAKE(offset) || \
- is_gen8_shadowed(dev_priv, reg)) \
- fw_engine = 0; \
- else if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER; \
- else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_MEDIA; \
- else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
+ fw_engine = __chv_reg_write_fw_domains(offset); \
if (fw_engine) \
__force_wake_auto(dev_priv, fw_engine); \
__raw_i915_write##x(dev_priv, reg, val); \
GEN6_WRITE_FOOTER; \
}
-static const i915_reg_t gen9_shadowed_regs[] = {
- RING_TAIL(RENDER_RING_BASE),
- RING_TAIL(GEN6_BSD_RING_BASE),
- RING_TAIL(VEBOX_RING_BASE),
- RING_TAIL(BLT_RING_BASE),
- FORCEWAKE_BLITTER_GEN9,
- FORCEWAKE_RENDER_GEN9,
- FORCEWAKE_MEDIA_GEN9,
- GEN6_RPNSWREQ,
- GEN6_RC_VIDEO_FREQ,
- /* TODO: Other registers are not yet used */
-};
-
-static bool is_gen9_shadowed(struct drm_i915_private *dev_priv,
- i915_reg_t reg)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(gen9_shadowed_regs); i++)
- if (i915_mmio_reg_equal(reg, gen9_shadowed_regs[i]))
- return true;
-
- return false;
-}
-
#define __gen9_write(x) \
static void \
gen9_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, \
bool trace) { \
enum forcewake_domains fw_engine; \
GEN6_WRITE_HEADER; \
- if (!SKL_NEEDS_FORCE_WAKE(offset) || \
- is_gen9_shadowed(dev_priv, reg)) \
- fw_engine = 0; \
- else if (FORCEWAKE_GEN9_RENDER_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER; \
- else if (FORCEWAKE_GEN9_MEDIA_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_MEDIA; \
- else if (FORCEWAKE_GEN9_COMMON_RANGE_OFFSET(offset)) \
- fw_engine = FORCEWAKE_RENDER | FORCEWAKE_MEDIA; \
- else \
- fw_engine = FORCEWAKE_BLITTER; \
+ fw_engine = __gen9_reg_write_fw_domains(offset); \
if (fw_engine) \
__force_wake_auto(dev_priv, fw_engine); \
__raw_i915_write##x(dev_priv, reg, val); \
@@ -1150,7 +1219,14 @@ static void fw_domain_init(struct drm_i915_private *dev_priv,
d->i915 = dev_priv;
d->id = domain_id;
- setup_timer(&d->timer, intel_uncore_fw_release_timer, (unsigned long)d);
+ BUILD_BUG_ON(FORCEWAKE_RENDER != (1 << FW_DOMAIN_ID_RENDER));
+ BUILD_BUG_ON(FORCEWAKE_BLITTER != (1 << FW_DOMAIN_ID_BLITTER));
+ BUILD_BUG_ON(FORCEWAKE_MEDIA != (1 << FW_DOMAIN_ID_MEDIA));
+
+ d->mask = 1 << domain_id;
+
+ hrtimer_init(&d->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ d->timer.function = intel_uncore_fw_release_timer;
dev_priv->uncore.fw_domains |= (1 << domain_id);
@@ -1189,7 +1265,11 @@ static void intel_uncore_fw_domains_init(struct drm_device *dev)
} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
dev_priv->uncore.funcs.force_wake_get =
fw_domains_get_with_thread_status;
- dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
+ if (IS_HASWELL(dev))
+ dev_priv->uncore.funcs.force_wake_put =
+ fw_domains_put_with_fifo;
+ else
+ dev_priv->uncore.funcs.force_wake_put = fw_domains_put;
fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER,
FORCEWAKE_MT, FORCEWAKE_ACK_HSW);
} else if (IS_IVYBRIDGE(dev)) {
@@ -1253,7 +1333,7 @@ void intel_uncore_init(struct drm_device *dev)
i915_check_vgpu(dev);
- intel_uncore_ellc_detect(dev);
+ intel_uncore_edram_detect(dev_priv);
intel_uncore_fw_domains_init(dev);
__intel_uncore_early_sanitize(dev, false);
@@ -1715,3 +1795,111 @@ intel_uncore_arm_unclaimed_mmio_detection(struct drm_i915_private *dev_priv)
return false;
}
+
+static enum forcewake_domains
+intel_uncore_forcewake_for_read(struct drm_i915_private *dev_priv,
+ i915_reg_t reg)
+{
+ enum forcewake_domains fw_domains;
+
+ if (intel_vgpu_active(dev_priv->dev))
+ return 0;
+
+ switch (INTEL_INFO(dev_priv)->gen) {
+ case 9:
+ fw_domains = __gen9_reg_read_fw_domains(i915_mmio_reg_offset(reg));
+ break;
+ case 8:
+ if (IS_CHERRYVIEW(dev_priv))
+ fw_domains = __chv_reg_read_fw_domains(i915_mmio_reg_offset(reg));
+ else
+ fw_domains = __gen6_reg_read_fw_domains(i915_mmio_reg_offset(reg));
+ break;
+ case 7:
+ case 6:
+ if (IS_VALLEYVIEW(dev_priv))
+ fw_domains = __vlv_reg_read_fw_domains(i915_mmio_reg_offset(reg));
+ else
+ fw_domains = __gen6_reg_read_fw_domains(i915_mmio_reg_offset(reg));
+ break;
+ default:
+ MISSING_CASE(INTEL_INFO(dev_priv)->gen);
+ case 5: /* forcewake was introduced with gen6 */
+ case 4:
+ case 3:
+ case 2:
+ return 0;
+ }
+
+ WARN_ON(fw_domains & ~dev_priv->uncore.fw_domains);
+
+ return fw_domains;
+}
+
+static enum forcewake_domains
+intel_uncore_forcewake_for_write(struct drm_i915_private *dev_priv,
+ i915_reg_t reg)
+{
+ enum forcewake_domains fw_domains;
+
+ if (intel_vgpu_active(dev_priv->dev))
+ return 0;
+
+ switch (INTEL_INFO(dev_priv)->gen) {
+ case 9:
+ fw_domains = __gen9_reg_write_fw_domains(i915_mmio_reg_offset(reg));
+ break;
+ case 8:
+ if (IS_CHERRYVIEW(dev_priv))
+ fw_domains = __chv_reg_write_fw_domains(i915_mmio_reg_offset(reg));
+ else
+ fw_domains = __gen8_reg_write_fw_domains(i915_mmio_reg_offset(reg));
+ break;
+ case 7:
+ case 6:
+ fw_domains = FORCEWAKE_RENDER;
+ break;
+ default:
+ MISSING_CASE(INTEL_INFO(dev_priv)->gen);
+ case 5:
+ case 4:
+ case 3:
+ case 2:
+ return 0;
+ }
+
+ WARN_ON(fw_domains & ~dev_priv->uncore.fw_domains);
+
+ return fw_domains;
+}
+
+/**
+ * intel_uncore_forcewake_for_reg - which forcewake domains are needed to access
+ * a register
+ * @dev_priv: pointer to struct drm_i915_private
+ * @reg: register in question
+ * @op: operation bitmask of FW_REG_READ and/or FW_REG_WRITE
+ *
+ * Returns a set of forcewake domains required to be taken with for example
+ * intel_uncore_forcewake_get for the specified register to be accessible in the
+ * specified mode (read, write or read/write) with raw mmio accessors.
+ *
+ * NOTE: On Gen6 and Gen7 write forcewake domain (FORCEWAKE_RENDER) requires the
+ * callers to do FIFO management on their own or risk losing writes.
+ */
+enum forcewake_domains
+intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
+ i915_reg_t reg, unsigned int op)
+{
+ enum forcewake_domains fw_domains = 0;
+
+ WARN_ON(!op);
+
+ if (op & FW_REG_READ)
+ fw_domains = intel_uncore_forcewake_for_read(dev_priv, reg);
+
+ if (op & FW_REG_WRITE)
+ fw_domains |= intel_uncore_forcewake_for_write(dev_priv, reg);
+
+ return fw_domains;
+}
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 1f10fa0928b4..7fc3ca5ce6c7 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -2,7 +2,7 @@ config DRM_RCAR_DU
tristate "DRM Support for R-Car Display Unit"
depends on DRM && OF
depends on ARM || ARM64
- depends on ARCH_SHMOBILE || COMPILE_TEST
+ depends on ARCH_RENESAS || COMPILE_TEST
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
@@ -27,6 +27,6 @@ config DRM_RCAR_LVDS
config DRM_RCAR_VSP
bool "R-Car DU VSP Compositor Support"
depends on DRM_RCAR_DU
- depends on VIDEO_RENESAS_VSP1
+ depends on VIDEO_RENESAS_VSP1=y || (VIDEO_RENESAS_VSP1 && DRM_RCAR_DU=m)
help
Enable support to expose the R-Car VSP Compositor as KMS planes.
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index a1d94d8d9443..7f6a55cae27a 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -114,27 +114,6 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder)
int ret;
u32 val;
- /*
- * FIXME(Yakir): driver should configure the CRTC output video
- * mode with the display information which indicated the monitor
- * support colorimetry.
- *
- * But don't know why the CRTC driver seems could only output the
- * RGBaaa rightly. For example, if connect the "innolux,n116bge"
- * eDP screen, EDID would indicated that screen only accepted the
- * 6bpc mode. But if I configure CRTC to RGB666 output, then eDP
- * screen would show a blue picture (RGB888 show a green picture).
- * But if I configure CTRC to RGBaaa, and eDP driver still keep
- * RGB666 input video mode, then screen would works prefect.
- */
- ret = rockchip_drm_crtc_mode_config(encoder->crtc,
- DRM_MODE_CONNECTOR_eDP,
- ROCKCHIP_OUT_MODE_AAAA);
- if (ret < 0) {
- dev_err(dp->dev, "Could not set crtc mode config (%d)\n", ret);
- return;
- }
-
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
if (ret < 0)
return;
@@ -158,11 +137,38 @@ static void rockchip_dp_drm_encoder_nop(struct drm_encoder *encoder)
/* do nothing */
}
+static int
+rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+ /*
+ * FIXME(Yakir): driver should configure the CRTC output video
+ * mode with the display information which indicated the monitor
+ * support colorimetry.
+ *
+ * But don't know why the CRTC driver seems could only output the
+ * RGBaaa rightly. For example, if connect the "innolux,n116bge"
+ * eDP screen, EDID would indicated that screen only accepted the
+ * 6bpc mode. But if I configure CRTC to RGB666 output, then eDP
+ * screen would show a blue picture (RGB888 show a green picture).
+ * But if I configure CTRC to RGBaaa, and eDP driver still keep
+ * RGB666 input video mode, then screen would works prefect.
+ */
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ s->output_type = DRM_MODE_CONNECTOR_eDP;
+
+ return 0;
+}
+
static struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = {
.mode_fixup = rockchip_dp_drm_encoder_mode_fixup,
.mode_set = rockchip_dp_drm_encoder_mode_set,
.enable = rockchip_dp_drm_encoder_enable,
.disable = rockchip_dp_drm_encoder_nop,
+ .atomic_check = rockchip_dp_drm_encoder_atomic_check,
};
static void rockchip_dp_drm_encoder_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 7975158064e8..dedc65b40f36 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -879,7 +879,6 @@ static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
int mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, encoder);
- u32 interface_pix_fmt;
u32 val;
if (clk_prepare_enable(dsi->pclk)) {
@@ -895,31 +894,41 @@ static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
clk_disable_unprepare(dsi->pclk);
+ if (mux)
+ val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
+ else
+ val = DSI0_SEL_VOP_LIT << 16;
+
+ regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
+ dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+}
+
+static int
+dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+
switch (dsi->format) {
case MIPI_DSI_FMT_RGB888:
- interface_pix_fmt = ROCKCHIP_OUT_MODE_P888;
+ s->output_mode = ROCKCHIP_OUT_MODE_P888;
break;
case MIPI_DSI_FMT_RGB666:
- interface_pix_fmt = ROCKCHIP_OUT_MODE_P666;
+ s->output_mode = ROCKCHIP_OUT_MODE_P666;
break;
case MIPI_DSI_FMT_RGB565:
- interface_pix_fmt = ROCKCHIP_OUT_MODE_P565;
+ s->output_mode = ROCKCHIP_OUT_MODE_P565;
break;
default:
WARN_ON(1);
- return;
+ return -EINVAL;
}
- rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_DSI,
- interface_pix_fmt);
+ s->output_type = DRM_MODE_CONNECTOR_DSI;
- if (mux)
- val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
- else
- val = DSI0_SEL_VOP_LIT << 16;
-
- regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
- dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+ return 0;
}
static struct drm_encoder_helper_funcs
@@ -927,6 +936,7 @@ dw_mipi_dsi_encoder_helper_funcs = {
.commit = dw_mipi_dsi_encoder_commit,
.mode_set = dw_mipi_dsi_encoder_mode_set,
.disable = dw_mipi_dsi_encoder_disable,
+ .atomic_check = dw_mipi_dsi_encoder_atomic_check,
};
static struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index d5cfef75fc80..801110f65a63 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -201,9 +201,6 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
u32 val;
int mux;
- rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
- ROCKCHIP_OUT_MODE_AAAA);
-
mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
if (mux)
val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
@@ -215,11 +212,25 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
(mux) ? "LIT" : "BIG");
}
+static int
+dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+
+ return 0;
+}
+
static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
.mode_set = dw_hdmi_rockchip_encoder_mode_set,
.enable = dw_hdmi_rockchip_encoder_enable,
.disable = dw_hdmi_rockchip_encoder_disable,
+ .atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
};
static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 10d62fff22f1..f8b4feb60b25 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -500,9 +500,6 @@ static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
{
struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
- rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
- ROCKCHIP_OUT_MODE_P888);
-
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
@@ -520,11 +517,25 @@ static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
+static int
+inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+ s->output_mode = ROCKCHIP_OUT_MODE_P888;
+ s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+
+ return 0;
+}
+
static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
.enable = inno_hdmi_encoder_enable,
.disable = inno_hdmi_encoder_disable,
.mode_fixup = inno_hdmi_encoder_mode_fixup,
.mode_set = inno_hdmi_encoder_mode_set,
+ .atomic_check = inno_hdmi_encoder_atomic_check,
};
static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
@@ -855,8 +866,9 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
if (IS_ERR(hdmi->ddc)) {
+ ret = PTR_ERR(hdmi->ddc);
hdmi->ddc = NULL;
- return PTR_ERR(hdmi->ddc);
+ return ret;
}
/*
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index f556a8f4fde6..399adf3c4224 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -36,6 +36,8 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
+static bool is_support_iommu = true;
+
/*
* Attach a (component) device to the shared drm dma mapping from master drm
* device. This is used by the VOPs to map GEM buffers to a common DMA
@@ -47,6 +49,9 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
int ret;
+ if (!is_support_iommu)
+ return 0;
+
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (ret)
return ret;
@@ -59,6 +64,9 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev)
{
+ if (!is_support_iommu)
+ return;
+
arm_iommu_detach_device(dev);
}
@@ -127,7 +135,7 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev,
static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
{
struct rockchip_drm_private *private;
- struct dma_iommu_mapping *mapping;
+ struct dma_iommu_mapping *mapping = NULL;
struct device *dev = drm_dev->dev;
struct drm_connector *connector;
int ret;
@@ -152,23 +160,26 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
goto err_config_cleanup;
}
- /* TODO(djkurtz): fetch the mapping start/size from somewhere */
- mapping = arm_iommu_create_mapping(&platform_bus_type, 0x00000000,
- SZ_2G);
- if (IS_ERR(mapping)) {
- ret = PTR_ERR(mapping);
- goto err_config_cleanup;
- }
+ if (is_support_iommu) {
+ /* TODO(djkurtz): fetch the mapping start/size from somewhere */
+ mapping = arm_iommu_create_mapping(&platform_bus_type,
+ 0x00000000,
+ SZ_2G);
+ if (IS_ERR(mapping)) {
+ ret = PTR_ERR(mapping);
+ goto err_config_cleanup;
+ }
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret)
- goto err_release_mapping;
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto err_release_mapping;
- dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+ dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
- ret = arm_iommu_attach_device(dev, mapping);
- if (ret)
- goto err_release_mapping;
+ ret = arm_iommu_attach_device(dev, mapping);
+ if (ret)
+ goto err_release_mapping;
+ }
/* Try to bind all sub drivers. */
ret = component_bind_all(dev, drm_dev);
@@ -218,6 +229,8 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
if (ret)
goto err_vblank_cleanup;
+ if (is_support_iommu)
+ arm_iommu_release_mapping(mapping);
return 0;
err_vblank_cleanup:
drm_vblank_cleanup(drm_dev);
@@ -226,9 +239,11 @@ err_kms_helper_poll_fini:
err_unbind:
component_unbind_all(dev, drm_dev);
err_detach_device:
- arm_iommu_detach_device(dev);
+ if (is_support_iommu)
+ arm_iommu_detach_device(dev);
err_release_mapping:
- arm_iommu_release_mapping(dev->archdata.mapping);
+ if (is_support_iommu)
+ arm_iommu_release_mapping(mapping);
err_config_cleanup:
drm_mode_config_cleanup(drm_dev);
drm_dev->dev_private = NULL;
@@ -243,8 +258,8 @@ static int rockchip_drm_unload(struct drm_device *drm_dev)
drm_vblank_cleanup(drm_dev);
drm_kms_helper_poll_fini(drm_dev);
component_unbind_all(dev, drm_dev);
- arm_iommu_detach_device(dev);
- arm_iommu_release_mapping(dev->archdata.mapping);
+ if (is_support_iommu)
+ arm_iommu_detach_device(dev);
drm_mode_config_cleanup(drm_dev);
drm_dev->dev_private = NULL;
@@ -488,6 +503,8 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
* works as expected.
*/
for (i = 0;; i++) {
+ struct device_node *iommu;
+
port = of_parse_phandle(np, "ports", i);
if (!port)
break;
@@ -497,6 +514,17 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
continue;
}
+ iommu = of_parse_phandle(port->parent, "iommus", 0);
+ if (!iommu || !of_device_is_available(iommu->parent)) {
+ dev_dbg(dev, "no iommu attached for %s, using non-iommu buffers\n",
+ port->parent->full_name);
+ /*
+ * if there is a crtc not support iommu, force set all
+ * crtc use non-iommu buffer.
+ */
+ is_support_iommu = false;
+ }
+
component_match_add(dev, &match, compare_of, port->parent);
of_node_put(port);
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 00d17d71aa4c..56f43a364c7f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -50,6 +50,14 @@ struct rockchip_atomic_commit {
struct mutex lock;
};
+struct rockchip_crtc_state {
+ struct drm_crtc_state base;
+ int output_type;
+ int output_mode;
+};
+#define to_rockchip_crtc_state(s) \
+ container_of(s, struct rockchip_crtc_state, base)
+
/*
* Rockchip drm private structure.
*
@@ -68,8 +76,6 @@ void rockchip_drm_atomic_work(struct work_struct *work);
int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
const struct rockchip_crtc_funcs *crtc_funcs);
void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc);
-int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connector_type,
- int out_mode);
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
struct device *dev);
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index a619f120f801..bf55cda356ba 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -310,7 +310,7 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
uint16_t vsu_mode;
uint16_t lb_mode;
uint32_t val;
- int vskiplines;
+ int vskiplines = 0;
if (dst_w > 3840) {
DRM_ERROR("Maximum destination width (3840) exceeded\n");
@@ -560,6 +560,22 @@ static void vop_plane_destroy(struct drm_plane *plane)
drm_plane_cleanup(plane);
}
+static int vop_plane_prepare_fb(struct drm_plane *plane,
+ const struct drm_plane_state *new_state)
+{
+ if (plane->state->fb)
+ drm_framebuffer_reference(plane->state->fb);
+
+ return 0;
+}
+
+static void vop_plane_cleanup_fb(struct drm_plane *plane,
+ const struct drm_plane_state *old_state)
+{
+ if (old_state->fb)
+ drm_framebuffer_unreference(old_state->fb);
+}
+
static int vop_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
@@ -756,6 +772,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs plane_helper_funcs = {
+ .prepare_fb = vop_plane_prepare_fb,
+ .cleanup_fb = vop_plane_cleanup_fb,
.atomic_check = vop_plane_atomic_check,
.atomic_update = vop_plane_atomic_update,
.atomic_disable = vop_plane_atomic_disable,
@@ -818,38 +836,6 @@ static const struct drm_plane_funcs vop_plane_funcs = {
.atomic_destroy_state = vop_atomic_plane_destroy_state,
};
-int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
- int connector_type,
- int out_mode)
-{
- struct vop *vop = to_vop(crtc);
-
- if (WARN_ON(!vop->is_enabled))
- return -EINVAL;
-
- switch (connector_type) {
- case DRM_MODE_CONNECTOR_LVDS:
- VOP_CTRL_SET(vop, rgb_en, 1);
- break;
- case DRM_MODE_CONNECTOR_eDP:
- VOP_CTRL_SET(vop, edp_en, 1);
- break;
- case DRM_MODE_CONNECTOR_HDMIA:
- VOP_CTRL_SET(vop, hdmi_en, 1);
- break;
- case DRM_MODE_CONNECTOR_DSI:
- VOP_CTRL_SET(vop, mipi_en, 1);
- break;
- default:
- DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
- return -EINVAL;
- };
- VOP_CTRL_SET(vop, out_mode, out_mode);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(rockchip_drm_crtc_mode_config);
-
static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
@@ -931,6 +917,7 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
static void vop_crtc_enable(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
u16 hdisplay = adjusted_mode->hdisplay;
@@ -985,6 +972,23 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
VOP_CTRL_SET(vop, pin_pol, val);
+ switch (s->output_type) {
+ case DRM_MODE_CONNECTOR_LVDS:
+ VOP_CTRL_SET(vop, rgb_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_eDP:
+ VOP_CTRL_SET(vop, edp_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA:
+ VOP_CTRL_SET(vop, hdmi_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_DSI:
+ VOP_CTRL_SET(vop, mipi_en, 1);
+ break;
+ default:
+ DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
+ }
+ VOP_CTRL_SET(vop, out_mode, s->output_mode);
VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
val = hact_st << 16;
@@ -1044,13 +1048,34 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
+static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+ struct rockchip_crtc_state *rockchip_state;
+
+ rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL);
+ if (!rockchip_state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base);
+ return &rockchip_state->base;
+}
+
+static void vop_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);
+
+ __drm_atomic_helper_crtc_destroy_state(crtc, &s->base);
+ kfree(s);
+}
+
static const struct drm_crtc_funcs vop_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.destroy = vop_crtc_destroy,
.reset = drm_atomic_helper_crtc_reset,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .atomic_duplicate_state = vop_crtc_duplicate_state,
+ .atomic_destroy_state = vop_crtc_destroy_state,
};
static bool vop_win_pending_is_complete(struct vop_win *vop_win)
diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
new file mode 100644
index 000000000000..99510e64e91a
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -0,0 +1,14 @@
+config DRM_SUN4I
+ tristate "DRM Support for Allwinner A10 Display Engine"
+ depends on DRM && ARM
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_PANEL
+ select REGMAP_MMIO
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you have an Allwinner SoC with a
+ Display Engine. If M is selected the module will be called
+ sun4i-drm.
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
new file mode 100644
index 000000000000..58cd55149827
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -0,0 +1,13 @@
+sun4i-drm-y += sun4i_crtc.o
+sun4i-drm-y += sun4i_drv.o
+sun4i-drm-y += sun4i_framebuffer.o
+sun4i-drm-y += sun4i_layer.o
+
+sun4i-tcon-y += sun4i_tcon.o
+sun4i-tcon-y += sun4i_rgb.o
+sun4i-tcon-y += sun4i_dotclock.o
+
+obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
+obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
+
+obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
new file mode 100644
index 000000000000..f7a15c1a93bf
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include <linux/component.h>
+#include <linux/reset.h>
+
+#include "sun4i_backend.h"
+#include "sun4i_drv.h"
+
+static u32 sunxi_rgb2yuv_coef[12] = {
+ 0x00000107, 0x00000204, 0x00000064, 0x00000108,
+ 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
+ 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
+};
+
+void sun4i_backend_apply_color_correction(struct sun4i_backend *backend)
+{
+ int i;
+
+ DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");
+
+ /* Set color correction */
+ regmap_write(backend->regs, SUN4I_BACKEND_OCCTL_REG,
+ SUN4I_BACKEND_OCCTL_ENABLE);
+
+ for (i = 0; i < 12; i++)
+ regmap_write(backend->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
+ sunxi_rgb2yuv_coef[i]);
+}
+EXPORT_SYMBOL(sun4i_backend_apply_color_correction);
+
+void sun4i_backend_disable_color_correction(struct sun4i_backend *backend)
+{
+ DRM_DEBUG_DRIVER("Disabling color correction\n");
+
+ /* Disable color correction */
+ regmap_update_bits(backend->regs, SUN4I_BACKEND_OCCTL_REG,
+ SUN4I_BACKEND_OCCTL_ENABLE, 0);
+}
+EXPORT_SYMBOL(sun4i_backend_disable_color_correction);
+
+void sun4i_backend_commit(struct sun4i_backend *backend)
+{
+ DRM_DEBUG_DRIVER("Committing changes\n");
+
+ regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
+ SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS |
+ SUN4I_BACKEND_REGBUFFCTL_LOADCTL);
+}
+EXPORT_SYMBOL(sun4i_backend_commit);
+
+void sun4i_backend_layer_enable(struct sun4i_backend *backend,
+ int layer, bool enable)
+{
+ u32 val;
+
+ DRM_DEBUG_DRIVER("Enabling layer %d\n", layer);
+
+ if (enable)
+ val = SUN4I_BACKEND_MODCTL_LAY_EN(layer);
+ else
+ val = 0;
+
+ regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ SUN4I_BACKEND_MODCTL_LAY_EN(layer), val);
+}
+EXPORT_SYMBOL(sun4i_backend_layer_enable);
+
+static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode)
+{
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888;
+ break;
+
+ case DRM_FORMAT_XRGB8888:
+ *mode = SUN4I_BACKEND_LAY_FBFMT_XRGB8888;
+ break;
+
+ case DRM_FORMAT_RGB888:
+ *mode = SUN4I_BACKEND_LAY_FBFMT_RGB888;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
+ int layer, struct drm_plane *plane)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+
+ DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
+
+ if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+ DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
+ state->crtc_w, state->crtc_h);
+ regmap_write(backend->regs, SUN4I_BACKEND_DISSIZE_REG,
+ SUN4I_BACKEND_DISSIZE(state->crtc_w,
+ state->crtc_h));
+ }
+
+ /* Set the line width */
+ DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
+ regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
+ fb->pitches[0] * 8);
+
+ /* Set height and width */
+ DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
+ state->crtc_w, state->crtc_h);
+ regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
+ SUN4I_BACKEND_LAYSIZE(state->crtc_w,
+ state->crtc_h));
+
+ /* Set base coordinates */
+ DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
+ state->crtc_x, state->crtc_y);
+ regmap_write(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
+ SUN4I_BACKEND_LAYCOOR(state->crtc_x,
+ state->crtc_y));
+
+ return 0;
+}
+EXPORT_SYMBOL(sun4i_backend_update_layer_coord);
+
+int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
+ int layer, struct drm_plane *plane)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ bool interlaced = false;
+ u32 val;
+ int ret;
+
+ if (plane->state->crtc)
+ interlaced = plane->state->crtc->state->adjusted_mode.flags
+ & DRM_MODE_FLAG_INTERLACE;
+
+ regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ SUN4I_BACKEND_MODCTL_ITLMOD_EN,
+ interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
+
+ DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
+ interlaced ? "on" : "off");
+
+ ret = sun4i_backend_drm_format_to_layer(fb->pixel_format, &val);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Invalid format\n");
+ return val;
+ }
+
+ regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer),
+ SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
+
+ return 0;
+}
+EXPORT_SYMBOL(sun4i_backend_update_layer_formats);
+
+int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
+ int layer, struct drm_plane *plane)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_gem_cma_object *gem;
+ u32 lo_paddr, hi_paddr;
+ dma_addr_t paddr;
+ int bpp;
+
+ /* Get the physical address of the buffer in memory */
+ gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+ DRM_DEBUG_DRIVER("Using GEM @ 0x%x\n", gem->paddr);
+
+ /* Compute the start of the displayed memory */
+ bpp = drm_format_plane_cpp(fb->pixel_format, 0);
+ paddr = gem->paddr + fb->offsets[0];
+ paddr += (state->src_x >> 16) * bpp;
+ paddr += (state->src_y >> 16) * fb->pitches[0];
+
+ DRM_DEBUG_DRIVER("Setting buffer address to 0x%x\n", paddr);
+
+ /* Write the 32 lower bits of the address (in bits) */
+ lo_paddr = paddr << 3;
+ DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr);
+ regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
+ lo_paddr);
+
+ /* And the upper bits */
+ hi_paddr = paddr >> 29;
+ DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr);
+ regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
+ SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer),
+ SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr));
+
+ return 0;
+}
+EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
+
+static struct regmap_config sun4i_backend_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x5800,
+};
+
+static int sun4i_backend_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_backend *backend;
+ struct resource *res;
+ void __iomem *regs;
+ int i, ret;
+
+ backend = devm_kzalloc(dev, sizeof(*backend), GFP_KERNEL);
+ if (!backend)
+ return -ENOMEM;
+ dev_set_drvdata(dev, backend);
+ drv->backend = backend;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(dev, "Couldn't map the backend registers\n");
+ return PTR_ERR(regs);
+ }
+
+ backend->regs = devm_regmap_init_mmio(dev, regs,
+ &sun4i_backend_regmap_config);
+ if (IS_ERR(backend->regs)) {
+ dev_err(dev, "Couldn't create the backend0 regmap\n");
+ return PTR_ERR(backend->regs);
+ }
+
+ backend->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(backend->reset)) {
+ dev_err(dev, "Couldn't get our reset line\n");
+ return PTR_ERR(backend->reset);
+ }
+
+ ret = reset_control_deassert(backend->reset);
+ if (ret) {
+ dev_err(dev, "Couldn't deassert our reset line\n");
+ return ret;
+ }
+
+ backend->bus_clk = devm_clk_get(dev, "ahb");
+ if (IS_ERR(backend->bus_clk)) {
+ dev_err(dev, "Couldn't get the backend bus clock\n");
+ ret = PTR_ERR(backend->bus_clk);
+ goto err_assert_reset;
+ }
+ clk_prepare_enable(backend->bus_clk);
+
+ backend->mod_clk = devm_clk_get(dev, "mod");
+ if (IS_ERR(backend->mod_clk)) {
+ dev_err(dev, "Couldn't get the backend module clock\n");
+ ret = PTR_ERR(backend->mod_clk);
+ goto err_disable_bus_clk;
+ }
+ clk_prepare_enable(backend->mod_clk);
+
+ backend->ram_clk = devm_clk_get(dev, "ram");
+ if (IS_ERR(backend->ram_clk)) {
+ dev_err(dev, "Couldn't get the backend RAM clock\n");
+ ret = PTR_ERR(backend->ram_clk);
+ goto err_disable_mod_clk;
+ }
+ clk_prepare_enable(backend->ram_clk);
+
+ /* Reset the registers */
+ for (i = 0x800; i < 0x1000; i += 4)
+ regmap_write(backend->regs, i, 0);
+
+ /* Disable registers autoloading */
+ regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
+ SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS);
+
+ /* Enable the backend */
+ regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ SUN4I_BACKEND_MODCTL_DEBE_EN |
+ SUN4I_BACKEND_MODCTL_START_CTL);
+
+ return 0;
+
+err_disable_mod_clk:
+ clk_disable_unprepare(backend->mod_clk);
+err_disable_bus_clk:
+ clk_disable_unprepare(backend->bus_clk);
+err_assert_reset:
+ reset_control_assert(backend->reset);
+ return ret;
+}
+
+static void sun4i_backend_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct sun4i_backend *backend = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(backend->ram_clk);
+ clk_disable_unprepare(backend->mod_clk);
+ clk_disable_unprepare(backend->bus_clk);
+ reset_control_assert(backend->reset);
+}
+
+static struct component_ops sun4i_backend_ops = {
+ .bind = sun4i_backend_bind,
+ .unbind = sun4i_backend_unbind,
+};
+
+static int sun4i_backend_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &sun4i_backend_ops);
+}
+
+static int sun4i_backend_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sun4i_backend_ops);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_backend_of_table[] = {
+ { .compatible = "allwinner,sun5i-a13-display-backend" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
+
+static struct platform_driver sun4i_backend_platform_driver = {
+ .probe = sun4i_backend_probe,
+ .remove = sun4i_backend_remove,
+ .driver = {
+ .name = "sun4i-backend",
+ .of_match_table = sun4i_backend_of_table,
+ },
+};
+module_platform_driver(sun4i_backend_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 Display Backend Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h
new file mode 100644
index 000000000000..7070bb3434e5
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_BACKEND_H_
+#define _SUN4I_BACKEND_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define SUN4I_BACKEND_MODCTL_REG 0x800
+#define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29)
+#define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28)
+#define SUN4I_BACKEND_MODCTL_OUT_SEL GENMASK(22, 20)
+#define SUN4I_BACKEND_MODCTL_OUT_LCD (0 << 20)
+#define SUN4I_BACKEND_MODCTL_OUT_FE0 (6 << 20)
+#define SUN4I_BACKEND_MODCTL_OUT_FE1 (7 << 20)
+#define SUN4I_BACKEND_MODCTL_HWC_EN BIT(16)
+#define SUN4I_BACKEND_MODCTL_LAY_EN(l) BIT(8 + l)
+#define SUN4I_BACKEND_MODCTL_OCSC_EN BIT(5)
+#define SUN4I_BACKEND_MODCTL_DFLK_EN BIT(4)
+#define SUN4I_BACKEND_MODCTL_DLP_START_CTL BIT(2)
+#define SUN4I_BACKEND_MODCTL_START_CTL BIT(1)
+#define SUN4I_BACKEND_MODCTL_DEBE_EN BIT(0)
+
+#define SUN4I_BACKEND_BACKCOLOR_REG 0x804
+#define SUN4I_BACKEND_BACKCOLOR(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+
+#define SUN4I_BACKEND_DISSIZE_REG 0x808
+#define SUN4I_BACKEND_DISSIZE(w, h) (((((h) - 1) & 0xffff) << 16) | \
+ (((w) - 1) & 0xffff))
+
+#define SUN4I_BACKEND_LAYSIZE_REG(l) (0x810 + (0x4 * (l)))
+#define SUN4I_BACKEND_LAYSIZE(w, h) (((((h) - 1) & 0x1fff) << 16) | \
+ (((w) - 1) & 0x1fff))
+
+#define SUN4I_BACKEND_LAYCOOR_REG(l) (0x820 + (0x4 * (l)))
+#define SUN4I_BACKEND_LAYCOOR(x, y) ((((u32)(y) & 0xffff) << 16) | \
+ ((u32)(x) & 0xffff))
+
+#define SUN4I_BACKEND_LAYLINEWIDTH_REG(l) (0x840 + (0x4 * (l)))
+
+#define SUN4I_BACKEND_LAYFB_L32ADD_REG(l) (0x850 + (0x4 * (l)))
+
+#define SUN4I_BACKEND_LAYFB_H4ADD_REG 0x860
+#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), 0)
+#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8))
+
+#define SUN4I_BACKEND_REGBUFFCTL_REG 0x870
+#define SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS BIT(1)
+#define SUN4I_BACKEND_REGBUFFCTL_LOADCTL BIT(0)
+
+#define SUN4I_BACKEND_CKMAX_REG 0x880
+#define SUN4I_BACKEND_CKMIN_REG 0x884
+#define SUN4I_BACKEND_CKCFG_REG 0x888
+#define SUN4I_BACKEND_ATTCTL_REG0(l) (0x890 + (0x4 * (l)))
+#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK BIT(15)
+#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15)
+#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10)
+#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10)
+
+#define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l)))
+#define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT GENMASK(15, 14)
+#define SUN4I_BACKEND_ATTCTL_REG1_LAY_WSCAFCT GENMASK(13, 12)
+#define SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT GENMASK(11, 8)
+#define SUN4I_BACKEND_LAY_FBFMT_1BPP (0 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_2BPP (1 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_4BPP (2 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_8BPP (3 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_RGB655 (4 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_RGB565 (5 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_RGB556 (6 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_ARGB1555 (7 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_RGBA5551 (8 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_XRGB8888 (9 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_ARGB8888 (10 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_RGB888 (11 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_ARGB4444 (12 << 8)
+#define SUN4I_BACKEND_LAY_FBFMT_RGBA4444 (13 << 8)
+
+#define SUN4I_BACKEND_DLCDPCTL_REG 0x8b0
+#define SUN4I_BACKEND_DLCDPFRMBUF_ADDRCTL_REG 0x8b4
+#define SUN4I_BACKEND_DLCDPCOOR_REG0 0x8b8
+#define SUN4I_BACKEND_DLCDPCOOR_REG1 0x8bc
+
+#define SUN4I_BACKEND_INT_EN_REG 0x8c0
+#define SUN4I_BACKEND_INT_FLAG_REG 0x8c4
+#define SUN4I_BACKEND_REG_LOAD_FINISHED BIT(1)
+
+#define SUN4I_BACKEND_HWCCTL_REG 0x8d8
+#define SUN4I_BACKEND_HWCFBCTL_REG 0x8e0
+#define SUN4I_BACKEND_WBCTL_REG 0x8f0
+#define SUN4I_BACKEND_WBADD_REG 0x8f4
+#define SUN4I_BACKEND_WBLINEWIDTH_REG 0x8f8
+#define SUN4I_BACKEND_SPREN_REG 0x900
+#define SUN4I_BACKEND_SPRFMTCTL_REG 0x908
+#define SUN4I_BACKEND_SPRALPHACTL_REG 0x90c
+#define SUN4I_BACKEND_IYUVCTL_REG 0x920
+#define SUN4I_BACKEND_IYUVADD_REG(c) (0x930 + (0x4 * (c)))
+#define SUN4I_BACKEND_IYUVLINEWITDTH_REG(c) (0x940 + (0x4 * (c)))
+#define SUN4I_BACKEND_YGCOEF_REG(c) (0x950 + (0x4 * (c)))
+#define SUN4I_BACKEND_YGCONS_REG 0x95c
+#define SUN4I_BACKEND_URCOEF_REG(c) (0x960 + (0x4 * (c)))
+#define SUN4I_BACKEND_URCONS_REG 0x96c
+#define SUN4I_BACKEND_VBCOEF_REG(c) (0x970 + (0x4 * (c)))
+#define SUN4I_BACKEND_VBCONS_REG 0x97c
+#define SUN4I_BACKEND_KSCTL_REG 0x980
+#define SUN4I_BACKEND_KSBKCOLOR_REG 0x984
+#define SUN4I_BACKEND_KSFSTLINEWIDTH_REG 0x988
+#define SUN4I_BACKEND_KSVSCAFCT_REG 0x98c
+#define SUN4I_BACKEND_KSHSCACOEF_REG(x) (0x9a0 + (0x4 * (x)))
+#define SUN4I_BACKEND_OCCTL_REG 0x9c0
+#define SUN4I_BACKEND_OCCTL_ENABLE BIT(0)
+
+#define SUN4I_BACKEND_OCRCOEF_REG(x) (0x9d0 + (0x4 * (x)))
+#define SUN4I_BACKEND_OCRCONS_REG 0x9dc
+#define SUN4I_BACKEND_OCGCOEF_REG(x) (0x9e0 + (0x4 * (x)))
+#define SUN4I_BACKEND_OCGCONS_REG 0x9ec
+#define SUN4I_BACKEND_OCBCOEF_REG(x) (0x9f0 + (0x4 * (x)))
+#define SUN4I_BACKEND_OCBCONS_REG 0x9fc
+#define SUN4I_BACKEND_SPRCOORCTL_REG(s) (0xa00 + (0x4 * (s)))
+#define SUN4I_BACKEND_SPRATTCTL_REG(s) (0xb00 + (0x4 * (s)))
+#define SUN4I_BACKEND_SPRADD_REG(s) (0xc00 + (0x4 * (s)))
+#define SUN4I_BACKEND_SPRLINEWIDTH_REG(s) (0xd00 + (0x4 * (s)))
+
+#define SUN4I_BACKEND_SPRPALTAB_OFF 0x4000
+#define SUN4I_BACKEND_GAMMATAB_OFF 0x4400
+#define SUN4I_BACKEND_HWCPATTERN_OFF 0x4800
+#define SUN4I_BACKEND_HWCCOLORTAB_OFF 0x4c00
+#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p)))
+
+struct sun4i_backend {
+ struct regmap *regs;
+
+ struct reset_control *reset;
+
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+ struct clk *ram_clk;
+};
+
+void sun4i_backend_apply_color_correction(struct sun4i_backend *backend);
+void sun4i_backend_disable_color_correction(struct sun4i_backend *backend);
+
+void sun4i_backend_commit(struct sun4i_backend *backend);
+
+void sun4i_backend_layer_enable(struct sun4i_backend *backend,
+ int layer, bool enable);
+int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
+ int layer, struct drm_plane *plane);
+int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
+ int layer, struct drm_plane *plane);
+int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
+ int layer, struct drm_plane *plane);
+
+#endif /* _SUN4I_BACKEND_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
new file mode 100644
index 000000000000..4182a21f5923
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_modes.h>
+
+#include <linux/clk-provider.h>
+#include <linux/ioport.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+
+#include <video/videomode.h>
+
+#include "sun4i_backend.h"
+#include "sun4i_crtc.h"
+#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
+
+static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ unsigned long flags;
+
+ if (crtc->state->event) {
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ scrtc->event = crtc->state->event;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ crtc->state->event = NULL;
+ }
+}
+
+static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
+ struct sun4i_drv *drv = scrtc->drv;
+
+ DRM_DEBUG_DRIVER("Committing plane changes\n");
+
+ sun4i_backend_commit(drv->backend);
+}
+
+static void sun4i_crtc_disable(struct drm_crtc *crtc)
+{
+ struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
+ struct sun4i_drv *drv = scrtc->drv;
+
+ DRM_DEBUG_DRIVER("Disabling the CRTC\n");
+
+ sun4i_tcon_disable(drv->tcon);
+}
+
+static void sun4i_crtc_enable(struct drm_crtc *crtc)
+{
+ struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
+ struct sun4i_drv *drv = scrtc->drv;
+
+ DRM_DEBUG_DRIVER("Enabling the CRTC\n");
+
+ sun4i_tcon_enable(drv->tcon);
+}
+
+static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
+ .atomic_begin = sun4i_crtc_atomic_begin,
+ .atomic_flush = sun4i_crtc_atomic_flush,
+ .disable = sun4i_crtc_disable,
+ .enable = sun4i_crtc_enable,
+};
+
+static const struct drm_crtc_funcs sun4i_crtc_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .destroy = drm_crtc_cleanup,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .set_config = drm_atomic_helper_set_config,
+};
+
+struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_crtc *scrtc;
+ int ret;
+
+ scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
+ if (!scrtc)
+ return NULL;
+ scrtc->drv = drv;
+
+ ret = drm_crtc_init_with_planes(drm, &scrtc->crtc,
+ drv->primary,
+ NULL,
+ &sun4i_crtc_funcs,
+ NULL);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't init DRM CRTC\n");
+ return NULL;
+ }
+
+ drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
+
+ return scrtc;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.h b/drivers/gpu/drm/sun4i/sun4i_crtc.h
new file mode 100644
index 000000000000..dec8ce4d9b25
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_CRTC_H_
+#define _SUN4I_CRTC_H_
+
+struct sun4i_crtc {
+ struct drm_crtc crtc;
+ struct drm_pending_vblank_event *event;
+
+ struct sun4i_drv *drv;
+};
+
+static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc *crtc)
+{
+ return container_of(crtc, struct sun4i_crtc, crtc);
+}
+
+struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm);
+
+#endif /* _SUN4I_CRTC_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.c b/drivers/gpu/drm/sun4i/sun4i_dotclock.c
new file mode 100644
index 000000000000..3ff668cb463c
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 Free Electrons
+ * Copyright (C) 2016 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+#include "sun4i_tcon.h"
+
+struct sun4i_dclk {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw)
+{
+ return container_of(hw, struct sun4i_dclk, hw);
+}
+
+static void sun4i_dclk_disable(struct clk_hw *hw)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+
+ regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
+ BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0);
+}
+
+static int sun4i_dclk_enable(struct clk_hw *hw)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+
+ return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
+ BIT(SUN4I_TCON0_DCLK_GATE_BIT),
+ BIT(SUN4I_TCON0_DCLK_GATE_BIT));
+}
+
+static int sun4i_dclk_is_enabled(struct clk_hw *hw)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+ u32 val;
+
+ regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
+
+ return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT);
+}
+
+static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+ u32 val;
+
+ regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
+
+ val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
+ val &= SUN4I_TCON0_DCLK_DIV_WIDTH;
+
+ if (!val)
+ val = 1;
+
+ return parent_rate / val;
+}
+
+static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return *parent_rate / DIV_ROUND_CLOSEST(*parent_rate, rate);
+}
+
+static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+ int div = DIV_ROUND_CLOSEST(parent_rate, rate);
+
+ return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
+ GENMASK(6, 0), div);
+}
+
+static int sun4i_dclk_get_phase(struct clk_hw *hw)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+ u32 val;
+
+ regmap_read(dclk->regmap, SUN4I_TCON0_IO_POL_REG, &val);
+
+ val >>= 28;
+ val &= 3;
+
+ return val * 120;
+}
+
+static int sun4i_dclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sun4i_dclk *dclk = hw_to_dclk(hw);
+
+ regmap_update_bits(dclk->regmap, SUN4I_TCON0_IO_POL_REG,
+ GENMASK(29, 28),
+ degrees / 120);
+
+ return 0;
+}
+
+static const struct clk_ops sun4i_dclk_ops = {
+ .disable = sun4i_dclk_disable,
+ .enable = sun4i_dclk_enable,
+ .is_enabled = sun4i_dclk_is_enabled,
+
+ .recalc_rate = sun4i_dclk_recalc_rate,
+ .round_rate = sun4i_dclk_round_rate,
+ .set_rate = sun4i_dclk_set_rate,
+
+ .get_phase = sun4i_dclk_get_phase,
+ .set_phase = sun4i_dclk_set_phase,
+};
+
+int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
+{
+ const char *clk_name, *parent_name;
+ struct clk_init_data init;
+ struct sun4i_dclk *dclk;
+
+ parent_name = __clk_get_name(tcon->sclk0);
+ of_property_read_string_index(dev->of_node, "clock-output-names", 0,
+ &clk_name);
+
+ dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
+ if (!dclk)
+ return -ENOMEM;
+
+ init.name = clk_name;
+ init.ops = &sun4i_dclk_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ dclk->regmap = tcon->regs;
+ dclk->hw.init = &init;
+
+ tcon->dclk = clk_register(dev, &dclk->hw);
+ if (IS_ERR(tcon->dclk))
+ return PTR_ERR(tcon->dclk);
+
+ return 0;
+}
+EXPORT_SYMBOL(sun4i_dclk_create);
+
+int sun4i_dclk_free(struct sun4i_tcon *tcon)
+{
+ clk_unregister(tcon->dclk);
+ return 0;
+}
+EXPORT_SYMBOL(sun4i_dclk_free);
diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.h b/drivers/gpu/drm/sun4i/sun4i_dotclock.h
new file mode 100644
index 000000000000..d5e25fa9eff1
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_DOTCLOCK_H_
+#define _SUN4I_DOTCLOCK_H_
+
+struct sun4i_tcon;
+
+int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon);
+int sun4i_dclk_free(struct sun4i_tcon *tcon);
+
+#endif /* _SUN4I_DOTCLOCK_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
new file mode 100644
index 000000000000..76e922bb60e5
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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/component.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "sun4i_crtc.h"
+#include "sun4i_drv.h"
+#include "sun4i_framebuffer.h"
+#include "sun4i_layer.h"
+#include "sun4i_tcon.h"
+
+static int sun4i_drv_connector_plug_all(struct drm_device *drm)
+{
+ struct drm_connector *connector, *failed;
+ int ret;
+
+ mutex_lock(&drm->mode_config.mutex);
+ list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
+ ret = drm_connector_register(connector);
+ if (ret) {
+ failed = connector;
+ goto err;
+ }
+ }
+ mutex_unlock(&drm->mode_config.mutex);
+ return 0;
+
+err:
+ list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
+ if (failed == connector)
+ break;
+
+ drm_connector_unregister(connector);
+ }
+ mutex_unlock(&drm->mode_config.mutex);
+
+ return ret;
+}
+
+static int sun4i_drv_enable_vblank(struct drm_device *drm, unsigned int pipe)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Enabling VBLANK on pipe %d\n", pipe);
+
+ sun4i_tcon_enable_vblank(tcon, true);
+
+ return 0;
+}
+
+static void sun4i_drv_disable_vblank(struct drm_device *drm, unsigned int pipe)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Disabling VBLANK on pipe %d\n", pipe);
+
+ sun4i_tcon_enable_vblank(tcon, false);
+}
+
+static const struct file_operations sun4i_drv_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static struct drm_driver sun4i_drv_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
+
+ /* Generic Operations */
+ .fops = &sun4i_drv_fops,
+ .name = "sun4i-drm",
+ .desc = "Allwinner sun4i Display Engine",
+ .date = "20150629",
+ .major = 1,
+ .minor = 0,
+
+ /* GEM Operations */
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+
+ /* PRIME Operations */
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+
+ /* Frame Buffer Operations */
+
+ /* VBlank Operations */
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = sun4i_drv_enable_vblank,
+ .disable_vblank = sun4i_drv_disable_vblank,
+};
+
+static int sun4i_drv_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ struct sun4i_drv *drv;
+ int ret;
+
+ drm = drm_dev_alloc(&sun4i_drv_driver, dev);
+ if (!drm)
+ return -ENOMEM;
+
+ ret = drm_dev_set_unique(drm, dev_name(drm->dev));
+ if (ret)
+ goto free_drm;
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv) {
+ ret = -ENOMEM;
+ goto free_drm;
+ }
+ drm->dev_private = drv;
+
+ drm_vblank_init(drm, 1);
+ drm_mode_config_init(drm);
+
+ ret = component_bind_all(drm->dev, drm);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't bind all pipelines components\n");
+ goto free_drm;
+ }
+
+ /* Create our layers */
+ drv->layers = sun4i_layers_init(drm);
+ if (!drv->layers) {
+ dev_err(drm->dev, "Couldn't create the planes\n");
+ ret = -EINVAL;
+ goto free_drm;
+ }
+
+ /* Create our CRTC */
+ drv->crtc = sun4i_crtc_init(drm);
+ if (!drv->crtc) {
+ dev_err(drm->dev, "Couldn't create the CRTC\n");
+ ret = -EINVAL;
+ goto free_drm;
+ }
+ drm->irq_enabled = true;
+
+ /* Create our framebuffer */
+ drv->fbdev = sun4i_framebuffer_init(drm);
+ if (IS_ERR(drv->fbdev)) {
+ dev_err(drm->dev, "Couldn't create our framebuffer\n");
+ ret = PTR_ERR(drv->fbdev);
+ goto free_drm;
+ }
+
+ /* Enable connectors polling */
+ drm_kms_helper_poll_init(drm);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto free_drm;
+
+ ret = sun4i_drv_connector_plug_all(drm);
+ if (ret)
+ goto unregister_drm;
+
+ return 0;
+
+unregister_drm:
+ drm_dev_unregister(drm);
+free_drm:
+ drm_dev_unref(drm);
+ return ret;
+}
+
+static void sun4i_drv_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ drm_dev_unregister(drm);
+ drm_kms_helper_poll_fini(drm);
+ sun4i_framebuffer_free(drm);
+ drm_vblank_cleanup(drm);
+ drm_dev_unref(drm);
+}
+
+static const struct component_master_ops sun4i_drv_master_ops = {
+ .bind = sun4i_drv_bind,
+ .unbind = sun4i_drv_unbind,
+};
+
+static bool sun4i_drv_node_is_frontend(struct device_node *node)
+{
+ return of_device_is_compatible(node,
+ "allwinner,sun5i-a13-display-frontend");
+}
+
+static bool sun4i_drv_node_is_tcon(struct device_node *node)
+{
+ return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon");
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+ DRM_DEBUG_DRIVER("Comparing of node %s with %s\n",
+ of_node_full_name(dev->of_node),
+ of_node_full_name(data));
+
+ return dev->of_node == data;
+}
+
+static int sun4i_drv_add_endpoints(struct device *dev,
+ struct component_match **match,
+ struct device_node *node)
+{
+ struct device_node *port, *ep, *remote;
+ int count = 0;
+
+ /*
+ * We don't support the frontend for now, so we will never
+ * have a device bound. Just skip over it, but we still want
+ * the rest our pipeline to be added.
+ */
+ if (!sun4i_drv_node_is_frontend(node) &&
+ !of_device_is_available(node))
+ return 0;
+
+ if (!sun4i_drv_node_is_frontend(node)) {
+ /* Add current component */
+ DRM_DEBUG_DRIVER("Adding component %s\n",
+ of_node_full_name(node));
+ component_match_add(dev, match, compare_of, node);
+ count++;
+ }
+
+ /* Inputs are listed first, then outputs */
+ port = of_graph_get_port_by_id(node, 1);
+ if (!port) {
+ DRM_DEBUG_DRIVER("No output to bind\n");
+ return count;
+ }
+
+ for_each_available_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote) {
+ DRM_DEBUG_DRIVER("Error retrieving the output node\n");
+ of_node_put(remote);
+ continue;
+ }
+
+ /*
+ * If the node is our TCON, the first port is used for our
+ * panel, and will not be part of the
+ * component framework.
+ */
+ if (sun4i_drv_node_is_tcon(node)) {
+ struct of_endpoint endpoint;
+
+ if (of_graph_parse_endpoint(ep, &endpoint)) {
+ DRM_DEBUG_DRIVER("Couldn't parse endpoint\n");
+ continue;
+ }
+
+ if (!endpoint.id) {
+ DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n");
+ continue;
+ }
+ }
+
+ /* Walk down our tree */
+ count += sun4i_drv_add_endpoints(dev, match, remote);
+
+ of_node_put(remote);
+ }
+
+ return count;
+}
+
+static int sun4i_drv_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ int i, count = 0;
+
+ for (i = 0;; i++) {
+ struct device_node *pipeline = of_parse_phandle(np,
+ "allwinner,pipelines",
+ i);
+ if (!pipeline)
+ break;
+
+ count += sun4i_drv_add_endpoints(&pdev->dev, &match,
+ pipeline);
+
+ DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
+ count, i);
+ }
+
+ if (count)
+ return component_master_add_with_match(&pdev->dev,
+ &sun4i_drv_master_ops,
+ match);
+ else
+ return 0;
+}
+
+static int sun4i_drv_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id sun4i_drv_of_table[] = {
+ { .compatible = "allwinner,sun5i-a13-display-engine" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
+
+static struct platform_driver sun4i_drv_platform_driver = {
+ .probe = sun4i_drv_probe,
+ .remove = sun4i_drv_remove,
+ .driver = {
+ .name = "sun4i-drm",
+ .of_match_table = sun4i_drv_of_table,
+ },
+};
+module_platform_driver(sun4i_drv_platform_driver);
+
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 Display Engine DRM/KMS Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
new file mode 100644
index 000000000000..597353eab728
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_DRV_H_
+#define _SUN4I_DRV_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+struct sun4i_drv {
+ struct sun4i_backend *backend;
+ struct sun4i_crtc *crtc;
+ struct sun4i_tcon *tcon;
+
+ struct drm_plane *primary;
+ struct drm_fbdev_cma *fbdev;
+
+ struct sun4i_layer **layers;
+};
+
+#endif /* _SUN4I_DRV_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c
new file mode 100644
index 000000000000..a0b30c216a5b
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drmP.h>
+
+#include "sun4i_drv.h"
+
+static void sun4i_de_output_poll_changed(struct drm_device *drm)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+
+ if (drv->fbdev)
+ drm_fbdev_cma_hotplug_event(drv->fbdev);
+}
+
+static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = {
+ .output_poll_changed = sun4i_de_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+ .fb_create = drm_fb_cma_create,
+};
+
+struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm)
+{
+ drm_mode_config_reset(drm);
+
+ drm->mode_config.max_width = 8192;
+ drm->mode_config.max_height = 8192;
+
+ drm->mode_config.funcs = &sun4i_de_mode_config_funcs;
+
+ return drm_fbdev_cma_init(drm, 32,
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+}
+
+void sun4i_framebuffer_free(struct drm_device *drm)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+
+ drm_fbdev_cma_fini(drv->fbdev);
+ drm_mode_config_cleanup(drm);
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.h b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h
new file mode 100644
index 000000000000..3afd65252ee0
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_FRAMEBUFFER_H_
+#define _SUN4I_FRAMEBUFFER_H_
+
+struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm);
+void sun4i_framebuffer_free(struct drm_device *drm);
+
+#endif /* _SUN4I_FRAMEBUFFER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
new file mode 100644
index 000000000000..068ab806309b
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drmP.h>
+
+#include "sun4i_backend.h"
+#include "sun4i_drv.h"
+#include "sun4i_layer.h"
+
+#define SUN4I_NUM_LAYERS 2
+
+static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ return 0;
+}
+
+static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
+ struct sun4i_drv *drv = layer->drv;
+ struct sun4i_backend *backend = drv->backend;
+
+ sun4i_backend_layer_enable(backend, layer->id, false);
+}
+
+static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
+ struct sun4i_drv *drv = layer->drv;
+ struct sun4i_backend *backend = drv->backend;
+
+ sun4i_backend_update_layer_coord(backend, layer->id, plane);
+ sun4i_backend_update_layer_formats(backend, layer->id, plane);
+ sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+ sun4i_backend_layer_enable(backend, layer->id, true);
+}
+
+static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
+ .atomic_check = sun4i_backend_layer_atomic_check,
+ .atomic_disable = sun4i_backend_layer_atomic_disable,
+ .atomic_update = sun4i_backend_layer_atomic_update,
+};
+
+static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .destroy = drm_plane_cleanup,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .update_plane = drm_atomic_helper_update_plane,
+};
+
+static const uint32_t sun4i_backend_layer_formats[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB888,
+};
+
+static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
+ enum drm_plane_type type)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_layer *layer;
+ int ret;
+
+ layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
+ if (!layer)
+ return ERR_PTR(-ENOMEM);
+
+ ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
+ &sun4i_backend_layer_funcs,
+ sun4i_backend_layer_formats,
+ ARRAY_SIZE(sun4i_backend_layer_formats),
+ type,
+ NULL);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't initialize layer\n");
+ return ERR_PTR(ret);
+ }
+
+ drm_plane_helper_add(&layer->plane,
+ &sun4i_backend_layer_helper_funcs);
+ layer->drv = drv;
+
+ if (type == DRM_PLANE_TYPE_PRIMARY)
+ drv->primary = &layer->plane;
+
+ return layer;
+}
+
+struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_layer **layers;
+ int i;
+
+ layers = devm_kcalloc(drm->dev, SUN4I_NUM_LAYERS, sizeof(**layers),
+ GFP_KERNEL);
+ if (!layers)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * The hardware is a bit unusual here.
+ *
+ * Even though it supports 4 layers, it does the composition
+ * in two separate steps.
+ *
+ * The first one is assigning a layer to one of its two
+ * pipes. If more that 1 layer is assigned to the same pipe,
+ * and if pixels overlaps, the pipe will take the pixel from
+ * the layer with the highest priority.
+ *
+ * The second step is the actual alpha blending, that takes
+ * the two pipes as input, and uses the eventual alpha
+ * component to do the transparency between the two.
+ *
+ * This two steps scenario makes us unable to guarantee a
+ * robust alpha blending between the 4 layers in all
+ * situations. So we just expose two layers, one per pipe. On
+ * SoCs that support it, sprites could fill the need for more
+ * layers.
+ */
+ for (i = 0; i < SUN4I_NUM_LAYERS; i++) {
+ enum drm_plane_type type = (i == 0)
+ ? DRM_PLANE_TYPE_PRIMARY
+ : DRM_PLANE_TYPE_OVERLAY;
+ struct sun4i_layer *layer = layers[i];
+
+ layer = sun4i_layer_init_one(drm, type);
+ if (IS_ERR(layer)) {
+ dev_err(drm->dev, "Couldn't initialize %s plane\n",
+ i ? "overlay" : "primary");
+ return ERR_CAST(layer);
+ };
+
+ DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
+ i ? "overlay" : "primary", i);
+ regmap_update_bits(drv->backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
+ SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
+ SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(i));
+
+ layer->id = i;
+ };
+
+ return layers;
+}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
new file mode 100644
index 000000000000..a2f65d7a3f4e
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_LAYER_H_
+#define _SUN4I_LAYER_H_
+
+struct sun4i_layer {
+ struct drm_plane plane;
+ struct sun4i_drv *drv;
+ int id;
+};
+
+static inline struct sun4i_layer *
+plane_to_sun4i_layer(struct drm_plane *plane)
+{
+ return container_of(plane, struct sun4i_layer, plane);
+}
+
+struct sun4i_layer **sun4i_layers_init(struct drm_device *drm);
+
+#endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
new file mode 100644
index 000000000000..ab6494818050
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
+
+struct sun4i_rgb {
+ struct drm_connector connector;
+ struct drm_encoder encoder;
+
+ struct sun4i_drv *drv;
+};
+
+static inline struct sun4i_rgb *
+drm_connector_to_sun4i_rgb(struct drm_connector *connector)
+{
+ return container_of(connector, struct sun4i_rgb,
+ connector);
+}
+
+static inline struct sun4i_rgb *
+drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct sun4i_rgb,
+ encoder);
+}
+
+static int sun4i_rgb_get_modes(struct drm_connector *connector)
+{
+ struct sun4i_rgb *rgb =
+ drm_connector_to_sun4i_rgb(connector);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ return drm_panel_get_modes(tcon->panel);
+}
+
+static int sun4i_rgb_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ u32 hsync = mode->hsync_end - mode->hsync_start;
+ u32 vsync = mode->vsync_end - mode->vsync_start;
+
+ DRM_DEBUG_DRIVER("Validating modes...\n");
+
+ if (hsync < 1)
+ return MODE_HSYNC_NARROW;
+
+ if (hsync > 0x3ff)
+ return MODE_HSYNC_WIDE;
+
+ if ((mode->hdisplay < 1) || (mode->htotal < 1))
+ return MODE_H_ILLEGAL;
+
+ if ((mode->hdisplay > 0x7ff) || (mode->htotal > 0xfff))
+ return MODE_BAD_HVALUE;
+
+ DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
+
+ if (vsync < 1)
+ return MODE_VSYNC_NARROW;
+
+ if (vsync > 0x3ff)
+ return MODE_VSYNC_WIDE;
+
+ if ((mode->vdisplay < 1) || (mode->vtotal < 1))
+ return MODE_V_ILLEGAL;
+
+ if ((mode->vdisplay > 0x7ff) || (mode->vtotal > 0xfff))
+ return MODE_BAD_VVALUE;
+
+ DRM_DEBUG_DRIVER("Vertical parameters OK\n");
+
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+sun4i_rgb_best_encoder(struct drm_connector *connector)
+{
+ struct sun4i_rgb *rgb =
+ drm_connector_to_sun4i_rgb(connector);
+
+ return &rgb->encoder;
+}
+
+static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = {
+ .get_modes = sun4i_rgb_get_modes,
+ .mode_valid = sun4i_rgb_mode_valid,
+ .best_encoder = sun4i_rgb_best_encoder,
+};
+
+static enum drm_connector_status
+sun4i_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void
+sun4i_rgb_connector_destroy(struct drm_connector *connector)
+{
+ struct sun4i_rgb *rgb = drm_connector_to_sun4i_rgb(connector);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ drm_panel_detach(tcon->panel);
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs sun4i_rgb_con_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = sun4i_rgb_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = sun4i_rgb_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int sun4i_rgb_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ return 0;
+}
+
+static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+ struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Enabling RGB output\n");
+
+ drm_panel_enable(tcon->panel);
+ sun4i_tcon_channel_enable(tcon, 0);
+}
+
+static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+ struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Disabling RGB output\n");
+
+ sun4i_tcon_channel_disable(tcon, 0);
+ drm_panel_disable(tcon->panel);
+}
+
+static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
+ struct sun4i_drv *drv = rgb->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ sun4i_tcon0_mode_set(tcon, mode);
+
+ clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
+
+ /* FIXME: This seems to be board specific */
+ clk_set_phase(tcon->dclk, 120);
+}
+
+static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
+ .atomic_check = sun4i_rgb_atomic_check,
+ .mode_set = sun4i_rgb_encoder_mode_set,
+ .disable = sun4i_rgb_encoder_disable,
+ .enable = sun4i_rgb_encoder_enable,
+};
+
+static void sun4i_rgb_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs sun4i_rgb_enc_funcs = {
+ .destroy = sun4i_rgb_enc_destroy,
+};
+
+int sun4i_rgb_init(struct drm_device *drm)
+{
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tcon *tcon = drv->tcon;
+ struct sun4i_rgb *rgb;
+ int ret;
+
+ /* If we don't have a panel, there's no point in going on */
+ if (!tcon->panel)
+ return -ENODEV;
+
+ rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL);
+ if (!rgb)
+ return -ENOMEM;
+ rgb->drv = drv;
+
+ drm_encoder_helper_add(&rgb->encoder,
+ &sun4i_rgb_enc_helper_funcs);
+ ret = drm_encoder_init(drm,
+ &rgb->encoder,
+ &sun4i_rgb_enc_funcs,
+ DRM_MODE_ENCODER_NONE,
+ NULL);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't initialise the rgb encoder\n");
+ goto err_out;
+ }
+
+ /* The RGB encoder can only work with the TCON channel 0 */
+ rgb->encoder.possible_crtcs = BIT(0);
+
+ drm_connector_helper_add(&rgb->connector,
+ &sun4i_rgb_con_helper_funcs);
+ ret = drm_connector_init(drm, &rgb->connector,
+ &sun4i_rgb_con_funcs,
+ DRM_MODE_CONNECTOR_Unknown);
+ if (ret) {
+ dev_err(drm->dev, "Couldn't initialise the rgb connector\n");
+ goto err_cleanup_connector;
+ }
+
+ drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder);
+
+ drm_panel_attach(tcon->panel, &rgb->connector);
+
+ return 0;
+
+err_cleanup_connector:
+ drm_encoder_cleanup(&rgb->encoder);
+err_out:
+ return ret;
+}
+EXPORT_SYMBOL(sun4i_rgb_init);
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.h b/drivers/gpu/drm/sun4i/sun4i_rgb.h
new file mode 100644
index 000000000000..7c4da4c8acdd
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.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 _SUN4I_RGB_H_
+#define _SUN4I_RGB_H_
+
+int sun4i_rgb_init(struct drm_device *drm);
+
+#endif /* _SUN4I_RGB_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
new file mode 100644
index 000000000000..9f19b0e08560
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <linux/component.h>
+#include <linux/ioport.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "sun4i_crtc.h"
+#include "sun4i_dotclock.h"
+#include "sun4i_drv.h"
+#include "sun4i_rgb.h"
+#include "sun4i_tcon.h"
+
+void sun4i_tcon_disable(struct sun4i_tcon *tcon)
+{
+ DRM_DEBUG_DRIVER("Disabling TCON\n");
+
+ /* Disable the TCON */
+ regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
+ SUN4I_TCON_GCTL_TCON_ENABLE, 0);
+}
+EXPORT_SYMBOL(sun4i_tcon_disable);
+
+void sun4i_tcon_enable(struct sun4i_tcon *tcon)
+{
+ DRM_DEBUG_DRIVER("Enabling TCON\n");
+
+ /* Enable the TCON */
+ regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
+ SUN4I_TCON_GCTL_TCON_ENABLE,
+ SUN4I_TCON_GCTL_TCON_ENABLE);
+}
+EXPORT_SYMBOL(sun4i_tcon_enable);
+
+void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
+{
+ /* Disable the TCON's channel */
+ if (channel == 0) {
+ regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
+ SUN4I_TCON0_CTL_TCON_ENABLE, 0);
+ clk_disable_unprepare(tcon->dclk);
+ } else if (channel == 1) {
+ regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+ SUN4I_TCON1_CTL_TCON_ENABLE, 0);
+ clk_disable_unprepare(tcon->sclk1);
+ }
+}
+EXPORT_SYMBOL(sun4i_tcon_channel_disable);
+
+void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
+{
+ /* Enable the TCON's channel */
+ if (channel == 0) {
+ regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
+ SUN4I_TCON0_CTL_TCON_ENABLE,
+ SUN4I_TCON0_CTL_TCON_ENABLE);
+ clk_prepare_enable(tcon->dclk);
+ } else if (channel == 1) {
+ regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+ SUN4I_TCON1_CTL_TCON_ENABLE,
+ SUN4I_TCON1_CTL_TCON_ENABLE);
+ clk_prepare_enable(tcon->sclk1);
+ }
+}
+EXPORT_SYMBOL(sun4i_tcon_channel_enable);
+
+void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
+{
+ u32 mask, val = 0;
+
+ DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis");
+
+ mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) |
+ SUN4I_TCON_GINT0_VBLANK_ENABLE(1);
+
+ if (enable)
+ val = mask;
+
+ regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val);
+}
+EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
+
+static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode,
+ int channel)
+{
+ int delay = mode->vtotal - mode->vdisplay;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ delay /= 2;
+
+ if (channel == 1)
+ delay -= 2;
+
+ delay = min(delay, 30);
+
+ DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay);
+
+ return delay;
+}
+
+void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
+ struct drm_display_mode *mode)
+{
+ unsigned int bp, hsync, vsync;
+ u8 clk_delay;
+ u32 val = 0;
+
+ /* Adjust clock delay */
+ clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
+ regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
+ SUN4I_TCON0_CTL_CLK_DELAY_MASK,
+ SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
+
+ /* Set the resolution */
+ regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,
+ SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |
+ SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
+
+ /*
+ * This is called a backporch in the register documentation,
+ * but it really is the front porch + hsync
+ */
+ bp = mode->crtc_htotal - mode->crtc_hsync_start;
+ DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
+ mode->crtc_htotal, bp);
+
+ /* Set horizontal display timings */
+ regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
+ SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) |
+ SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
+
+ /*
+ * This is called a backporch in the register documentation,
+ * but it really is the front porch + hsync
+ */
+ bp = mode->crtc_vtotal - mode->crtc_vsync_start;
+ DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
+ mode->crtc_vtotal, bp);
+
+ /* Set vertical display timings */
+ regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
+ SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal) |
+ SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
+
+ /* Set Hsync and Vsync length */
+ hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
+ regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG,
+ SUN4I_TCON0_BASIC3_V_SYNC(vsync) |
+ SUN4I_TCON0_BASIC3_H_SYNC(hsync));
+
+ /* Setup the polarity of the various signals */
+ if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+ val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
+
+ if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+ val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
+
+ regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG,
+ SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE,
+ val);
+
+ /* Map output pins to channel 0 */
+ regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
+ SUN4I_TCON_GCTL_IOMAP_MASK,
+ SUN4I_TCON_GCTL_IOMAP_TCON0);
+
+ /* Enable the output on the pins */
+ regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0);
+}
+EXPORT_SYMBOL(sun4i_tcon0_mode_set);
+
+void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
+ struct drm_display_mode *mode)
+{
+ unsigned int bp, hsync, vsync;
+ u8 clk_delay;
+ u32 val;
+
+ /* Adjust clock delay */
+ clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
+ regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+ SUN4I_TCON1_CTL_CLK_DELAY_MASK,
+ SUN4I_TCON1_CTL_CLK_DELAY(clk_delay));
+
+ /* Set interlaced mode */
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ val = SUN4I_TCON1_CTL_INTERLACE_ENABLE;
+ else
+ val = 0;
+ regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+ SUN4I_TCON1_CTL_INTERLACE_ENABLE,
+ val);
+
+ /* Set the input resolution */
+ regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG,
+ SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) |
+ SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay));
+
+ /* Set the upscaling resolution */
+ regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG,
+ SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) |
+ SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay));
+
+ /* Set the output resolution */
+ regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG,
+ SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) |
+ SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
+
+ /* Set horizontal display timings */
+ bp = mode->crtc_htotal - mode->crtc_hsync_end;
+ DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
+ mode->htotal, bp);
+ regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
+ SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |
+ SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
+
+ /* Set vertical display timings */
+ bp = mode->crtc_vtotal - mode->crtc_vsync_end;
+ DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
+ mode->vtotal, bp);
+ regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
+ SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal) |
+ SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));
+
+ /* Set Hsync and Vsync length */
+ hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
+ regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG,
+ SUN4I_TCON1_BASIC5_V_SYNC(vsync) |
+ SUN4I_TCON1_BASIC5_H_SYNC(hsync));
+
+ /* Map output pins to channel 1 */
+ regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
+ SUN4I_TCON_GCTL_IOMAP_MASK,
+ SUN4I_TCON_GCTL_IOMAP_TCON1);
+
+ /*
+ * FIXME: Undocumented bits
+ */
+ if (tcon->has_mux)
+ regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
+}
+EXPORT_SYMBOL(sun4i_tcon1_mode_set);
+
+static void sun4i_tcon_finish_page_flip(struct drm_device *dev,
+ struct sun4i_crtc *scrtc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (scrtc->event) {
+ drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event);
+ drm_crtc_vblank_put(&scrtc->crtc);
+ scrtc->event = NULL;
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static irqreturn_t sun4i_tcon_handler(int irq, void *private)
+{
+ struct sun4i_tcon *tcon = private;
+ struct drm_device *drm = tcon->drm;
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_crtc *scrtc = drv->crtc;
+ unsigned int status;
+
+ regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
+
+ if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) |
+ SUN4I_TCON_GINT0_VBLANK_INT(1))))
+ return IRQ_NONE;
+
+ drm_crtc_handle_vblank(&scrtc->crtc);
+ sun4i_tcon_finish_page_flip(drm, scrtc);
+
+ /* Acknowledge the interrupt */
+ regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG,
+ SUN4I_TCON_GINT0_VBLANK_INT(0) |
+ SUN4I_TCON_GINT0_VBLANK_INT(1),
+ 0);
+
+ return IRQ_HANDLED;
+}
+
+static int sun4i_tcon_init_clocks(struct device *dev,
+ struct sun4i_tcon *tcon)
+{
+ tcon->clk = devm_clk_get(dev, "ahb");
+ if (IS_ERR(tcon->clk)) {
+ dev_err(dev, "Couldn't get the TCON bus clock\n");
+ return PTR_ERR(tcon->clk);
+ }
+ clk_prepare_enable(tcon->clk);
+
+ tcon->sclk0 = devm_clk_get(dev, "tcon-ch0");
+ if (IS_ERR(tcon->sclk0)) {
+ dev_err(dev, "Couldn't get the TCON channel 0 clock\n");
+ return PTR_ERR(tcon->sclk0);
+ }
+
+ tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
+ if (IS_ERR(tcon->sclk1)) {
+ dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
+ return PTR_ERR(tcon->sclk1);
+ }
+
+ return sun4i_dclk_create(dev, tcon);
+}
+
+static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
+{
+ sun4i_dclk_free(tcon);
+ clk_disable_unprepare(tcon->clk);
+}
+
+static int sun4i_tcon_init_irq(struct device *dev,
+ struct sun4i_tcon *tcon)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq, ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "Couldn't retrieve the TCON interrupt\n");
+ return irq;
+ }
+
+ ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0,
+ dev_name(dev), tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't request the IRQ\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct regmap_config sun4i_tcon_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x800,
+};
+
+static int sun4i_tcon_init_regmap(struct device *dev,
+ struct sun4i_tcon *tcon)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *res;
+ void __iomem *regs;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(dev, "Couldn't map the TCON registers\n");
+ return PTR_ERR(regs);
+ }
+
+ tcon->regs = devm_regmap_init_mmio(dev, regs,
+ &sun4i_tcon_regmap_config);
+ if (IS_ERR(tcon->regs)) {
+ dev_err(dev, "Couldn't create the TCON regmap\n");
+ return PTR_ERR(tcon->regs);
+ }
+
+ /* Make sure the TCON is disabled and all IRQs are off */
+ regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0);
+ regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);
+ regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0);
+
+ /* Disable IO lines and set them to tristate */
+ regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0);
+ regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0);
+
+ return 0;
+}
+
+static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
+{
+ struct device_node *port, *remote, *child;
+ struct device_node *end_node = NULL;
+
+ /* Inputs are listed first, then outputs */
+ port = of_graph_get_port_by_id(node, 1);
+
+ /*
+ * Our first output is the RGB interface where the panel will
+ * be connected.
+ */
+ for_each_child_of_node(port, child) {
+ u32 reg;
+
+ of_property_read_u32(child, "reg", &reg);
+ if (reg == 0)
+ end_node = child;
+ }
+
+ if (!end_node) {
+ DRM_DEBUG_DRIVER("Missing panel endpoint\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ remote = of_graph_get_remote_port_parent(end_node);
+ if (!remote) {
+ DRM_DEBUG_DRIVER("Enable to parse remote node\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return of_drm_find_panel(remote);
+}
+
+static int sun4i_tcon_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm = data;
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tcon *tcon;
+ int ret;
+
+ tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
+ if (!tcon)
+ return -ENOMEM;
+ dev_set_drvdata(dev, tcon);
+ drv->tcon = tcon;
+ tcon->drm = drm;
+
+ if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon"))
+ tcon->has_mux = true;
+
+ tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
+ if (IS_ERR(tcon->lcd_rst)) {
+ dev_err(dev, "Couldn't get our reset line\n");
+ return PTR_ERR(tcon->lcd_rst);
+ }
+
+ /* Make sure our TCON is reset */
+ if (!reset_control_status(tcon->lcd_rst))
+ reset_control_assert(tcon->lcd_rst);
+
+ ret = reset_control_deassert(tcon->lcd_rst);
+ if (ret) {
+ dev_err(dev, "Couldn't deassert our reset line\n");
+ return ret;
+ }
+
+ ret = sun4i_tcon_init_regmap(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't init our TCON regmap\n");
+ goto err_assert_reset;
+ }
+
+ ret = sun4i_tcon_init_clocks(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't init our TCON clocks\n");
+ goto err_assert_reset;
+ }
+
+ ret = sun4i_tcon_init_irq(dev, tcon);
+ if (ret) {
+ dev_err(dev, "Couldn't init our TCON interrupts\n");
+ goto err_free_clocks;
+ }
+
+ tcon->panel = sun4i_tcon_find_panel(dev->of_node);
+ if (IS_ERR(tcon->panel)) {
+ dev_info(dev, "No panel found... RGB output disabled\n");
+ return 0;
+ }
+
+ return sun4i_rgb_init(drm);
+
+err_free_clocks:
+ sun4i_tcon_free_clocks(tcon);
+err_assert_reset:
+ reset_control_assert(tcon->lcd_rst);
+ return ret;
+}
+
+static void sun4i_tcon_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct sun4i_tcon *tcon = dev_get_drvdata(dev);
+
+ sun4i_tcon_free_clocks(tcon);
+}
+
+static struct component_ops sun4i_tcon_ops = {
+ .bind = sun4i_tcon_bind,
+ .unbind = sun4i_tcon_unbind,
+};
+
+static int sun4i_tcon_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct drm_panel *panel;
+
+ /*
+ * The panel is not ready.
+ * Defer the probe.
+ */
+ panel = sun4i_tcon_find_panel(node);
+ if (IS_ERR(panel)) {
+ /*
+ * If we don't have a panel endpoint, just go on
+ */
+ if (PTR_ERR(panel) != -ENODEV)
+ return -EPROBE_DEFER;
+ }
+
+ return component_add(&pdev->dev, &sun4i_tcon_ops);
+}
+
+static int sun4i_tcon_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sun4i_tcon_ops);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_tcon_of_table[] = {
+ { .compatible = "allwinner,sun5i-a13-tcon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
+
+static struct platform_driver sun4i_tcon_platform_driver = {
+ .probe = sun4i_tcon_probe,
+ .remove = sun4i_tcon_remove,
+ .driver = {
+ .name = "sun4i-tcon",
+ .of_match_table = sun4i_tcon_of_table,
+ },
+};
+module_platform_driver(sun4i_tcon_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
new file mode 100644
index 000000000000..0e0b11db401b
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Maxime Ripard <maxime.ripard@free-electrons.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 __SUN4I_TCON_H__
+#define __SUN4I_TCON_H__
+
+#include <drm/drm_crtc.h>
+
+#include <linux/kernel.h>
+#include <linux/reset.h>
+
+#define SUN4I_TCON_GCTL_REG 0x0
+#define SUN4I_TCON_GCTL_TCON_ENABLE BIT(31)
+#define SUN4I_TCON_GCTL_IOMAP_MASK BIT(0)
+#define SUN4I_TCON_GCTL_IOMAP_TCON1 (1 << 0)
+#define SUN4I_TCON_GCTL_IOMAP_TCON0 (0 << 0)
+
+#define SUN4I_TCON_GINT0_REG 0x4
+#define SUN4I_TCON_GINT0_VBLANK_ENABLE(pipe) BIT(31 - (pipe))
+#define SUN4I_TCON_GINT0_VBLANK_INT(pipe) BIT(15 - (pipe))
+
+#define SUN4I_TCON_GINT1_REG 0x8
+#define SUN4I_TCON_FRM_CTL_REG 0x10
+
+#define SUN4I_TCON0_CTL_REG 0x40
+#define SUN4I_TCON0_CTL_TCON_ENABLE BIT(31)
+#define SUN4I_TCON0_CTL_CLK_DELAY_MASK GENMASK(8, 4)
+#define SUN4I_TCON0_CTL_CLK_DELAY(delay) ((delay << 4) & SUN4I_TCON0_CTL_CLK_DELAY_MASK)
+
+#define SUN4I_TCON0_DCLK_REG 0x44
+#define SUN4I_TCON0_DCLK_GATE_BIT (31)
+#define SUN4I_TCON0_DCLK_DIV_SHIFT (0)
+#define SUN4I_TCON0_DCLK_DIV_WIDTH (7)
+
+#define SUN4I_TCON0_BASIC0_REG 0x48
+#define SUN4I_TCON0_BASIC0_X(width) ((((width) - 1) & 0xfff) << 16)
+#define SUN4I_TCON0_BASIC0_Y(height) (((height) - 1) & 0xfff)
+
+#define SUN4I_TCON0_BASIC1_REG 0x4c
+#define SUN4I_TCON0_BASIC1_H_TOTAL(total) ((((total) - 1) & 0x1fff) << 16)
+#define SUN4I_TCON0_BASIC1_H_BACKPORCH(bp) (((bp) - 1) & 0xfff)
+
+#define SUN4I_TCON0_BASIC2_REG 0x50
+#define SUN4I_TCON0_BASIC2_V_TOTAL(total) ((((total) * 2) & 0x1fff) << 16)
+#define SUN4I_TCON0_BASIC2_V_BACKPORCH(bp) (((bp) - 1) & 0xfff)
+
+#define SUN4I_TCON0_BASIC3_REG 0x54
+#define SUN4I_TCON0_BASIC3_H_SYNC(width) ((((width) - 1) & 0x7ff) << 16)
+#define SUN4I_TCON0_BASIC3_V_SYNC(height) (((height) - 1) & 0x7ff)
+
+#define SUN4I_TCON0_HV_IF_REG 0x58
+#define SUN4I_TCON0_CPU_IF_REG 0x60
+#define SUN4I_TCON0_CPU_WR_REG 0x64
+#define SUN4I_TCON0_CPU_RD0_REG 0x68
+#define SUN4I_TCON0_CPU_RDA_REG 0x6c
+#define SUN4I_TCON0_TTL0_REG 0x70
+#define SUN4I_TCON0_TTL1_REG 0x74
+#define SUN4I_TCON0_TTL2_REG 0x78
+#define SUN4I_TCON0_TTL3_REG 0x7c
+#define SUN4I_TCON0_TTL4_REG 0x80
+#define SUN4I_TCON0_LVDS_IF_REG 0x84
+#define SUN4I_TCON0_IO_POL_REG 0x88
+#define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase) ((phase & 3) << 28)
+#define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE BIT(25)
+#define SUN4I_TCON0_IO_POL_VSYNC_POSITIVE BIT(24)
+
+#define SUN4I_TCON0_IO_TRI_REG 0x8c
+#define SUN4I_TCON0_IO_TRI_HSYNC_DISABLE BIT(25)
+#define SUN4I_TCON0_IO_TRI_VSYNC_DISABLE BIT(24)
+#define SUN4I_TCON0_IO_TRI_DATA_PINS_DISABLE(pins) GENMASK(pins, 0)
+
+#define SUN4I_TCON1_CTL_REG 0x90
+#define SUN4I_TCON1_CTL_TCON_ENABLE BIT(31)
+#define SUN4I_TCON1_CTL_INTERLACE_ENABLE BIT(20)
+#define SUN4I_TCON1_CTL_CLK_DELAY_MASK GENMASK(8, 4)
+#define SUN4I_TCON1_CTL_CLK_DELAY(delay) ((delay << 4) & SUN4I_TCON1_CTL_CLK_DELAY_MASK)
+
+#define SUN4I_TCON1_BASIC0_REG 0x94
+#define SUN4I_TCON1_BASIC0_X(width) ((((width) - 1) & 0xfff) << 16)
+#define SUN4I_TCON1_BASIC0_Y(height) (((height) - 1) & 0xfff)
+
+#define SUN4I_TCON1_BASIC1_REG 0x98
+#define SUN4I_TCON1_BASIC1_X(width) ((((width) - 1) & 0xfff) << 16)
+#define SUN4I_TCON1_BASIC1_Y(height) (((height) - 1) & 0xfff)
+
+#define SUN4I_TCON1_BASIC2_REG 0x9c
+#define SUN4I_TCON1_BASIC2_X(width) ((((width) - 1) & 0xfff) << 16)
+#define SUN4I_TCON1_BASIC2_Y(height) (((height) - 1) & 0xfff)
+
+#define SUN4I_TCON1_BASIC3_REG 0xa0
+#define SUN4I_TCON1_BASIC3_H_TOTAL(total) ((((total) - 1) & 0x1fff) << 16)
+#define SUN4I_TCON1_BASIC3_H_BACKPORCH(bp) (((bp) - 1) & 0xfff)
+
+#define SUN4I_TCON1_BASIC4_REG 0xa4
+#define SUN4I_TCON1_BASIC4_V_TOTAL(total) (((total) & 0x1fff) << 16)
+#define SUN4I_TCON1_BASIC4_V_BACKPORCH(bp) (((bp) - 1) & 0xfff)
+
+#define SUN4I_TCON1_BASIC5_REG 0xa8
+#define SUN4I_TCON1_BASIC5_H_SYNC(width) ((((width) - 1) & 0x3ff) << 16)
+#define SUN4I_TCON1_BASIC5_V_SYNC(height) (((height) - 1) & 0x3ff)
+
+#define SUN4I_TCON1_IO_POL_REG 0xf0
+#define SUN4I_TCON1_IO_TRI_REG 0xf4
+#define SUN4I_TCON_CEU_CTL_REG 0x100
+#define SUN4I_TCON_CEU_MUL_RR_REG 0x110
+#define SUN4I_TCON_CEU_MUL_RG_REG 0x114
+#define SUN4I_TCON_CEU_MUL_RB_REG 0x118
+#define SUN4I_TCON_CEU_ADD_RC_REG 0x11c
+#define SUN4I_TCON_CEU_MUL_GR_REG 0x120
+#define SUN4I_TCON_CEU_MUL_GG_REG 0x124
+#define SUN4I_TCON_CEU_MUL_GB_REG 0x128
+#define SUN4I_TCON_CEU_ADD_GC_REG 0x12c
+#define SUN4I_TCON_CEU_MUL_BR_REG 0x130
+#define SUN4I_TCON_CEU_MUL_BG_REG 0x134
+#define SUN4I_TCON_CEU_MUL_BB_REG 0x138
+#define SUN4I_TCON_CEU_ADD_BC_REG 0x13c
+#define SUN4I_TCON_CEU_RANGE_R_REG 0x140
+#define SUN4I_TCON_CEU_RANGE_G_REG 0x144
+#define SUN4I_TCON_CEU_RANGE_B_REG 0x148
+#define SUN4I_TCON_MUX_CTRL_REG 0x200
+#define SUN4I_TCON1_FILL_CTL_REG 0x300
+#define SUN4I_TCON1_FILL_BEG0_REG 0x304
+#define SUN4I_TCON1_FILL_END0_REG 0x308
+#define SUN4I_TCON1_FILL_DATA0_REG 0x30c
+#define SUN4I_TCON1_FILL_BEG1_REG 0x310
+#define SUN4I_TCON1_FILL_END1_REG 0x314
+#define SUN4I_TCON1_FILL_DATA1_REG 0x318
+#define SUN4I_TCON1_FILL_BEG2_REG 0x31c
+#define SUN4I_TCON1_FILL_END2_REG 0x320
+#define SUN4I_TCON1_FILL_DATA2_REG 0x324
+#define SUN4I_TCON1_GAMMA_TABLE_REG 0x400
+
+#define SUN4I_TCON_MAX_CHANNELS 2
+
+struct sun4i_tcon {
+ struct drm_device *drm;
+ struct regmap *regs;
+
+ /* Main bus clock */
+ struct clk *clk;
+
+ /* Clocks for the TCON channels */
+ struct clk *sclk0;
+ struct clk *sclk1;
+
+ /* Pixel clock */
+ struct clk *dclk;
+
+ /* Reset control */
+ struct reset_control *lcd_rst;
+
+ /* Platform adjustments */
+ bool has_mux;
+
+ struct drm_panel *panel;
+};
+
+/* Global Control */
+void sun4i_tcon_disable(struct sun4i_tcon *tcon);
+void sun4i_tcon_enable(struct sun4i_tcon *tcon);
+
+/* Channel Control */
+void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel);
+void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel);
+
+void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
+
+/* Mode Related Controls */
+void sun4i_tcon_switch_interlace(struct sun4i_tcon *tcon,
+ bool enable);
+void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
+ struct drm_display_mode *mode);
+void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
+ struct drm_display_mode *mode);
+
+#endif /* __SUN4I_TCON_H__ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
new file mode 100644
index 000000000000..bc047f923508
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2015 Free Electrons
+ * Copyright (C) 2015 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include "sun4i_backend.h"
+#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
+
+#define SUN4I_TVE_EN_REG 0x000
+#define SUN4I_TVE_EN_DAC_MAP_MASK GENMASK(19, 4)
+#define SUN4I_TVE_EN_DAC_MAP(dac, out) (((out) & 0xf) << (dac + 1) * 4)
+#define SUN4I_TVE_EN_ENABLE BIT(0)
+
+#define SUN4I_TVE_CFG0_REG 0x004
+#define SUN4I_TVE_CFG0_DAC_CONTROL_54M BIT(26)
+#define SUN4I_TVE_CFG0_CORE_DATAPATH_54M BIT(25)
+#define SUN4I_TVE_CFG0_CORE_CONTROL_54M BIT(24)
+#define SUN4I_TVE_CFG0_YC_EN BIT(17)
+#define SUN4I_TVE_CFG0_COMP_EN BIT(16)
+#define SUN4I_TVE_CFG0_RES(x) ((x) & 0xf)
+#define SUN4I_TVE_CFG0_RES_480i SUN4I_TVE_CFG0_RES(0)
+#define SUN4I_TVE_CFG0_RES_576i SUN4I_TVE_CFG0_RES(1)
+
+#define SUN4I_TVE_DAC0_REG 0x008
+#define SUN4I_TVE_DAC0_CLOCK_INVERT BIT(24)
+#define SUN4I_TVE_DAC0_LUMA(x) (((x) & 3) << 20)
+#define SUN4I_TVE_DAC0_LUMA_0_4 SUN4I_TVE_DAC0_LUMA(3)
+#define SUN4I_TVE_DAC0_CHROMA(x) (((x) & 3) << 18)
+#define SUN4I_TVE_DAC0_CHROMA_0_75 SUN4I_TVE_DAC0_CHROMA(3)
+#define SUN4I_TVE_DAC0_INTERNAL_DAC(x) (((x) & 3) << 16)
+#define SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS SUN4I_TVE_DAC0_INTERNAL_DAC(3)
+#define SUN4I_TVE_DAC0_DAC_EN(dac) BIT(dac)
+
+#define SUN4I_TVE_NOTCH_REG 0x00c
+#define SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(dac, x) ((4 - (x)) << (dac * 3))
+
+#define SUN4I_TVE_CHROMA_FREQ_REG 0x010
+
+#define SUN4I_TVE_PORCH_REG 0x014
+#define SUN4I_TVE_PORCH_BACK(x) ((x) << 16)
+#define SUN4I_TVE_PORCH_FRONT(x) (x)
+
+#define SUN4I_TVE_LINE_REG 0x01c
+#define SUN4I_TVE_LINE_FIRST(x) ((x) << 16)
+#define SUN4I_TVE_LINE_NUMBER(x) (x)
+
+#define SUN4I_TVE_LEVEL_REG 0x020
+#define SUN4I_TVE_LEVEL_BLANK(x) ((x) << 16)
+#define SUN4I_TVE_LEVEL_BLACK(x) (x)
+
+#define SUN4I_TVE_DAC1_REG 0x024
+#define SUN4I_TVE_DAC1_AMPLITUDE(dac, x) ((x) << (dac * 8))
+
+#define SUN4I_TVE_DETECT_STA_REG 0x038
+#define SUN4I_TVE_DETECT_STA_DAC(dac) BIT((dac * 8))
+#define SUN4I_TVE_DETECT_STA_UNCONNECTED 0
+#define SUN4I_TVE_DETECT_STA_CONNECTED 1
+#define SUN4I_TVE_DETECT_STA_GROUND 2
+
+#define SUN4I_TVE_CB_CR_LVL_REG 0x10c
+#define SUN4I_TVE_CB_CR_LVL_CR_BURST(x) ((x) << 8)
+#define SUN4I_TVE_CB_CR_LVL_CB_BURST(x) (x)
+
+#define SUN4I_TVE_TINT_BURST_PHASE_REG 0x110
+#define SUN4I_TVE_TINT_BURST_PHASE_CHROMA(x) (x)
+
+#define SUN4I_TVE_BURST_WIDTH_REG 0x114
+#define SUN4I_TVE_BURST_WIDTH_BREEZEWAY(x) ((x) << 16)
+#define SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(x) ((x) << 8)
+#define SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(x) (x)
+
+#define SUN4I_TVE_CB_CR_GAIN_REG 0x118
+#define SUN4I_TVE_CB_CR_GAIN_CR(x) ((x) << 8)
+#define SUN4I_TVE_CB_CR_GAIN_CB(x) (x)
+
+#define SUN4I_TVE_SYNC_VBI_REG 0x11c
+#define SUN4I_TVE_SYNC_VBI_SYNC(x) ((x) << 16)
+#define SUN4I_TVE_SYNC_VBI_VBLANK(x) (x)
+
+#define SUN4I_TVE_ACTIVE_LINE_REG 0x124
+#define SUN4I_TVE_ACTIVE_LINE(x) (x)
+
+#define SUN4I_TVE_CHROMA_REG 0x128
+#define SUN4I_TVE_CHROMA_COMP_GAIN(x) ((x) & 3)
+#define SUN4I_TVE_CHROMA_COMP_GAIN_50 SUN4I_TVE_CHROMA_COMP_GAIN(2)
+
+#define SUN4I_TVE_12C_REG 0x12c
+#define SUN4I_TVE_12C_NOTCH_WIDTH_WIDE BIT(8)
+#define SUN4I_TVE_12C_COMP_YUV_EN BIT(0)
+
+#define SUN4I_TVE_RESYNC_REG 0x130
+#define SUN4I_TVE_RESYNC_FIELD BIT(31)
+#define SUN4I_TVE_RESYNC_LINE(x) ((x) << 16)
+#define SUN4I_TVE_RESYNC_PIXEL(x) (x)
+
+#define SUN4I_TVE_SLAVE_REG 0x134
+
+#define SUN4I_TVE_WSS_DATA2_REG 0x244
+
+struct color_gains {
+ u16 cb;
+ u16 cr;
+};
+
+struct burst_levels {
+ u16 cb;
+ u16 cr;
+};
+
+struct video_levels {
+ u16 black;
+ u16 blank;
+};
+
+struct resync_parameters {
+ bool field;
+ u16 line;
+ u16 pixel;
+};
+
+struct tv_mode {
+ char *name;
+
+ u32 mode;
+ u32 chroma_freq;
+ u16 back_porch;
+ u16 front_porch;
+ u16 line_number;
+ u16 vblank_level;
+
+ u32 hdisplay;
+ u16 hfront_porch;
+ u16 hsync_len;
+ u16 hback_porch;
+
+ u32 vdisplay;
+ u16 vfront_porch;
+ u16 vsync_len;
+ u16 vback_porch;
+
+ bool yc_en;
+ bool dac3_en;
+ bool dac_bit25_en;
+
+ struct color_gains *color_gains;
+ struct burst_levels *burst_levels;
+ struct video_levels *video_levels;
+ struct resync_parameters *resync_params;
+};
+
+struct sun4i_tv {
+ struct drm_connector connector;
+ struct drm_encoder encoder;
+
+ struct clk *clk;
+ struct regmap *regs;
+ struct reset_control *reset;
+
+ struct sun4i_drv *drv;
+};
+
+struct video_levels ntsc_video_levels = {
+ .black = 282, .blank = 240,
+};
+
+struct video_levels pal_video_levels = {
+ .black = 252, .blank = 252,
+};
+
+struct burst_levels ntsc_burst_levels = {
+ .cb = 79, .cr = 0,
+};
+
+struct burst_levels pal_burst_levels = {
+ .cb = 40, .cr = 40,
+};
+
+struct color_gains ntsc_color_gains = {
+ .cb = 160, .cr = 160,
+};
+
+struct color_gains pal_color_gains = {
+ .cb = 224, .cr = 224,
+};
+
+struct resync_parameters ntsc_resync_parameters = {
+ .field = false, .line = 14, .pixel = 12,
+};
+
+struct resync_parameters pal_resync_parameters = {
+ .field = true, .line = 13, .pixel = 12,
+};
+
+struct tv_mode tv_modes[] = {
+ {
+ .name = "NTSC",
+ .mode = SUN4I_TVE_CFG0_RES_480i,
+ .chroma_freq = 0x21f07c1f,
+ .yc_en = true,
+ .dac3_en = true,
+ .dac_bit25_en = true,
+
+ .back_porch = 118,
+ .front_porch = 32,
+ .line_number = 525,
+
+ .hdisplay = 720,
+ .hfront_porch = 18,
+ .hsync_len = 2,
+ .hback_porch = 118,
+
+ .vdisplay = 480,
+ .vfront_porch = 26,
+ .vsync_len = 2,
+ .vback_porch = 17,
+
+ .vblank_level = 240,
+
+ .color_gains = &ntsc_color_gains,
+ .burst_levels = &ntsc_burst_levels,
+ .video_levels = &ntsc_video_levels,
+ .resync_params = &ntsc_resync_parameters,
+ },
+ {
+ .name = "PAL",
+ .mode = SUN4I_TVE_CFG0_RES_576i,
+ .chroma_freq = 0x2a098acb,
+
+ .back_porch = 138,
+ .front_porch = 24,
+ .line_number = 625,
+
+ .hdisplay = 720,
+ .hfront_porch = 3,
+ .hsync_len = 2,
+ .hback_porch = 139,
+
+ .vdisplay = 576,
+ .vfront_porch = 28,
+ .vsync_len = 2,
+ .vback_porch = 19,
+
+ .vblank_level = 252,
+
+ .color_gains = &pal_color_gains,
+ .burst_levels = &pal_burst_levels,
+ .video_levels = &pal_video_levels,
+ .resync_params = &pal_resync_parameters,
+ },
+};
+
+static inline struct sun4i_tv *
+drm_encoder_to_sun4i_tv(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct sun4i_tv,
+ encoder);
+}
+
+static inline struct sun4i_tv *
+drm_connector_to_sun4i_tv(struct drm_connector *connector)
+{
+ return container_of(connector, struct sun4i_tv,
+ connector);
+}
+
+/*
+ * FIXME: If only the drm_display_mode private field was usable, this
+ * could go away...
+ *
+ * So far, it doesn't seem to be preserved when the mode is passed by
+ * to mode_set for some reason.
+ */
+static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
+{
+ int i;
+
+ /* First try to identify the mode by name */
+ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+ struct tv_mode *tv_mode = &tv_modes[i];
+
+ DRM_DEBUG_DRIVER("Comparing mode %s vs %s",
+ mode->name, tv_mode->name);
+
+ if (!strcmp(mode->name, tv_mode->name))
+ return tv_mode;
+ }
+
+ /* Then by number of lines */
+ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+ struct tv_mode *tv_mode = &tv_modes[i];
+
+ DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",
+ mode->name, tv_mode->name,
+ mode->vdisplay, tv_mode->vdisplay);
+
+ if (mode->vdisplay == tv_mode->vdisplay)
+ return tv_mode;
+ }
+
+ return NULL;
+}
+
+static void sun4i_tv_mode_to_drm_mode(struct tv_mode *tv_mode,
+ struct drm_display_mode *mode)
+{
+ DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name);
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ mode->clock = 13500;
+ mode->flags = DRM_MODE_FLAG_INTERLACE;
+
+ mode->hdisplay = tv_mode->hdisplay;
+ mode->hsync_start = mode->hdisplay + tv_mode->hfront_porch;
+ mode->hsync_end = mode->hsync_start + tv_mode->hsync_len;
+ mode->htotal = mode->hsync_end + tv_mode->hback_porch;
+
+ mode->vdisplay = tv_mode->vdisplay;
+ mode->vsync_start = mode->vdisplay + tv_mode->vfront_porch;
+ mode->vsync_end = mode->vsync_start + tv_mode->vsync_len;
+ mode->vtotal = mode->vsync_end + tv_mode->vback_porch;
+}
+
+static int sun4i_tv_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ return 0;
+}
+
+static void sun4i_tv_disable(struct drm_encoder *encoder)
+{
+ struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
+ struct sun4i_drv *drv = tv->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Disabling the TV Output\n");
+
+ sun4i_tcon_channel_disable(tcon, 1);
+
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_ENABLE,
+ 0);
+ sun4i_backend_disable_color_correction(drv->backend);
+}
+
+static void sun4i_tv_enable(struct drm_encoder *encoder)
+{
+ struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
+ struct sun4i_drv *drv = tv->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+
+ DRM_DEBUG_DRIVER("Enabling the TV Output\n");
+
+ sun4i_backend_apply_color_correction(drv->backend);
+
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_ENABLE,
+ SUN4I_TVE_EN_ENABLE);
+
+ sun4i_tcon_channel_enable(tcon, 1);
+}
+
+static void sun4i_tv_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
+ struct sun4i_drv *drv = tv->drv;
+ struct sun4i_tcon *tcon = drv->tcon;
+ struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
+
+ sun4i_tcon1_mode_set(tcon, mode);
+
+ /* Enable and map the DAC to the output */
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_DAC_MAP_MASK,
+ SUN4I_TVE_EN_DAC_MAP(0, 1) |
+ SUN4I_TVE_EN_DAC_MAP(1, 2) |
+ SUN4I_TVE_EN_DAC_MAP(2, 3) |
+ SUN4I_TVE_EN_DAC_MAP(3, 4));
+
+ /* Set PAL settings */
+ regmap_write(tv->regs, SUN4I_TVE_CFG0_REG,
+ tv_mode->mode |
+ (tv_mode->yc_en ? SUN4I_TVE_CFG0_YC_EN : 0) |
+ SUN4I_TVE_CFG0_COMP_EN |
+ SUN4I_TVE_CFG0_DAC_CONTROL_54M |
+ SUN4I_TVE_CFG0_CORE_DATAPATH_54M |
+ SUN4I_TVE_CFG0_CORE_CONTROL_54M);
+
+ /* Configure the DAC for a composite output */
+ regmap_write(tv->regs, SUN4I_TVE_DAC0_REG,
+ SUN4I_TVE_DAC0_DAC_EN(0) |
+ (tv_mode->dac3_en ? SUN4I_TVE_DAC0_DAC_EN(3) : 0) |
+ SUN4I_TVE_DAC0_INTERNAL_DAC_37_5_OHMS |
+ SUN4I_TVE_DAC0_CHROMA_0_75 |
+ SUN4I_TVE_DAC0_LUMA_0_4 |
+ SUN4I_TVE_DAC0_CLOCK_INVERT |
+ (tv_mode->dac_bit25_en ? BIT(25) : 0) |
+ BIT(30));
+
+ /* Configure the sample delay between DAC0 and the other DAC */
+ regmap_write(tv->regs, SUN4I_TVE_NOTCH_REG,
+ SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(1, 0) |
+ SUN4I_TVE_NOTCH_DAC0_TO_DAC_DLY(2, 0));
+
+ regmap_write(tv->regs, SUN4I_TVE_CHROMA_FREQ_REG,
+ tv_mode->chroma_freq);
+
+ /* Set the front and back porch */
+ regmap_write(tv->regs, SUN4I_TVE_PORCH_REG,
+ SUN4I_TVE_PORCH_BACK(tv_mode->back_porch) |
+ SUN4I_TVE_PORCH_FRONT(tv_mode->front_porch));
+
+ /* Set the lines setup */
+ regmap_write(tv->regs, SUN4I_TVE_LINE_REG,
+ SUN4I_TVE_LINE_FIRST(22) |
+ SUN4I_TVE_LINE_NUMBER(tv_mode->line_number));
+
+ regmap_write(tv->regs, SUN4I_TVE_LEVEL_REG,
+ SUN4I_TVE_LEVEL_BLANK(tv_mode->video_levels->blank) |
+ SUN4I_TVE_LEVEL_BLACK(tv_mode->video_levels->black));
+
+ regmap_write(tv->regs, SUN4I_TVE_DAC1_REG,
+ SUN4I_TVE_DAC1_AMPLITUDE(0, 0x18) |
+ SUN4I_TVE_DAC1_AMPLITUDE(1, 0x18) |
+ SUN4I_TVE_DAC1_AMPLITUDE(2, 0x18) |
+ SUN4I_TVE_DAC1_AMPLITUDE(3, 0x18));
+
+ regmap_write(tv->regs, SUN4I_TVE_CB_CR_LVL_REG,
+ SUN4I_TVE_CB_CR_LVL_CB_BURST(tv_mode->burst_levels->cb) |
+ SUN4I_TVE_CB_CR_LVL_CR_BURST(tv_mode->burst_levels->cr));
+
+ /* Set burst width for a composite output */
+ regmap_write(tv->regs, SUN4I_TVE_BURST_WIDTH_REG,
+ SUN4I_TVE_BURST_WIDTH_HSYNC_WIDTH(126) |
+ SUN4I_TVE_BURST_WIDTH_BURST_WIDTH(68) |
+ SUN4I_TVE_BURST_WIDTH_BREEZEWAY(22));
+
+ regmap_write(tv->regs, SUN4I_TVE_CB_CR_GAIN_REG,
+ SUN4I_TVE_CB_CR_GAIN_CB(tv_mode->color_gains->cb) |
+ SUN4I_TVE_CB_CR_GAIN_CR(tv_mode->color_gains->cr));
+
+ regmap_write(tv->regs, SUN4I_TVE_SYNC_VBI_REG,
+ SUN4I_TVE_SYNC_VBI_SYNC(0x10) |
+ SUN4I_TVE_SYNC_VBI_VBLANK(tv_mode->vblank_level));
+
+ regmap_write(tv->regs, SUN4I_TVE_ACTIVE_LINE_REG,
+ SUN4I_TVE_ACTIVE_LINE(1440));
+
+ /* Set composite chroma gain to 50 % */
+ regmap_write(tv->regs, SUN4I_TVE_CHROMA_REG,
+ SUN4I_TVE_CHROMA_COMP_GAIN_50);
+
+ regmap_write(tv->regs, SUN4I_TVE_12C_REG,
+ SUN4I_TVE_12C_COMP_YUV_EN |
+ SUN4I_TVE_12C_NOTCH_WIDTH_WIDE);
+
+ regmap_write(tv->regs, SUN4I_TVE_RESYNC_REG,
+ SUN4I_TVE_RESYNC_PIXEL(tv_mode->resync_params->pixel) |
+ SUN4I_TVE_RESYNC_LINE(tv_mode->resync_params->line) |
+ (tv_mode->resync_params->field ?
+ SUN4I_TVE_RESYNC_FIELD : 0));
+
+ regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0);
+
+ clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
+}
+
+static struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
+ .atomic_check = sun4i_tv_atomic_check,
+ .disable = sun4i_tv_disable,
+ .enable = sun4i_tv_enable,
+ .mode_set = sun4i_tv_mode_set,
+};
+
+static void sun4i_tv_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs sun4i_tv_funcs = {
+ .destroy = sun4i_tv_destroy,
+};
+
+static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
+ struct tv_mode *tv_mode = &tv_modes[i];
+
+ strcpy(mode->name, tv_mode->name);
+
+ sun4i_tv_mode_to_drm_mode(tv_mode, mode);
+ drm_mode_probed_add(connector, mode);
+ }
+
+ return i;
+}
+
+static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ /* TODO */
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+sun4i_tv_comp_best_encoder(struct drm_connector *connector)
+{
+ struct sun4i_tv *tv = drm_connector_to_sun4i_tv(connector);
+
+ return &tv->encoder;
+}
+
+static struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {
+ .get_modes = sun4i_tv_comp_get_modes,
+ .mode_valid = sun4i_tv_comp_mode_valid,
+ .best_encoder = sun4i_tv_comp_best_encoder,
+};
+
+static enum drm_connector_status
+sun4i_tv_comp_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void
+sun4i_tv_comp_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs sun4i_tv_comp_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = sun4i_tv_comp_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = sun4i_tv_comp_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct regmap_config sun4i_tv_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SUN4I_TVE_WSS_DATA2_REG,
+ .name = "tv-encoder",
+};
+
+static int sun4i_tv_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tv *tv;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ tv = devm_kzalloc(dev, sizeof(*tv), GFP_KERNEL);
+ if (!tv)
+ return -ENOMEM;
+ tv->drv = drv;
+ dev_set_drvdata(dev, tv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(dev, "Couldn't map the TV encoder registers\n");
+ return PTR_ERR(regs);
+ }
+
+ tv->regs = devm_regmap_init_mmio(dev, regs,
+ &sun4i_tv_regmap_config);
+ if (IS_ERR(tv->regs)) {
+ dev_err(dev, "Couldn't create the TV encoder regmap\n");
+ return PTR_ERR(tv->regs);
+ }
+
+ tv->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(tv->reset)) {
+ dev_err(dev, "Couldn't get our reset line\n");
+ return PTR_ERR(tv->reset);
+ }
+
+ ret = reset_control_deassert(tv->reset);
+ if (ret) {
+ dev_err(dev, "Couldn't deassert our reset line\n");
+ return ret;
+ }
+
+ tv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(tv->clk)) {
+ dev_err(dev, "Couldn't get the TV encoder clock\n");
+ ret = PTR_ERR(tv->clk);
+ goto err_assert_reset;
+ }
+ clk_prepare_enable(tv->clk);
+
+ drm_encoder_helper_add(&tv->encoder,
+ &sun4i_tv_helper_funcs);
+ ret = drm_encoder_init(drm,
+ &tv->encoder,
+ &sun4i_tv_funcs,
+ DRM_MODE_ENCODER_TVDAC,
+ NULL);
+ if (ret) {
+ dev_err(dev, "Couldn't initialise the TV encoder\n");
+ goto err_disable_clk;
+ }
+
+ tv->encoder.possible_crtcs = BIT(0);
+
+ drm_connector_helper_add(&tv->connector,
+ &sun4i_tv_comp_connector_helper_funcs);
+ ret = drm_connector_init(drm, &tv->connector,
+ &sun4i_tv_comp_connector_funcs,
+ DRM_MODE_CONNECTOR_Composite);
+ if (ret) {
+ dev_err(dev,
+ "Couldn't initialise the Composite connector\n");
+ goto err_cleanup_connector;
+ }
+ tv->connector.interlace_allowed = true;
+
+ drm_mode_connector_attach_encoder(&tv->connector, &tv->encoder);
+
+ return 0;
+
+err_cleanup_connector:
+ drm_encoder_cleanup(&tv->encoder);
+err_disable_clk:
+ clk_disable_unprepare(tv->clk);
+err_assert_reset:
+ reset_control_assert(tv->reset);
+ return ret;
+}
+
+static void sun4i_tv_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct sun4i_tv *tv = dev_get_drvdata(dev);
+
+ drm_connector_cleanup(&tv->connector);
+ drm_encoder_cleanup(&tv->encoder);
+ clk_disable_unprepare(tv->clk);
+}
+
+static struct component_ops sun4i_tv_ops = {
+ .bind = sun4i_tv_bind,
+ .unbind = sun4i_tv_unbind,
+};
+
+static int sun4i_tv_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &sun4i_tv_ops);
+}
+
+static int sun4i_tv_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sun4i_tv_ops);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_tv_of_table[] = {
+ { .compatible = "allwinner,sun4i-a10-tv-encoder" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun4i_tv_of_table);
+
+static struct platform_driver sun4i_tv_platform_driver = {
+ .probe = sun4i_tv_probe,
+ .remove = sun4i_tv_remove,
+ .driver = {
+ .name = "sun4i-tve",
+ .of_match_table = sun4i_tv_of_table,
+ },
+};
+module_platform_driver(sun4i_tv_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 TV Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
index 584810474e5b..e53df59cb139 100644
--- a/drivers/gpu/drm/vc4/Kconfig
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -5,6 +5,7 @@ config DRM_VC4
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
+ select DRM_PANEL
help
Choose this option if you have a system that has a Broadcom
VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
index 4c6a99f0398c..fb77db755e0a 100644
--- a/drivers/gpu/drm/vc4/Makefile
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -7,6 +7,7 @@ vc4-y := \
vc4_bo.o \
vc4_crtc.o \
vc4_drv.o \
+ vc4_dpi.o \
vc4_kms.o \
vc4_gem.o \
vc4_hdmi.o \
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 355ee4b091b3..231356f42a04 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -49,6 +49,10 @@ struct vc4_crtc {
/* Which HVS channel we're using for our CRTC. */
int channel;
+ u8 lut_r[256];
+ u8 lut_g[256];
+ u8 lut_b[256];
+
struct drm_pending_vblank_event *event;
};
@@ -147,6 +151,46 @@ static void vc4_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
+static void
+vc4_crtc_lut_load(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ u32 i;
+
+ /* The LUT memory is laid out with each HVS channel in order,
+ * each of which takes 256 writes for R, 256 for G, then 256
+ * for B.
+ */
+ HVS_WRITE(SCALER_GAMADDR,
+ SCALER_GAMADDR_AUTOINC |
+ (vc4_crtc->channel * 3 * crtc->gamma_size));
+
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]);
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
+}
+
+static void
+vc4_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+ uint32_t start, uint32_t size)
+{
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ u32 i;
+
+ for (i = start; i < start + size; i++) {
+ vc4_crtc->lut_r[i] = r[i] >> 8;
+ vc4_crtc->lut_g[i] = g[i] >> 8;
+ vc4_crtc->lut_b[i] = b[i] >> 8;
+ }
+
+ vc4_crtc_lut_load(crtc);
+}
+
static u32 vc4_get_fifo_full_level(u32 format)
{
static const u32 fifo_len_bytes = 64;
@@ -260,8 +304,14 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
SCALER_DISPBKGND_AUTOHS |
+ SCALER_DISPBKGND_GAMMA |
(interlace ? SCALER_DISPBKGND_INTERLACE : 0));
+ /* Reload the LUT, since the SRAMs would have been disabled if
+ * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
+ */
+ vc4_crtc_lut_load(crtc);
+
if (debug_dump_regs) {
DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc));
vc4_crtc_dump_regs(vc4_crtc);
@@ -613,6 +663,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = vc4_crtc_duplicate_state,
.atomic_destroy_state = vc4_crtc_destroy_state,
+ .gamma_set = vc4_crtc_gamma_set,
};
static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
@@ -711,6 +762,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
primary_plane->crtc = crtc;
vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
vc4_crtc->channel = vc4_crtc->data->hvs_channel;
+ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
/* Set up some arbitrary number of planes. We're not limited
* by a set number of physical registers, just the space in
@@ -751,6 +803,12 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
vc4_set_crtc_possible_masks(drm, crtc);
+ for (i = 0; i < crtc->gamma_size; i++) {
+ vc4_crtc->lut_r[i] = i;
+ vc4_crtc->lut_g[i] = i;
+ vc4_crtc->lut_b[i] = i;
+ }
+
platform_set_drvdata(pdev, vc4_crtc);
return 0;
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
index d76ad10b07fd..245115d49c46 100644
--- a/drivers/gpu/drm/vc4/vc4_debugfs.c
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -17,6 +17,7 @@
static const struct drm_info_list vc4_debugfs_list[] = {
{"bo_stats", vc4_bo_stats_debugfs, 0},
+ {"dpi_regs", vc4_dpi_debugfs_regs, 0},
{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
{"hvs_regs", vc4_hvs_debugfs_regs, 0},
{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
new file mode 100644
index 000000000000..9817dbfa4ac3
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2016 Broadcom Limited
+ *
+ * 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/>.
+ */
+
+/**
+ * DOC: VC4 DPI module
+ *
+ * The VC4 DPI hardware supports MIPI DPI type 4 and Nokia ViSSI
+ * signals, which are routed out to GPIO0-27 with the ALT2 function.
+ */
+
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+#include "drm_panel.h"
+#include "linux/clk.h"
+#include "linux/component.h"
+#include "linux/of_graph.h"
+#include "linux/of_platform.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define DPI_C 0x00
+# define DPI_OUTPUT_ENABLE_MODE BIT(16)
+
+/* The order field takes the incoming 24 bit RGB from the pixel valve
+ * and shuffles the 3 channels.
+ */
+# define DPI_ORDER_MASK VC4_MASK(15, 14)
+# define DPI_ORDER_SHIFT 14
+# define DPI_ORDER_RGB 0
+# define DPI_ORDER_BGR 1
+# define DPI_ORDER_GRB 2
+# define DPI_ORDER_BRG 3
+
+/* The format field takes the ORDER-shuffled pixel valve data and
+ * formats it onto the output lines.
+ */
+# define DPI_FORMAT_MASK VC4_MASK(13, 11)
+# define DPI_FORMAT_SHIFT 11
+/* This define is named in the hardware, but actually just outputs 0. */
+# define DPI_FORMAT_9BIT_666_RGB 0
+/* Outputs 00000000rrrrrggggggbbbbb */
+# define DPI_FORMAT_16BIT_565_RGB_1 1
+/* Outputs 000rrrrr00gggggg000bbbbb */
+# define DPI_FORMAT_16BIT_565_RGB_2 2
+/* Outputs 00rrrrr000gggggg00bbbbb0 */
+# define DPI_FORMAT_16BIT_565_RGB_3 3
+/* Outputs 000000rrrrrrggggggbbbbbb */
+# define DPI_FORMAT_18BIT_666_RGB_1 4
+/* Outputs 00rrrrrr00gggggg00bbbbbb */
+# define DPI_FORMAT_18BIT_666_RGB_2 5
+/* Outputs rrrrrrrrggggggggbbbbbbbb */
+# define DPI_FORMAT_24BIT_888_RGB 6
+
+/* Reverses the polarity of the corresponding signal */
+# define DPI_PIXEL_CLK_INVERT BIT(10)
+# define DPI_HSYNC_INVERT BIT(9)
+# define DPI_VSYNC_INVERT BIT(8)
+# define DPI_OUTPUT_ENABLE_INVERT BIT(7)
+
+/* Outputs the signal the falling clock edge instead of rising. */
+# define DPI_HSYNC_NEGATE BIT(6)
+# define DPI_VSYNC_NEGATE BIT(5)
+# define DPI_OUTPUT_ENABLE_NEGATE BIT(4)
+
+/* Disables the signal */
+# define DPI_HSYNC_DISABLE BIT(3)
+# define DPI_VSYNC_DISABLE BIT(2)
+# define DPI_OUTPUT_ENABLE_DISABLE BIT(1)
+
+/* Power gate to the device, full reset at 0 -> 1 transition */
+# define DPI_ENABLE BIT(0)
+
+/* All other registers besides DPI_C return the ID */
+#define DPI_ID 0x04
+# define DPI_ID_VALUE 0x00647069
+
+/* General DPI hardware state. */
+struct vc4_dpi {
+ struct platform_device *pdev;
+
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct drm_panel *panel;
+
+ void __iomem *regs;
+
+ struct clk *pixel_clock;
+ struct clk *core_clock;
+};
+
+#define DPI_READ(offset) readl(dpi->regs + (offset))
+#define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset))
+
+/* VC4 DPI encoder KMS struct */
+struct vc4_dpi_encoder {
+ struct vc4_encoder base;
+ struct vc4_dpi *dpi;
+};
+
+static inline struct vc4_dpi_encoder *
+to_vc4_dpi_encoder(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct vc4_dpi_encoder, base.base);
+}
+
+/* VC4 DPI connector KMS struct */
+struct vc4_dpi_connector {
+ struct drm_connector base;
+ struct vc4_dpi *dpi;
+
+ /* Since the connector is attached to just the one encoder,
+ * this is the reference to it so we can do the best_encoder()
+ * hook.
+ */
+ struct drm_encoder *encoder;
+};
+
+static inline struct vc4_dpi_connector *
+to_vc4_dpi_connector(struct drm_connector *connector)
+{
+ return container_of(connector, struct vc4_dpi_connector, base);
+}
+
+#define DPI_REG(reg) { reg, #reg }
+static const struct {
+ u32 reg;
+ const char *name;
+} dpi_regs[] = {
+ DPI_REG(DPI_C),
+ DPI_REG(DPI_ID),
+};
+
+static void vc4_dpi_dump_regs(struct vc4_dpi *dpi)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dpi_regs); i++) {
+ DRM_INFO("0x%04x (%s): 0x%08x\n",
+ dpi_regs[i].reg, dpi_regs[i].name,
+ DPI_READ(dpi_regs[i].reg));
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_dpi_debugfs_regs(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 vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_dpi *dpi = vc4->dpi;
+ int i;
+
+ if (!dpi)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(dpi_regs); i++) {
+ seq_printf(m, "%s (0x%04x): 0x%08x\n",
+ dpi_regs[i].name, dpi_regs[i].reg,
+ DPI_READ(dpi_regs[i].reg));
+ }
+
+ return 0;
+}
+#endif
+
+static enum drm_connector_status
+vc4_dpi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct vc4_dpi_connector *vc4_connector =
+ to_vc4_dpi_connector(connector);
+ struct vc4_dpi *dpi = vc4_connector->dpi;
+
+ if (dpi->panel)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static void vc4_dpi_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static int vc4_dpi_connector_get_modes(struct drm_connector *connector)
+{
+ struct vc4_dpi_connector *vc4_connector =
+ to_vc4_dpi_connector(connector);
+ struct vc4_dpi *dpi = vc4_connector->dpi;
+
+ if (dpi->panel)
+ return drm_panel_get_modes(dpi->panel);
+
+ return 0;
+}
+
+static struct drm_encoder *
+vc4_dpi_connector_best_encoder(struct drm_connector *connector)
+{
+ struct vc4_dpi_connector *dpi_connector =
+ to_vc4_dpi_connector(connector);
+ return dpi_connector->encoder;
+}
+
+static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .detect = vc4_dpi_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = vc4_dpi_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = {
+ .get_modes = vc4_dpi_connector_get_modes,
+ .best_encoder = vc4_dpi_connector_best_encoder,
+};
+
+static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
+ struct vc4_dpi *dpi)
+{
+ struct drm_connector *connector = NULL;
+ struct vc4_dpi_connector *dpi_connector;
+ int ret = 0;
+
+ dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector),
+ GFP_KERNEL);
+ if (!dpi_connector) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ connector = &dpi_connector->base;
+
+ dpi_connector->encoder = dpi->encoder;
+ dpi_connector->dpi = dpi;
+
+ drm_connector_init(dev, connector, &vc4_dpi_connector_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ drm_connector_helper_add(connector, &vc4_dpi_connector_helper_funcs);
+
+ connector->polled = 0;
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ drm_mode_connector_attach_encoder(connector, dpi->encoder);
+
+ return connector;
+
+ fail:
+ if (connector)
+ vc4_dpi_connector_destroy(connector);
+
+ return ERR_PTR(ret);
+}
+
+static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
+ struct vc4_dpi *dpi = vc4_encoder->dpi;
+
+ drm_panel_disable(dpi->panel);
+
+ clk_disable_unprepare(dpi->pixel_clock);
+
+ drm_panel_unprepare(dpi->panel);
+}
+
+static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct drm_display_mode *mode = &encoder->crtc->mode;
+ struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
+ struct vc4_dpi *dpi = vc4_encoder->dpi;
+ u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
+ int ret;
+
+ ret = drm_panel_prepare(dpi->panel);
+ if (ret) {
+ DRM_ERROR("Panel failed to prepare\n");
+ return;
+ }
+
+ if (dpi->connector->display_info.num_bus_formats) {
+ u32 bus_format = dpi->connector->display_info.bus_formats[0];
+
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,
+ DPI_FORMAT);
+ break;
+ case MEDIA_BUS_FMT_BGR888_1X24:
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB,
+ DPI_FORMAT);
+ dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,
+ DPI_FORMAT);
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,
+ DPI_FORMAT);
+ break;
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_3,
+ DPI_FORMAT);
+ break;
+ default:
+ DRM_ERROR("Unknown media bus format %d\n", bus_format);
+ break;
+ }
+ }
+
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ dpi_c |= DPI_HSYNC_INVERT;
+ else if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+ dpi_c |= DPI_HSYNC_DISABLE;
+
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ dpi_c |= DPI_VSYNC_INVERT;
+ else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+ dpi_c |= DPI_VSYNC_DISABLE;
+
+ DPI_WRITE(DPI_C, dpi_c);
+
+ ret = clk_set_rate(dpi->pixel_clock, mode->clock * 1000);
+ if (ret)
+ DRM_ERROR("Failed to set clock rate: %d\n", ret);
+
+ ret = clk_prepare_enable(dpi->pixel_clock);
+ if (ret)
+ DRM_ERROR("Failed to set clock rate: %d\n", ret);
+
+ ret = drm_panel_enable(dpi->panel);
+ if (ret) {
+ DRM_ERROR("Panel failed to enable\n");
+ drm_panel_unprepare(dpi->panel);
+ return;
+ }
+}
+
+static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = {
+ .disable = vc4_dpi_encoder_disable,
+ .enable = vc4_dpi_encoder_enable,
+};
+
+static const struct of_device_id vc4_dpi_dt_match[] = {
+ { .compatible = "brcm,bcm2835-dpi", .data = NULL },
+ {}
+};
+
+/* Walks the OF graph to find the panel node and then asks DRM to look
+ * up the panel.
+ */
+static struct drm_panel *vc4_dpi_get_panel(struct device *dev)
+{
+ struct device_node *endpoint, *panel_node;
+ struct device_node *np = dev->of_node;
+ struct drm_panel *panel;
+
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint) {
+ dev_err(dev, "no endpoint to fetch DPI panel\n");
+ return NULL;
+ }
+
+ /* don't proceed if we have an endpoint but no panel_node tied to it */
+ panel_node = of_graph_get_remote_port_parent(endpoint);
+ of_node_put(endpoint);
+ if (!panel_node) {
+ dev_err(dev, "no valid panel node\n");
+ return NULL;
+ }
+
+ panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+
+ return panel;
+}
+
+static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_dpi *dpi;
+ struct vc4_dpi_encoder *vc4_dpi_encoder;
+ int ret;
+
+ dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
+ if (!dpi)
+ return -ENOMEM;
+
+ vc4_dpi_encoder = devm_kzalloc(dev, sizeof(*vc4_dpi_encoder),
+ GFP_KERNEL);
+ if (!vc4_dpi_encoder)
+ return -ENOMEM;
+ vc4_dpi_encoder->base.type = VC4_ENCODER_TYPE_DPI;
+ vc4_dpi_encoder->dpi = dpi;
+ dpi->encoder = &vc4_dpi_encoder->base.base;
+
+ dpi->pdev = pdev;
+ dpi->regs = vc4_ioremap_regs(pdev, 0);
+ if (IS_ERR(dpi->regs))
+ return PTR_ERR(dpi->regs);
+
+ vc4_dpi_dump_regs(dpi);
+
+ if (DPI_READ(DPI_ID) != DPI_ID_VALUE) {
+ dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n",
+ DPI_READ(DPI_ID), DPI_ID_VALUE);
+ return -ENODEV;
+ }
+
+ dpi->core_clock = devm_clk_get(dev, "core");
+ if (IS_ERR(dpi->core_clock)) {
+ ret = PTR_ERR(dpi->core_clock);
+ if (ret != -EPROBE_DEFER)
+ DRM_ERROR("Failed to get core clock: %d\n", ret);
+ return ret;
+ }
+ dpi->pixel_clock = devm_clk_get(dev, "pixel");
+ if (IS_ERR(dpi->pixel_clock)) {
+ ret = PTR_ERR(dpi->pixel_clock);
+ if (ret != -EPROBE_DEFER)
+ DRM_ERROR("Failed to get pixel clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dpi->core_clock);
+ if (ret)
+ DRM_ERROR("Failed to turn on core clock: %d\n", ret);
+
+ dpi->panel = vc4_dpi_get_panel(dev);
+
+ drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs,
+ DRM_MODE_ENCODER_DPI, NULL);
+ drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs);
+
+ dpi->connector = vc4_dpi_connector_init(drm, dpi);
+ if (IS_ERR(dpi->connector)) {
+ ret = PTR_ERR(dpi->connector);
+ goto err_destroy_encoder;
+ }
+
+ if (dpi->panel)
+ drm_panel_attach(dpi->panel, dpi->connector);
+
+ dev_set_drvdata(dev, dpi);
+
+ vc4->dpi = dpi;
+
+ return 0;
+
+err_destroy_encoder:
+ drm_encoder_cleanup(dpi->encoder);
+ clk_disable_unprepare(dpi->core_clock);
+ return ret;
+}
+
+static void vc4_dpi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_dpi *dpi = dev_get_drvdata(dev);
+
+ if (dpi->panel)
+ drm_panel_detach(dpi->panel);
+
+ vc4_dpi_connector_destroy(dpi->connector);
+ drm_encoder_cleanup(dpi->encoder);
+
+ clk_disable_unprepare(dpi->core_clock);
+
+ vc4->dpi = NULL;
+}
+
+static const struct component_ops vc4_dpi_ops = {
+ .bind = vc4_dpi_bind,
+ .unbind = vc4_dpi_unbind,
+};
+
+static int vc4_dpi_dev_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &vc4_dpi_ops);
+}
+
+static int vc4_dpi_dev_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vc4_dpi_ops);
+ return 0;
+}
+
+struct platform_driver vc4_dpi_driver = {
+ .probe = vc4_dpi_dev_probe,
+ .remove = vc4_dpi_dev_remove,
+ .driver = {
+ .name = "vc4_dpi",
+ .of_match_table = vc4_dpi_dt_match,
+ },
+};
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index b7d2ff0e6e1f..143dd98aa079 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -81,6 +81,7 @@ static struct drm_driver vc4_drm_driver = {
DRIVER_ATOMIC |
DRIVER_GEM |
DRIVER_HAVE_IRQ |
+ DRIVER_RENDER |
DRIVER_PRIME),
.lastclose = vc4_lastclose,
.irq_handler = vc4_irq,
@@ -237,6 +238,7 @@ static const struct component_master_ops vc4_drm_ops = {
static struct platform_driver *const component_drivers[] = {
&vc4_hdmi_driver,
+ &vc4_dpi_driver,
&vc4_crtc_driver,
&vc4_hvs_driver,
&vc4_v3d_driver,
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index fa2ad15d4f62..37cac59401d7 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -16,6 +16,7 @@ struct vc4_dev {
struct vc4_hvs *hvs;
struct vc4_crtc *crtc[3];
struct vc4_v3d *v3d;
+ struct vc4_dpi *dpi;
struct drm_fbdev_cma *fbdev;
@@ -422,6 +423,10 @@ void vc4_debugfs_cleanup(struct drm_minor *minor);
/* vc4_drv.c */
void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
+/* vc4_dpi.c */
+extern struct platform_driver vc4_dpi_driver;
+int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused);
+
/* vc4_gem.c */
void vc4_gem_init(struct drm_device *dev);
void vc4_gem_destroy(struct drm_device *dev);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index d8b864925fd3..fd2644d231ff 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -573,7 +573,7 @@ err_unprepare_hsm:
err_unprepare_pix:
clk_disable_unprepare(hdmi->pixel_clock);
err_put_i2c:
- put_device(&vc4->hdmi->ddc->dev);
+ put_device(&hdmi->ddc->dev);
return ret;
}
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
index bf42a8e87111..6163b95c5411 100644
--- a/drivers/gpu/drm/vc4/vc4_regs.h
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -390,6 +390,12 @@
#define SCALER_DISPBASE2 0x0000006c
#define SCALER_DISPALPHA2 0x00000070
#define SCALER_GAMADDR 0x00000078
+# define SCALER_GAMADDR_AUTOINC BIT(31)
+/* Enables all gamma ramp SRAMs, not just those of CRTCs with gamma
+ * enabled.
+ */
+# define SCALER_GAMADDR_SRAMENB BIT(30)
+
#define SCALER_GAMDATA 0x000000e0
#define SCALER_DLIST_START 0x00002000
#define SCALER_DLIST_SIZE 0x00004000
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 800c48e80811..2f2bf23d9675 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -250,7 +250,6 @@ struct drm_framebuffer {
struct drm_property_blob {
struct drm_mode_object base;
struct drm_device *dev;
- struct kref refcount;
struct list_head head_global;
struct list_head head_file;
size_t length;
diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h
index 6554b6f38bb4..c6d9c9c55794 100644
--- a/include/drm/drm_fb_cma_helper.h
+++ b/include/drm/drm_fb_cma_helper.h
@@ -38,6 +38,8 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
unsigned int plane);
#ifdef CONFIG_DEBUG_FS
+struct seq_file;
+
int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg);
#endif
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 7a7856e02e49..f7e53ea9ee96 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -202,6 +202,7 @@ struct drm_mode_get_plane_res {
#define DRM_MODE_ENCODER_VIRTUAL 5
#define DRM_MODE_ENCODER_DSI 6
#define DRM_MODE_ENCODER_DPMST 7
+#define DRM_MODE_ENCODER_DPI 8
struct drm_mode_get_encoder {
__u32 encoder_id;
@@ -241,6 +242,7 @@ struct drm_mode_get_encoder {
#define DRM_MODE_CONNECTOR_eDP 14
#define DRM_MODE_CONNECTOR_VIRTUAL 15
#define DRM_MODE_CONNECTOR_DSI 16
+#define DRM_MODE_CONNECTOR_DPI 17
struct drm_mode_get_connector {
diff --git a/include/uapi/drm/qxl_drm.h b/include/uapi/drm/qxl_drm.h
index 4d1e32640463..826615d8e180 100644
--- a/include/uapi/drm/qxl_drm.h
+++ b/include/uapi/drm/qxl_drm.h
@@ -84,7 +84,6 @@ struct drm_qxl_command {
__u32 pad;
};
-/* XXX: call it drm_qxl_commands? */
struct drm_qxl_execbuffer {
__u32 flags; /* for future use */
__u32 commands_num;
diff --git a/include/uapi/drm/sis_drm.h b/include/uapi/drm/sis_drm.h
index 374858cdcdaa..a20d88bc4970 100644
--- a/include/uapi/drm/sis_drm.h
+++ b/include/uapi/drm/sis_drm.h
@@ -27,6 +27,8 @@
#ifndef __SIS_DRM_H__
#define __SIS_DRM_H__
+#include "drm.h"
+
/* SiS specific ioctls */
#define NOT_USED_0_3
#define DRM_SIS_FB_ALLOC 0x04
diff --git a/include/video/exynos5433_decon.h b/include/video/exynos5433_decon.h
index c1c1ca18abc0..0098a522d9f4 100644
--- a/include/video/exynos5433_decon.h
+++ b/include/video/exynos5433_decon.h
@@ -179,9 +179,9 @@
#define TRIGCON_TRIGMODE_W1BUF (1 << 10)
#define TRIGCON_SWTRIGCMD_W0BUF (1 << 6)
#define TRIGCON_TRIGMODE_W0BUF (1 << 5)
-#define TRIGCON_HWTRIGMASK_I80_RGB (1 << 4)
-#define TRIGCON_HWTRIGEN_I80_RGB (1 << 3)
-#define TRIGCON_HWTRIG_INV_I80_RGB (1 << 2)
+#define TRIGCON_HWTRIGMASK (1 << 4)
+#define TRIGCON_HWTRIGEN (1 << 3)
+#define TRIGCON_HWTRIG_INV (1 << 2)
#define TRIGCON_SWTRIGCMD (1 << 1)
#define TRIGCON_SWTRIGEN (1 << 0)
OpenPOWER on IntegriCloud