Pull drm updates from Dave Airlie: "This is the main drm pull request for v4.2. I've one other new driver from freescale on my radar, it's been posted and reviewed, I'd just like to get someone to give it a last look, so maybe I'll send it or maybe I'll leave it. There is no major nouveau changes in here, Ben was working on something big, and we agreed it was a bit late, there wasn't anything else he considered urgent to merge. There might be another msm pull for some bits that are waiting on arm-soc, I'll see how we time it. This touches some "of" stuff, acks are in place except for the fixes to the build in various configs,t hat I just applied. Summary: New drivers: - virtio-gpu: KMS only pieces of driver for virtio-gpu in qemu. This is just the first part of this driver, enough to run unaccelerated userspace on. As qemu merges more we'll start adding the 3D features for the virgl 3d work. - amdgpu: a new driver from AMD to driver their newer GPUs. (VI+) It contains a new cleaner userspace API, and is a clean break from radeon moving forward, that AMD are going to concentrate on. It also contains a set of register headers auto generated from AMD internal database. core: - atomic modesetting API completed, enabled by default now. - Add support for mode_id blob to atomic ioctl to complete interface. - bunch of Displayport MST fixes - lots of misc fixes. panel: - new simple panels - fix some long-standing build issues with bridge drivers radeon: - VCE1 support - add a GPU reset counter for userspace - lots of fixes. amdkfd: - H/W debugger support module - static user-mode queues - support killing all the waves when a process terminates - use standard DECLARE_BITMAP i915: - Add Broxton support - S3, rotation support for Skylake - RPS booting tuning - CPT modeset sequence fixes - ns2501 dither support - enable cmd parser on haswell - cdclk handling fixes - gen8 dynamic pte allocation - lots of atomic conversion work exynos: - Add atomic modesetting support - Add iommu support - Consolidate drm driver initialization - and MIC, DECON and MIPI-DSI support for exynos5433 omapdrm: - atomic modesetting support (fixes lots of things in rewrite) tegra: - DP aux transaction fixes - iommu support fix msm: - adreno a306 support - various dsi bits - various 64-bit fixes - NV12MT support rcar-du: - atomic and misc fixes sti: - fix HDMI timing complaince tilcdc: - use drm component API to access tda998x driver - fix module unloading qxl: - stability fixes" * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (872 commits) drm/nouveau: Pause between setting gpu to D3hot and cutting the power drm/dp/mst: close deadlock in connector destruction. drm: Always enable atomic API drm/vgem: Set unique to "vgem" of: fix a build error to of_graph_get_endpoint_by_regs function drm/dp/mst: take lock around looking up the branch device on hpd irq drm/dp/mst: make sure mst_primary mstb is valid in work function of: add EXPORT_SYMBOL for of_graph_get_endpoint_by_regs ARM: dts: rename the clock of MIPI DSI 'pll_clk' to 'sclk_mipi' drm/atomic: Don't set crtc_state->enable manually drm/exynos: dsi: do not set TE GPIO direction by input drm/exynos: dsi: add support for MIC driver as a bridge drm/exynos: dsi: add support for Exynos5433 drm/exynos: dsi: make use of array for clock access drm/exynos: dsi: make use of driver data for static values drm/exynos: dsi: add macros for register access drm/exynos: dsi: rename pll_clk to sclk_clk drm/exynos: mic: add MIC driver of: add helper for getting endpoint node of specific identifiers drm/exynos: add Exynos5433 decon driver ...
Unified · Split
| @@ -2439,6 +2439,18 @@ void intel_crt_init(struct drm_device *dev) · 2438 unmodified lines | ||
| 2439 | 2439 | <title>Tile group</title> |
| 2440 | 2440 | !Pdrivers/gpu/drm/drm_crtc.c Tile group |
| 2441 | 2441 | </sect2> |
| 2442 | + <sect2> | |
| 2443 | + <title>Bridges</title> | |
| 2444 | + <sect3> | |
| 2445 | + <title>Overview</title> | |
| 2446 | +!Pdrivers/gpu/drm/drm_bridge.c overview | |
| 2447 | + </sect3> | |
| 2448 | + <sect3> | |
| 2449 | + <title>Default bridge callback sequence</title> | |
| 2450 | +!Pdrivers/gpu/drm/drm_bridge.c bridge callbacks | |
| 2451 | + </sect3> | |
| 2452 | +!Edrivers/gpu/drm/drm_bridge.c | |
| 2453 | + </sect2> | |
| 2442 | 2454 | </sect1> |
| 2443 | 2455 | |
| 2444 | 2456 | <!-- Internals: kms properties --> |
| @@ -2573,7 +2585,22 @@ void intel_crt_init(struct drm_device *dev) · 128 unmodified lines | ||
| 2573 | 2585 | <td valign="top" >Description/Restrictions</td> |
| 2574 | 2586 | </tr> |
| 2575 | 2587 | <tr> |
| 2576 | − <td rowspan="36" valign="top" >DRM</td> | |
| 2588 | + <td rowspan="37" valign="top" >DRM</td> | |
| 2589 | + <td valign="top" >Generic</td> | |
| 2590 | + <td valign="top" >“rotation”</td> | |
| 2591 | + <td valign="top" >BITMASK</td> | |
| 2592 | + <td valign="top" >{ 0, "rotate-0" }, | |
| 2593 | + { 1, "rotate-90" }, | |
| 2594 | + { 2, "rotate-180" }, | |
| 2595 | + { 3, "rotate-270" }, | |
| 2596 | + { 4, "reflect-x" }, | |
| 2597 | + { 5, "reflect-y" }</td> | |
| 2598 | + <td valign="top" >CRTC, Plane</td> | |
| 2599 | + <td valign="top" >rotate-(degrees) rotates the image by the specified amount in degrees | |
| 2600 | + in counter clockwise direction. reflect-x and reflect-y reflects the | |
| 2601 | + image along the specified axis prior to rotation</td> | |
| 2602 | + </tr> | |
| 2603 | + <tr> | |
| 2577 | 2604 | <td rowspan="5" valign="top" >Connector</td> |
| 2578 | 2605 | <td valign="top" >“EDID”</td> |
| 2579 | 2606 | <td valign="top" >BLOB | IMMUTABLE</td> |
| @@ -2834,7 +2861,7 @@ void intel_crt_init(struct drm_device *dev) · 254 unmodified lines | ||
| 2834 | 2861 | <td valign="top" >TBD</td> |
| 2835 | 2862 | </tr> |
| 2836 | 2863 | <tr> |
| 2837 | − <td rowspan="21" valign="top" >i915</td> | |
| 2864 | + <td rowspan="20" valign="top" >i915</td> | |
| 2838 | 2865 | <td rowspan="2" valign="top" >Generic</td> |
| 2839 | 2866 | <td valign="top" >"Broadcast RGB"</td> |
| 2840 | 2867 | <td valign="top" >ENUM</td> |
| @@ -2850,14 +2877,6 @@ void intel_crt_init(struct drm_device *dev) · 9 unmodified lines | ||
| 2850 | 2877 | <td valign="top" >TBD</td> |
| 2851 | 2878 | </tr> |
| 2852 | 2879 | <tr> |
| 2853 | − <td rowspan="1" valign="top" >Plane</td> | |
| 2854 | − <td valign="top" >“rotation”</td> | |
| 2855 | − <td valign="top" >BITMASK</td> | |
| 2856 | − <td valign="top" >{ 0, "rotate-0" }, { 2, "rotate-180" }</td> | |
| 2857 | − <td valign="top" >Plane</td> | |
| 2858 | − <td valign="top" >TBD</td> | |
| 2859 | − </tr> | |
| 2860 | − <tr> | |
| 2861 | 2880 | <td rowspan="17" valign="top" >SDVO-TV</td> |
| 2862 | 2881 | <td valign="top" >“mode”</td> |
| 2863 | 2882 | <td valign="top" >ENUM</td> |
| @@ -3365,19 +3384,7 @@ void intel_crt_init(struct drm_device *dev) · 501 unmodified lines | ||
| 3365 | 3384 | </tr> |
| 3366 | 3385 | <tr> |
| 3367 | 3386 | <td rowspan="2" valign="top" >omap</td> |
| 3368 | − <td rowspan="2" valign="top" >Generic</td> | |
| 3369 | − <td valign="top" >“rotation”</td> | |
| 3370 | − <td valign="top" >BITMASK</td> | |
| 3371 | − <td valign="top" >{ 0, "rotate-0" }, | |
| 3372 | − { 1, "rotate-90" }, | |
| 3373 | − { 2, "rotate-180" }, | |
| 3374 | − { 3, "rotate-270" }, | |
| 3375 | − { 4, "reflect-x" }, | |
| 3376 | − { 5, "reflect-y" }</td> | |
| 3377 | − <td valign="top" >CRTC, Plane</td> | |
| 3378 | − <td valign="top" >TBD</td> | |
| 3379 | − </tr> | |
| 3380 | − <tr> | |
| 3387 | + <td valign="top" >Generic</td> | |
| 3381 | 3388 | <td valign="top" >“zorder”</td> |
| 3382 | 3389 | <td valign="top" >RANGE</td> |
| 3383 | 3390 | <td valign="top" >Min=0, Max=3</td> |
| @@ -4067,7 +4074,7 @@ int num_ioctls;</synopsis> · 683 unmodified lines | ||
| 4067 | 4074 | <title>DPIO</title> |
| 4068 | 4075 | !Pdrivers/gpu/drm/i915/i915_reg.h DPIO |
| 4069 | 4076 | <table id="dpiox2"> |
| 4070 | − <title>Dual channel PHY (VLV/CHV)</title> | |
| 4077 | + <title>Dual channel PHY (VLV/CHV/BXT)</title> | |
| 4071 | 4078 | <tgroup cols="8"> |
| 4072 | 4079 | <colspec colname="c0" /> |
| 4073 | 4080 | <colspec colname="c1" /> |
| @@ -4118,7 +4125,7 @@ int num_ioctls;</synopsis> · 44 unmodified lines | ||
| 4118 | 4125 | </tgroup> |
| 4119 | 4126 | </table> |
| 4120 | 4127 | <table id="dpiox1"> |
| 4121 | − <title>Single channel PHY (CHV)</title> | |
| 4128 | + <title>Single channel PHY (CHV/BXT)</title> | |
| 4122 | 4129 | <tgroup cols="4"> |
| 4123 | 4130 | <colspec colname="c0" /> |
| 4124 | 4131 | <colspec colname="c1" /> |
| @@ -4153,6 +4160,12 @@ int num_ioctls;</synopsis> · 28 unmodified lines | ||
| 4153 | 4160 | </tgroup> |
| 4154 | 4161 | </table> |
| 4155 | 4162 | </sect2> |
| 4163 | + | |
| 4164 | + <sect2> | |
| 4165 | + <title>CSR firmware support for DMC</title> | |
| 4166 | +!Pdrivers/gpu/drm/i915/intel_csr.c csr support for dmc | |
| 4167 | +!Idrivers/gpu/drm/i915/intel_csr.c | |
| 4168 | + </sect2> | |
| 4156 | 4169 | </sect1> |
| 4157 | 4170 | |
| 4158 | 4171 | <sect1> |
| @@ -4204,7 +4217,6 @@ int num_ioctls;</synopsis> · 45 unmodified lines | ||
| 4204 | 4217 | !Idrivers/gpu/drm/i915/i915_gem_shrinker.c |
| 4205 | 4218 | </sect2> |
| 4206 | 4219 | </sect1> |
| 4207 | − | |
| 4208 | 4220 | <sect1> |
| 4209 | 4221 | <title> Tracing </title> |
| 4210 | 4222 | <para> |
| @@ -0,0 +1,120 @@ | ||
| 1 | +Qualcomm Technologies Inc. adreno/snapdragon DSI output | |
| 2 | + | |
| 3 | +DSI Controller: | |
| 4 | +Required properties: | |
| 5 | +- compatible: | |
| 6 | + * "qcom,mdss-dsi-ctrl" | |
| 7 | +- reg: Physical base address and length of the registers of controller | |
| 8 | +- reg-names: The names of register regions. The following regions are required: | |
| 9 | + * "dsi_ctrl" | |
| 10 | +- qcom,dsi-host-index: The ID of DSI controller hardware instance. This should | |
| 11 | + be 0 or 1, since we have 2 DSI controllers at most for now. | |
| 12 | +- interrupts: The interrupt signal from the DSI block. | |
| 13 | +- power-domains: Should be <&mmcc MDSS_GDSC>. | |
| 14 | +- clocks: device clocks | |
| 15 | + See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details. | |
| 16 | +- clock-names: the following clocks are required: | |
| 17 | + * "bus_clk" | |
| 18 | + * "byte_clk" | |
| 19 | + * "core_clk" | |
| 20 | + * "core_mmss_clk" | |
| 21 | + * "iface_clk" | |
| 22 | + * "mdp_core_clk" | |
| 23 | + * "pixel_clk" | |
| 24 | +- vdd-supply: phandle to vdd regulator device node | |
| 25 | +- vddio-supply: phandle to vdd-io regulator device node | |
| 26 | +- vdda-supply: phandle to vdda regulator device node | |
| 27 | +- qcom,dsi-phy: phandle to DSI PHY device node | |
| 28 | + | |
| 29 | +Optional properties: | |
| 30 | +- panel@0: Node of panel connected to this DSI controller. | |
| 31 | + See files in Documentation/devicetree/bindings/panel/ for each supported | |
| 32 | + panel. | |
| 33 | +- qcom,dual-panel-mode: Boolean value indicating if the DSI controller is | |
| 34 | + driving a panel which needs 2 DSI links. | |
| 35 | +- qcom,master-panel: Boolean value indicating if the DSI controller is driving | |
| 36 | + the master link of the 2-DSI panel. | |
| 37 | +- qcom,sync-dual-panel: Boolean value indicating if the DSI controller is | |
| 38 | + driving a 2-DSI panel whose 2 links need receive command simultaneously. | |
| 39 | +- interrupt-parent: phandle to the MDP block if the interrupt signal is routed | |
| 40 | + through MDP block | |
| 41 | + | |
| 42 | +DSI PHY: | |
| 43 | +Required properties: | |
| 44 | +- compatible: Could be the following | |
| 45 | + * "qcom,dsi-phy-28nm-hpm" | |
| 46 | + * "qcom,dsi-phy-28nm-lp" | |
| 47 | +- reg: Physical base address and length of the registers of PLL, PHY and PHY | |
| 48 | + regulator | |
| 49 | +- reg-names: The names of register regions. The following regions are required: | |
| 50 | + * "dsi_pll" | |
| 51 | + * "dsi_phy" | |
| 52 | + * "dsi_phy_regulator" | |
| 53 | +- qcom,dsi-phy-index: The ID of DSI PHY hardware instance. This should | |
| 54 | + be 0 or 1, since we have 2 DSI PHYs at most for now. | |
| 55 | +- power-domains: Should be <&mmcc MDSS_GDSC>. | |
| 56 | +- clocks: device clocks | |
| 57 | + See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details. | |
| 58 | +- clock-names: the following clocks are required: | |
| 59 | + * "iface_clk" | |
| 60 | +- vddio-supply: phandle to vdd-io regulator device node | |
| 61 | + | |
| 62 | +Example: | |
| 63 | + mdss_dsi0: qcom,mdss_dsi@fd922800 { | |
| 64 | + compatible = "qcom,mdss-dsi-ctrl"; | |
| 65 | + qcom,dsi-host-index = <0>; | |
| 66 | + interrupt-parent = <&mdss_mdp>; | |
| 67 | + interrupts = <4 0>; | |
| 68 | + reg-names = "dsi_ctrl"; | |
| 69 | + reg = <0xfd922800 0x200>; | |
| 70 | + power-domains = <&mmcc MDSS_GDSC>; | |
| 71 | + clock-names = | |
| 72 | + "bus_clk", | |
| 73 | + "byte_clk", | |
| 74 | + "core_clk", | |
| 75 | + "core_mmss_clk", | |
| 76 | + "iface_clk", | |
| 77 | + "mdp_core_clk", | |
| 78 | + "pixel_clk"; | |
| 79 | + clocks = | |
| 80 | + <&mmcc MDSS_AXI_CLK>, | |
| 81 | + <&mmcc MDSS_BYTE0_CLK>, | |
| 82 | + <&mmcc MDSS_ESC0_CLK>, | |
| 83 | + <&mmcc MMSS_MISC_AHB_CLK>, | |
| 84 | + <&mmcc MDSS_AHB_CLK>, | |
| 85 | + <&mmcc MDSS_MDP_CLK>, | |
| 86 | + <&mmcc MDSS_PCLK0_CLK>; | |
| 87 | + vdda-supply = <&pma8084_l2>; | |
| 88 | + vdd-supply = <&pma8084_l22>; | |
| 89 | + vddio-supply = <&pma8084_l12>; | |
| 90 | + | |
| 91 | + qcom,dsi-phy = <&mdss_dsi_phy0>; | |
| 92 | + | |
| 93 | + qcom,dual-panel-mode; | |
| 94 | + qcom,master-panel; | |
| 95 | + qcom,sync-dual-panel; | |
| 96 | + | |
| 97 | + panel: panel@0 { | |
| 98 | + compatible = "sharp,lq101r1sx01"; | |
| 99 | + reg = <0>; | |
| 100 | + link2 = <&secondary>; | |
| 101 | + | |
| 102 | + power-supply = <...>; | |
| 103 | + backlight = <...>; | |
| 104 | + }; | |
| 105 | + }; | |
| 106 | + | |
| 107 | + mdss_dsi_phy0: qcom,mdss_dsi_phy@fd922a00 { | |
| 108 | + compatible = "qcom,dsi-phy-28nm-hpm"; | |
| 109 | + qcom,dsi-phy-index = <0>; | |
| 110 | + reg-names = | |
| 111 | + "dsi_pll", | |
| 112 | + "dsi_phy", | |
| 113 | + "dsi_phy_regulator"; | |
| 114 | + reg = <0xfd922a00 0xd4>, | |
| 115 | + <0xfd922b00 0x2b0>, | |
| 116 | + <0xfd922d80 0x7b>; | |
| 117 | + clock-names = "iface_clk"; | |
| 118 | + clocks = <&mmcc MDSS_AHB_CLK>; | |
| 119 | + vddio-supply = <&pma8084_l12>; | |
| 120 | + }; | |
| @@ -0,0 +1,60 @@ | ||
| 1 | +Qualcomm Technologies Inc. adreno/snapdragon eDP output | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: | |
| 5 | + * "qcom,mdss-edp" | |
| 6 | +- reg: Physical base address and length of the registers of controller and PLL | |
| 7 | +- reg-names: The names of register regions. The following regions are required: | |
| 8 | + * "edp" | |
| 9 | + * "pll_base" | |
| 10 | +- interrupts: The interrupt signal from the eDP block. | |
| 11 | +- power-domains: Should be <&mmcc MDSS_GDSC>. | |
| 12 | +- clocks: device clocks | |
| 13 | + See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details. | |
| 14 | +- clock-names: the following clocks are required: | |
| 15 | + * "core_clk" | |
| 16 | + * "iface_clk" | |
| 17 | + * "mdp_core_clk" | |
| 18 | + * "pixel_clk" | |
| 19 | + * "link_clk" | |
| 20 | +- #clock-cells: The value should be 1. | |
| 21 | +- vdda-supply: phandle to vdda regulator device node | |
| 22 | +- lvl-vdd-supply: phandle to regulator device node which is used to supply power | |
| 23 | + to HPD receiving chip | |
| 24 | +- panel-en-gpios: GPIO pin to supply power to panel. | |
| 25 | +- panel-hpd-gpios: GPIO pin used for eDP hpd. | |
| 26 | + | |
| 27 | + | |
| 28 | +Optional properties: | |
| 29 | +- interrupt-parent: phandle to the MDP block if the interrupt signal is routed | |
| 30 | + through MDP block | |
| 31 | + | |
| 32 | +Example: | |
| 33 | + mdss_edp: qcom,mdss_edp@fd923400 { | |
| 34 | + compatible = "qcom,mdss-edp"; | |
| 35 | + reg-names = | |
| 36 | + "edp", | |
| 37 | + "pll_base"; | |
| 38 | + reg = <0xfd923400 0x700>, | |
| 39 | + <0xfd923a00 0xd4>; | |
| 40 | + interrupt-parent = <&mdss_mdp>; | |
| 41 | + interrupts = <12 0>; | |
| 42 | + power-domains = <&mmcc MDSS_GDSC>; | |
| 43 | + clock-names = | |
| 44 | + "core_clk", | |
| 45 | + "pixel_clk", | |
| 46 | + "iface_clk", | |
| 47 | + "link_clk", | |
| 48 | + "mdp_core_clk"; | |
| 49 | + clocks = | |
| 50 | + <&mmcc MDSS_EDPAUX_CLK>, | |
| 51 | + <&mmcc MDSS_EDPPIXEL_CLK>, | |
| 52 | + <&mmcc MDSS_AHB_CLK>, | |
| 53 | + <&mmcc MDSS_EDPLINK_CLK>, | |
| 54 | + <&mmcc MDSS_MDP_CLK>; | |
| 55 | + #clock-cells = <1>; | |
| 56 | + vdda-supply = <&pma8084_l12>; | |
| 57 | + lvl-vdd-supply = <&lvl_vreg>; | |
| 58 | + panel-en-gpios = <&tlmm 137 0>; | |
| 59 | + panel-hpd-gpios = <&tlmm 103 0>; | |
| 60 | + }; | |
| @@ -20,6 +20,9 @@ Required properties: · 19 unmodified lines | ||
| 20 | 20 | Optional properties: |
| 21 | 21 | - qcom,hdmi-tx-mux-en-gpio: hdmi mux enable pin |
| 22 | 22 | - qcom,hdmi-tx-mux-sel-gpio: hdmi mux select pin |
| 23 | +- pinctrl-names: the pin control state names; should contain "default" | |
| 24 | +- pinctrl-0: the default pinctrl state (active) | |
| 25 | +- pinctrl-1: the "sleep" pinctrl state | |
| 23 | 26 | |
| 24 | 27 | Example: |
| 25 | 28 | |
| @@ -44,5 +47,8 @@ Example: · 18 unmodified lines | ||
| 44 | 47 | qcom,hdmi-tx-hpd = <&msmgpio 72 GPIO_ACTIVE_HIGH>; |
| 45 | 48 | core-vdda-supply = <&pm8921_hdmi_mvs>; |
| 46 | 49 | hdmi-mux-supply = <&ext_3p3v>; |
| 50 | + pinctrl-names = "default", "sleep"; | |
| 51 | + pinctrl-0 = <&hpd_active &ddc_active &cec_active>; | |
| 52 | + pinctrl-1 = <&hpd_suspend &ddc_suspend &cec_suspend>; | |
| 47 | 53 | }; |
| 48 | 54 | }; |
| @@ -1,18 +0,0 @@ | ||
| 1 | −Device-Tree bindings for tilcdc DRM encoder slave output driver | |
| 2 | − | |
| 3 | −Required properties: | |
| 4 | − - compatible: value should be "ti,tilcdc,slave". | |
| 5 | − - i2c: the phandle for the i2c device the encoder slave is connected to | |
| 6 | − | |
| 7 | −Recommended properties: | |
| 8 | − - pinctrl-names, pinctrl-0: the pincontrol settings to configure | |
| 9 | − muxing properly for pins that connect to TFP410 device | |
| 10 | − | |
| 11 | −Example: | |
| 12 | − | |
| 13 | − hdmi { | |
| 14 | − compatible = "ti,tilcdc,slave"; | |
| 15 | − i2c = <&i2c0>; | |
| 16 | − pinctrl-names = "default"; | |
| 17 | − pinctrl-0 = <&nxp_hdmi_bonelt_pins>; | |
| 18 | − }; | |
| @@ -18,6 +18,12 @@ Optional properties: · 17 unmodified lines | ||
| 18 | 18 | - max-pixelclock: The maximum pixel clock that can be supported |
| 19 | 19 | by the lcd controller in KHz. |
| 20 | 20 | |
| 21 | +Optional nodes: | |
| 22 | + | |
| 23 | + - port/ports: to describe a connection to an external encoder. The | |
| 24 | + binding follows Documentation/devicetree/bindings/graph.txt and | |
| 25 | + suppors a single port with a single endpoint. | |
| 26 | + | |
| 21 | 27 | Example: |
| 22 | 28 | |
| 23 | 29 | fb: fb@4830e000 { |
| @@ -26,4 +32,25 @@ Example: · 2 unmodified lines | ||
| 26 | 32 | interrupt-parent = <&intc>; |
| 27 | 33 | interrupts = <36>; |
| 28 | 34 | ti,hwmods = "lcdc"; |
| 35 | + | |
| 36 | + port { | |
| 37 | + lcdc_0: endpoint@0 { | |
| 38 | + remote-endpoint = <&hdmi_0>; | |
| 39 | + }; | |
| 40 | + }; | |
| 41 | + }; | |
| 42 | + | |
| 43 | + tda19988: tda19988 { | |
| 44 | + compatible = "nxp,tda998x"; | |
| 45 | + reg = <0x70>; | |
| 46 | + | |
| 47 | + pinctrl-names = "default", "off"; | |
| 48 | + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; | |
| 49 | + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; | |
| 50 | + | |
| 51 | + port { | |
| 52 | + hdmi_0: endpoint@0 { | |
| 53 | + remote-endpoint = <&lcdc_0>; | |
| 54 | + }; | |
| 55 | + }; | |
| 29 | 56 | }; |
| @@ -0,0 +1,7 @@ | ||
| 1 | +HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "hannstar,hsd100pxn1" | |
| 5 | + | |
| 6 | +This binding is compatible with the simple-panel binding, which is specified | |
| 7 | +in simple-panel.txt in this directory. | |
| @@ -0,0 +1,7 @@ | ||
| 1 | +LG 7" (800x480 pixels) TFT LCD panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "lg,lb070wv8" | |
| 5 | + | |
| 6 | +This binding is compatible with the simple-panel binding, which is specified | |
| 7 | +in simple-panel.txt in this directory. | |
| @@ -0,0 +1,51 @@ | ||
| 1 | +Device-Tree bindings for Samsung Exynos SoC mobile image compressor (MIC) | |
| 2 | + | |
| 3 | +MIC (mobile image compressor) resides between decon and mipi dsi. Mipi dsi is | |
| 4 | +not capable to transfer high resoltuion frame data as decon can send. MIC | |
| 5 | +solves this problem by compressing the frame data by 1/2 before it is | |
| 6 | +transferred through mipi dsi. The compressed frame data must be uncompressed in | |
| 7 | +the panel PCB. | |
| 8 | + | |
| 9 | +Required properties: | |
| 10 | +- compatible: value should be "samsung,exynos5433-mic". | |
| 11 | +- reg: physical base address and length of the MIC registers set and system | |
| 12 | + register of mic. | |
| 13 | +- clocks: must include clock specifiers corresponding to entries in the | |
| 14 | + clock-names property. | |
| 15 | +- clock-names: list of clock names sorted in the same order as the clocks | |
| 16 | + property. Must contain "pclk_mic0", "sclk_rgb_vclk_to_mic0". | |
| 17 | +- samsung,disp-syscon: the reference node for syscon for DISP block. | |
| 18 | +- ports: contains a port which is connected to decon node and dsi node. | |
| 19 | + address-cells and size-cells must 1 and 0, respectively. | |
| 20 | +- port: contains an endpoint node which is connected to the endpoint in the | |
| 21 | + decon node or dsi node. The reg value must be 0 and 1 respectively. | |
| 22 | + | |
| 23 | +Example: | |
| 24 | +SoC specific DT entry: | |
| 25 | +mic: mic@13930000 { | |
| 26 | + compatible = "samsung,exynos5433-mic"; | |
| 27 | + reg = <0x13930000 0x48>; | |
| 28 | + clocks = <&cmu_disp CLK_PCLK_MIC0>, | |
| 29 | + <&cmu_disp CLK_SCLK_RGB_VCLK_TO_MIC0>; | |
| 30 | + clock-names = "pclk_mic0", "sclk_rgb_vclk_to_mic0"; | |
| 31 | + samsung,disp-syscon = <&syscon_disp>; | |
| 32 | + | |
| 33 | + ports { | |
| 34 | + #address-cells = <1>; | |
| 35 | + #size-cells = <0>; | |
| 36 | + | |
| 37 | + port@0 { | |
| 38 | + reg = <0>; | |
| 39 | + mic_to_decon: endpoint { | |
| 40 | + remote-endpoint = <&decon_to_mic>; | |
| 41 | + }; | |
| 42 | + }; | |
| 43 | + | |
| 44 | + port@1 { | |
| 45 | + reg = <1>; | |
| 46 | + mic_to_dsi: endpoint { | |
| 47 | + remote-endpoint = <&dsi_to_mic>; | |
| 48 | + }; | |
| 49 | + }; | |
| 50 | + }; | |
| 51 | +}; | |
| @@ -0,0 +1,65 @@ | ||
| 1 | +Device-Tree bindings for Samsung Exynos SoC display controller (DECON) | |
| 2 | + | |
| 3 | +DECON (Display and Enhancement Controller) is the Display Controller for the | |
| 4 | +Exynos series of SoCs which transfers the image data from a video memory | |
| 5 | +buffer to an external LCD interface. | |
| 6 | + | |
| 7 | +Required properties: | |
| 8 | +- compatible: value should be "samsung,exynos5433-decon"; | |
| 9 | +- reg: physical base address and length of the DECON registers set. | |
| 10 | +- interrupts: should contain a list of all DECON IP block interrupts in the | |
| 11 | + order: VSYNC, LCD_SYSTEM. The interrupt specifier format | |
| 12 | + depends on the interrupt controller used. | |
| 13 | +- interrupt-names: should contain the interrupt names: "vsync", "lcd_sys" | |
| 14 | + in the same order as they were listed in the interrupts | |
| 15 | + property. | |
| 16 | +- clocks: must include clock specifiers corresponding to entries in the | |
| 17 | + clock-names property. | |
| 18 | +- clock-names: list of clock names sorted in the same order as the clocks | |
| 19 | + property. Must contain "aclk_decon", "aclk_smmu_decon0x", | |
| 20 | + "aclk_xiu_decon0x", "pclk_smmu_decon0x", clk_decon_vclk", | |
| 21 | + "sclk_decon_eclk" | |
| 22 | +- ports: contains a port which is connected to mic node. address-cells and | |
| 23 | + size-cells must 1 and 0, respectively. | |
| 24 | +- port: contains an endpoint node which is connected to the endpoint in the mic | |
| 25 | + node. The reg value muset be 0. | |
| 26 | +- i80-if-timings: specify whether the panel which is connected to decon uses | |
| 27 | + i80 lcd interface or mipi video interface. This node contains | |
| 28 | + no timing information as that of fimd does. Because there is | |
| 29 | + no register in decon to specify i80 interface timing value, | |
| 30 | + it is not needed, but make it remain to use same kind of node | |
| 31 | + in fimd and exynos7 decon. | |
| 32 | + | |
| 33 | +Example: | |
| 34 | +SoC specific DT entry: | |
| 35 | +decon: decon@13800000 { | |
| 36 | + compatible = "samsung,exynos5433-decon"; | |
| 37 | + reg = <0x13800000 0x2104>; | |
| 38 | + clocks = <&cmu_disp CLK_ACLK_DECON>, <&cmu_disp CLK_ACLK_SMMU_DECON0X>, | |
| 39 | + <&cmu_disp CLK_ACLK_XIU_DECON0X>, | |
| 40 | + <&cmu_disp CLK_PCLK_SMMU_DECON0X>, | |
| 41 | + <&cmu_disp CLK_SCLK_DECON_VCLK>, | |
| 42 | + <&cmu_disp CLK_SCLK_DECON_ECLK>; | |
| 43 | + clock-names = "aclk_decon", "aclk_smmu_decon0x", "aclk_xiu_decon0x", | |
| 44 | + "pclk_smmu_decon0x", "sclk_decon_vclk", "sclk_decon_eclk"; | |
| 45 | + interrupt-names = "vsync", "lcd_sys"; | |
| 46 | + interrupts = <0 202 0>, <0 203 0>; | |
| 47 | + | |
| 48 | + ports { | |
| 49 | + #address-cells = <1>; | |
| 50 | + #size-cells = <0>; | |
| 51 | + | |
| 52 | + port@0 { | |
| 53 | + reg = <0>; | |
| 54 | + decon_to_mic: endpoint { | |
| 55 | + remote-endpoint = <&mic_to_decon>; | |
| 56 | + }; | |
| 57 | + }; | |
| 58 | + }; | |
| 59 | +}; | |
| 60 | + | |
| 61 | +Board specific DT entry: | |
| 62 | +&decon { | |
| 63 | + i80-if-timings { | |
| 64 | + }; | |
| 65 | +}; | |
| @@ -6,17 +6,19 @@ Required properties: · 5 unmodified lines | ||
| 6 | 6 | "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ |
| 7 | 7 | "samsung,exynos4415-mipi-dsi" /* for Exynos4415 SoC */ |
| 8 | 8 | "samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */ |
| 9 | + "samsung,exynos5433-mipi-dsi" /* for Exynos5433 SoCs */ | |
| 9 | 10 | - reg: physical base address and length of the registers set for the device |
| 10 | 11 | - interrupts: should contain DSI interrupt |
| 11 | 12 | - clocks: list of clock specifiers, must contain an entry for each required |
| 12 | 13 | entry in clock-names |
| 13 | − - clock-names: should include "bus_clk"and "pll_clk" entries | |
| 14 | + - clock-names: should include "bus_clk"and "sclk_mipi" entries | |
| 15 | + the use of "pll_clk" is deprecated | |
| 14 | 16 | - phys: list of phy specifiers, must contain an entry for each required |
| 15 | 17 | entry in phy-names |
| 16 | 18 | - phy-names: should include "dsim" entry |
| 17 | 19 | - vddcore-supply: MIPI DSIM Core voltage supply (e.g. 1.1V) |
| 18 | 20 | - vddio-supply: MIPI DSIM I/O and PLL voltage supply (e.g. 1.8V) |
| 19 | − - samsung,pll-clock-frequency: specifies frequency of the "pll_clk" clock | |
| 21 | + - samsung,pll-clock-frequency: specifies frequency of the oscillator clock | |
| 20 | 22 | - #address-cells, #size-cells: should be set respectively to <1> and <0> |
| 21 | 23 | according to DSI host bindings (see MIPI DSI bindings [1]) |
| 22 | 24 | |
| @@ -30,10 +32,19 @@ Video interfaces: · 7 unmodified lines | ||
| 30 | 32 | Device node can contain video interface port nodes according to [2]. |
| 31 | 33 | The following are properties specific to those nodes: |
| 32 | 34 | |
| 33 | − port node: | |
| 34 | − - reg: (required) can be 0 for input RGB/I80 port or 1 for DSI port; | |
| 35 | + port node inbound: | |
| 36 | + - reg: (required) must be 0. | |
| 37 | + port node outbound: | |
| 38 | + - reg: (required) must be 1. | |
| 35 | 39 | |
| 36 | − endpoint node of DSI port (reg = 1): | |
| 40 | + endpoint node connected from mic node (reg = 0): | |
| 41 | + - remote-endpoint: specifies the endpoint in mic node. This node is required | |
| 42 | + for Exynos5433 mipi dsi. So mic can access to panel node | |
| 43 | + thoughout this dsi node. | |
| 44 | + endpoint node connected to panel node (reg = 1): | |
| 45 | + - remote-endpoint: specifies the endpoint in panel node. This node is | |
| 46 | + required in all kinds of exynos mipi dsi to represent | |
| 47 | + the connection between mipi dsi and panel. | |
| 37 | 48 | - samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst |
| 38 | 49 | mode |
| 39 | 50 | - samsung,esc-clock-frequency: specifies DSI frequency in escape mode |
| @@ -48,7 +59,7 @@ Example: · 8 unmodified lines | ||
| 48 | 59 | reg = <0x11C80000 0x10000>; |
| 49 | 60 | interrupts = <0 79 0>; |
| 50 | 61 | clocks = <&clock 286>, <&clock 143>; |
| 51 | − clock-names = "bus_clk", "pll_clk"; | |
| 62 | + clock-names = "bus_clk", "sclk_mipi"; | |
| 52 | 63 | phys = <&mipi_phy 1>; |
| 53 | 64 | phy-names = "dsim"; |
| 54 | 65 | vddcore-supply = <&vusb_reg>; |
| @@ -72,7 +83,15 @@ Example: · 17 unmodified lines | ||
| 72 | 83 | #address-cells = <1>; |
| 73 | 84 | #size-cells = <0>; |
| 74 | 85 | |
| 86 | + port@0 { | |
| 87 | + reg = <0>; | |
| 88 | + decon_to_mic: endpoint { | |
| 89 | + remote-endpoint = <&mic_to_decon>; | |
| 90 | + }; | |
| 91 | + }; | |
| 92 | + | |
| 75 | 93 | port@1 { |
| 94 | + reg = <1>; | |
| 76 | 95 | dsi_ep: endpoint { |
| 77 | 96 | reg = <0>; |
| 78 | 97 | samsung,burst-clock-frequency = <500000000>; |
| @@ -632,7 +632,7 @@ F: drivers/iommu/amd_iommu*.[ch] · 631 unmodified lines | ||
| 632 | 632 | F: include/linux/amd-iommu.h |
| 633 | 633 | |
| 634 | 634 | AMD KFD |
| 635 | −M: Oded Gabbay <oded.gabbay@amd.com> | |
| 635 | +M: Oded Gabbay <oded.gabbay@gmail.com> | |
| 636 | 636 | L: dri-devel@lists.freedesktop.org |
| 637 | 637 | T: git git://people.freedesktop.org/~gabbayo/linux.git |
| 638 | 638 | S: Supported |
| @@ -10777,6 +10777,15 @@ F: drivers/block/virtio_blk.c · 10138 unmodified lines | ||
| 10777 | 10777 | F: include/linux/virtio_*.h |
| 10778 | 10778 | F: include/uapi/linux/virtio_*.h |
| 10779 | 10779 | |
| 10780 | +VIRTIO GPU DRIVER | |
| 10781 | +M: David Airlie <airlied@linux.ie> | |
| 10782 | +M: Gerd Hoffmann <kraxel@redhat.com> | |
| 10783 | +L: dri-devel@lists.freedesktop.org | |
| 10784 | +L: virtualization@lists.linux-foundation.org | |
| 10785 | +S: Maintained | |
| 10786 | +F: drivers/gpu/drm/virtio/ | |
| 10787 | +F: include/uapi/linux/virtio_gpu.h | |
| 10788 | + | |
| 10780 | 10789 | VIRTIO HOST (VHOST) |
| 10781 | 10790 | M: "Michael S. Tsirkin" <mst@redhat.com> |
| 10782 | 10791 | L: kvm@vger.kernel.org |
| @@ -166,7 +166,7 @@ · 165 unmodified lines | ||
| 166 | 166 | phys = <&mipi_phy 1>; |
| 167 | 167 | phy-names = "dsim"; |
| 168 | 168 | clocks = <&clock CLK_DSIM0>, <&clock CLK_SCLK_MIPI0>; |
| 169 | − clock-names = "bus_clk", "pll_clk"; | |
| 169 | + clock-names = "bus_clk", "sclk_mipi"; | |
| 170 | 170 | status = "disabled"; |
| 171 | 171 | #address-cells = <1>; |
| 172 | 172 | #size-cells = <0>; |
| @@ -546,6 +546,7 @@ static const struct pci_device_id intel_stolen_ids[] __initconst = { · 545 unmodified lines | ||
| 546 | 546 | INTEL_BDW_D_IDS(&gen8_stolen_funcs), |
| 547 | 547 | INTEL_CHV_IDS(&chv_stolen_funcs), |
| 548 | 548 | INTEL_SKL_IDS(&gen9_stolen_funcs), |
| 549 | + INTEL_BXT_IDS(&gen9_stolen_funcs), | |
| 549 | 550 | }; |
| 550 | 551 | |
| 551 | 552 | static void __init intel_graphics_stolen(int num, int slot, int func) |
| @@ -120,6 +120,27 @@ config DRM_RADEON · 119 unmodified lines | ||
| 120 | 120 | |
| 121 | 121 | source "drivers/gpu/drm/radeon/Kconfig" |
| 122 | 122 | |
| 123 | +config DRM_AMDGPU | |
| 124 | + tristate "AMD GPU" | |
| 125 | + depends on DRM && PCI | |
| 126 | + select FB_CFB_FILLRECT | |
| 127 | + select FB_CFB_COPYAREA | |
| 128 | + select FB_CFB_IMAGEBLIT | |
| 129 | + select FW_LOADER | |
| 130 | + select DRM_KMS_HELPER | |
| 131 | + select DRM_KMS_FB_HELPER | |
| 132 | + select DRM_TTM | |
| 133 | + select POWER_SUPPLY | |
| 134 | + select HWMON | |
| 135 | + select BACKLIGHT_CLASS_DEVICE | |
| 136 | + select INTERVAL_TREE | |
| 137 | + help | |
| 138 | + Choose this option if you have a recent AMD Radeon graphics card. | |
| 139 | + | |
| 140 | + If M is selected, the module will be called amdgpu. | |
| 141 | + | |
| 142 | +source "drivers/gpu/drm/amd/amdgpu/Kconfig" | |
| 143 | + | |
| 123 | 144 | source "drivers/gpu/drm/nouveau/Kconfig" |
| 124 | 145 | |
| 125 | 146 | config DRM_I810 |
| @@ -206,6 +227,8 @@ source "drivers/gpu/drm/qxl/Kconfig" · 80 unmodified lines | ||
| 206 | 227 | |
| 207 | 228 | source "drivers/gpu/drm/bochs/Kconfig" |
| 208 | 229 | |
| 230 | +source "drivers/gpu/drm/virtio/Kconfig" | |
| 231 | + | |
| 209 | 232 | source "drivers/gpu/drm/msm/Kconfig" |
| 210 | 233 | |
| 211 | 234 | source "drivers/gpu/drm/tegra/Kconfig" |
| @@ -2,8 +2,6 @@ · 1 unmodified line | ||
| 2 | 2 | # Makefile for the drm device driver. This driver provides support for the |
| 3 | 3 | # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. |
| 4 | 4 | |
| 5 | −ccflags-y := -Iinclude/drm | |
| 6 | − | |
| 7 | 5 | drm-y := drm_auth.o drm_bufs.o drm_cache.o \ |
| 8 | 6 | drm_context.o drm_dma.o \ |
| 9 | 7 | drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ |
| @@ -39,6 +37,7 @@ obj-$(CONFIG_DRM_TDFX) += tdfx/ · 29 unmodified lines | ||
| 39 | 37 | obj-$(CONFIG_DRM_R128) += r128/ |
| 40 | 38 | obj-$(CONFIG_HSA_AMD) += amd/amdkfd/ |
| 41 | 39 | obj-$(CONFIG_DRM_RADEON)+= radeon/ |
| 40 | +obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/ | |
| 42 | 41 | obj-$(CONFIG_DRM_MGA) += mga/ |
| 43 | 42 | obj-$(CONFIG_DRM_I810) += i810/ |
| 44 | 43 | obj-$(CONFIG_DRM_I915) += i915/ |
| @@ -60,9 +59,10 @@ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ · 15 unmodified lines | ||
| 60 | 59 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ |
| 61 | 60 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ |
| 62 | 61 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ |
| 63 | −obj-$(CONFIG_DRM_TILCDC) += tilcdc/ | |
| 62 | +obj-y += tilcdc/ | |
| 64 | 63 | obj-$(CONFIG_DRM_QXL) += qxl/ |
| 65 | 64 | obj-$(CONFIG_DRM_BOCHS) += bochs/ |
| 65 | +obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ | |
| 66 | 66 | obj-$(CONFIG_DRM_MSM) += msm/ |
| 67 | 67 | obj-$(CONFIG_DRM_TEGRA) += tegra/ |
| 68 | 68 | obj-$(CONFIG_DRM_STI) += sti/ |
| @@ -0,0 +1,17 @@ | ||
| 1 | +config DRM_AMDGPU_CIK | |
| 2 | + bool "Enable amdgpu support for CIK parts" | |
| 3 | + depends on DRM_AMDGPU | |
| 4 | + help | |
| 5 | + Choose this option if you want to enable experimental support | |
| 6 | + for CIK asics. | |
| 7 | + | |
| 8 | + CIK is already supported in radeon. CIK support in amdgpu | |
| 9 | + is for experimentation and testing. | |
| 10 | + | |
| 11 | +config DRM_AMDGPU_USERPTR | |
| 12 | + bool "Always enable userptr write support" | |
| 13 | + depends on DRM_AMDGPU | |
| 14 | + select MMU_NOTIFIER | |
| 15 | + help | |
| 16 | + This option selects CONFIG_MMU_NOTIFIER if it isn't already | |
| 17 | + selected to enabled full userptr support. | |
| @@ -0,0 +1,81 @@ | ||
| 1 | +# | |
| 2 | +# Makefile for the drm device driver. This driver provides support for the | |
| 3 | +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. | |
| 4 | + | |
| 5 | +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/amd/include/asic_reg \ | |
| 6 | + -Idrivers/gpu/drm/amd/include | |
| 7 | + | |
| 8 | +amdgpu-y := amdgpu_drv.o | |
| 9 | + | |
| 10 | +# add KMS driver | |
| 11 | +amdgpu-y += amdgpu_device.o amdgpu_kms.o \ | |
| 12 | + amdgpu_atombios.o atombios_crtc.o amdgpu_connectors.o \ | |
| 13 | + atom.o amdgpu_fence.o amdgpu_ttm.o amdgpu_object.o amdgpu_gart.o \ | |
| 14 | + amdgpu_encoders.o amdgpu_display.o amdgpu_i2c.o \ | |
| 15 | + amdgpu_fb.o amdgpu_gem.o amdgpu_ring.o \ | |
| 16 | + amdgpu_cs.o amdgpu_bios.o amdgpu_benchmark.o amdgpu_test.o \ | |
| 17 | + amdgpu_pm.o atombios_dp.o amdgpu_afmt.o amdgpu_trace_points.o \ | |
| 18 | + atombios_encoders.o amdgpu_semaphore.o amdgpu_sa.o atombios_i2c.o \ | |
| 19 | + amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \ | |
| 20 | + amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o | |
| 21 | + | |
| 22 | +# add asic specific block | |
| 23 | +amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o gmc_v7_0.o cik_ih.o kv_smc.o kv_dpm.o \ | |
| 24 | + ci_smc.o ci_dpm.o dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o | |
| 25 | + | |
| 26 | +amdgpu-y += \ | |
| 27 | + vi.o | |
| 28 | + | |
| 29 | +# add GMC block | |
| 30 | +amdgpu-y += \ | |
| 31 | + gmc_v8_0.o | |
| 32 | + | |
| 33 | +# add IH block | |
| 34 | +amdgpu-y += \ | |
| 35 | + amdgpu_irq.o \ | |
| 36 | + amdgpu_ih.o \ | |
| 37 | + iceland_ih.o \ | |
| 38 | + tonga_ih.o \ | |
| 39 | + cz_ih.o | |
| 40 | + | |
| 41 | +# add SMC block | |
| 42 | +amdgpu-y += \ | |
| 43 | + amdgpu_dpm.o \ | |
| 44 | + cz_smc.o cz_dpm.o \ | |
| 45 | + tonga_smc.o tonga_dpm.o \ | |
| 46 | + iceland_smc.o iceland_dpm.o | |
| 47 | + | |
| 48 | +# add DCE block | |
| 49 | +amdgpu-y += \ | |
| 50 | + dce_v10_0.o \ | |
| 51 | + dce_v11_0.o | |
| 52 | + | |
| 53 | +# add GFX block | |
| 54 | +amdgpu-y += \ | |
| 55 | + amdgpu_gfx.o \ | |
| 56 | + gfx_v8_0.o | |
| 57 | + | |
| 58 | +# add async DMA block | |
| 59 | +amdgpu-y += \ | |
| 60 | + sdma_v2_4.o \ | |
| 61 | + sdma_v3_0.o | |
| 62 | + | |
| 63 | +# add UVD block | |
| 64 | +amdgpu-y += \ | |
| 65 | + amdgpu_uvd.o \ | |
| 66 | + uvd_v5_0.o \ | |
| 67 | + uvd_v6_0.o | |
| 68 | + | |
| 69 | +# add VCE block | |
| 70 | +amdgpu-y += \ | |
| 71 | + amdgpu_vce.o \ | |
| 72 | + vce_v3_0.o | |
| 73 | + | |
| 74 | +amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o | |
| 75 | +amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o | |
| 76 | +amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o | |
| 77 | +amdgpu-$(CONFIG_MMU_NOTIFIER) += amdgpu_mn.o | |
| 78 | + | |
| 79 | +obj-$(CONFIG_DRM_AMDGPU)+= amdgpu.o | |
| 80 | + | |
| 81 | +CFLAGS_amdgpu_trace_points.o := -I$(src) | |
| @@ -0,0 +1,736 @@ | ||
| 1 | +/* | |
| 2 | +* Copyright 2006-2007 Advanced Micro Devices, Inc. | |
| 3 | +* | |
| 4 | +* Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | +* copy of this software and associated documentation files (the "Software"), | |
| 6 | +* to deal in the Software without restriction, including without limitation | |
| 7 | +* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | +* and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | +* Software is furnished to do so, subject to the following conditions: | |
| 10 | +* | |
| 11 | +* The above copyright notice and this permission notice shall be included in | |
| 12 | +* all copies or substantial portions of the Software. | |
| 13 | +* | |
| 14 | +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | +* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | +* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | +* OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | +*/ | |
| 22 | +/* based on stg/asic_reg/drivers/inc/asic_reg/ObjectID.h ver 23 */ | |
| 23 | + | |
| 24 | +#ifndef _OBJECTID_H | |
| 25 | +#define _OBJECTID_H | |
| 26 | + | |
| 27 | +#if defined(_X86_) | |
| 28 | +#pragma pack(1) | |
| 29 | +#endif | |
| 30 | + | |
| 31 | +/****************************************************/ | |
| 32 | +/* Graphics Object Type Definition */ | |
| 33 | +/****************************************************/ | |
| 34 | +#define GRAPH_OBJECT_TYPE_NONE 0x0 | |
| 35 | +#define GRAPH_OBJECT_TYPE_GPU 0x1 | |
| 36 | +#define GRAPH_OBJECT_TYPE_ENCODER 0x2 | |
| 37 | +#define GRAPH_OBJECT_TYPE_CONNECTOR 0x3 | |
| 38 | +#define GRAPH_OBJECT_TYPE_ROUTER 0x4 | |
| 39 | +/* deleted */ | |
| 40 | +#define GRAPH_OBJECT_TYPE_DISPLAY_PATH 0x6 | |
| 41 | +#define GRAPH_OBJECT_TYPE_GENERIC 0x7 | |
| 42 | + | |
| 43 | +/****************************************************/ | |
| 44 | +/* Encoder Object ID Definition */ | |
| 45 | +/****************************************************/ | |
| 46 | +#define ENCODER_OBJECT_ID_NONE 0x00 | |
| 47 | + | |
| 48 | +/* Radeon Class Display Hardware */ | |
| 49 | +#define ENCODER_OBJECT_ID_INTERNAL_LVDS 0x01 | |
| 50 | +#define ENCODER_OBJECT_ID_INTERNAL_TMDS1 0x02 | |
| 51 | +#define ENCODER_OBJECT_ID_INTERNAL_TMDS2 0x03 | |
| 52 | +#define ENCODER_OBJECT_ID_INTERNAL_DAC1 0x04 | |
| 53 | +#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */ | |
| 54 | +#define ENCODER_OBJECT_ID_INTERNAL_SDVOA 0x06 | |
| 55 | +#define ENCODER_OBJECT_ID_INTERNAL_SDVOB 0x07 | |
| 56 | + | |
| 57 | +/* External Third Party Encoders */ | |
| 58 | +#define ENCODER_OBJECT_ID_SI170B 0x08 | |
| 59 | +#define ENCODER_OBJECT_ID_CH7303 0x09 | |
| 60 | +#define ENCODER_OBJECT_ID_CH7301 0x0A | |
| 61 | +#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */ | |
| 62 | +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOA 0x0C | |
| 63 | +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOB 0x0D | |
| 64 | +#define ENCODER_OBJECT_ID_TITFP513 0x0E | |
| 65 | +#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */ | |
| 66 | +#define ENCODER_OBJECT_ID_VT1623 0x10 | |
| 67 | +#define ENCODER_OBJECT_ID_HDMI_SI1930 0x11 | |
| 68 | +#define ENCODER_OBJECT_ID_HDMI_INTERNAL 0x12 | |
| 69 | +#define ENCODER_OBJECT_ID_ALMOND 0x22 | |
| 70 | +#define ENCODER_OBJECT_ID_TRAVIS 0x23 | |
| 71 | +#define ENCODER_OBJECT_ID_NUTMEG 0x22 | |
| 72 | +#define ENCODER_OBJECT_ID_HDMI_ANX9805 0x26 | |
| 73 | + | |
| 74 | +/* Kaleidoscope (KLDSCP) Class Display Hardware (internal) */ | |
| 75 | +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 0x13 | |
| 76 | +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 0x14 | |
| 77 | +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 0x15 | |
| 78 | +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */ | |
| 79 | +#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */ | |
| 80 | +#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */ | |
| 81 | +#define ENCODER_OBJECT_ID_INTERNAL_DDI 0x19 | |
| 82 | +#define ENCODER_OBJECT_ID_VT1625 0x1A | |
| 83 | +#define ENCODER_OBJECT_ID_HDMI_SI1932 0x1B | |
| 84 | +#define ENCODER_OBJECT_ID_DP_AN9801 0x1C | |
| 85 | +#define ENCODER_OBJECT_ID_DP_DP501 0x1D | |
| 86 | +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY 0x1E | |
| 87 | +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA 0x1F | |
| 88 | +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 0x20 | |
| 89 | +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 0x21 | |
| 90 | +#define ENCODER_OBJECT_ID_INTERNAL_VCE 0x24 | |
| 91 | +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 0x25 | |
| 92 | +#define ENCODER_OBJECT_ID_INTERNAL_AMCLK 0x27 | |
| 93 | + | |
| 94 | +#define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO 0xFF | |
| 95 | + | |
| 96 | +/****************************************************/ | |
| 97 | +/* Connector Object ID Definition */ | |
| 98 | +/****************************************************/ | |
| 99 | +#define CONNECTOR_OBJECT_ID_NONE 0x00 | |
| 100 | +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I 0x01 | |
| 101 | +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I 0x02 | |
| 102 | +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D 0x03 | |
| 103 | +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D 0x04 | |
| 104 | +#define CONNECTOR_OBJECT_ID_VGA 0x05 | |
| 105 | +#define CONNECTOR_OBJECT_ID_COMPOSITE 0x06 | |
| 106 | +#define CONNECTOR_OBJECT_ID_SVIDEO 0x07 | |
| 107 | +#define CONNECTOR_OBJECT_ID_YPbPr 0x08 | |
| 108 | +#define CONNECTOR_OBJECT_ID_D_CONNECTOR 0x09 | |
| 109 | +#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */ | |
| 110 | +#define CONNECTOR_OBJECT_ID_SCART 0x0B | |
| 111 | +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_A 0x0C | |
| 112 | +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_B 0x0D | |
| 113 | +#define CONNECTOR_OBJECT_ID_LVDS 0x0E | |
| 114 | +#define CONNECTOR_OBJECT_ID_7PIN_DIN 0x0F | |
| 115 | +#define CONNECTOR_OBJECT_ID_PCIE_CONNECTOR 0x10 | |
| 116 | +#define CONNECTOR_OBJECT_ID_CROSSFIRE 0x11 | |
| 117 | +#define CONNECTOR_OBJECT_ID_HARDCODE_DVI 0x12 | |
| 118 | +#define CONNECTOR_OBJECT_ID_DISPLAYPORT 0x13 | |
| 119 | +#define CONNECTOR_OBJECT_ID_eDP 0x14 | |
| 120 | +#define CONNECTOR_OBJECT_ID_MXM 0x15 | |
| 121 | +#define CONNECTOR_OBJECT_ID_LVDS_eDP 0x16 | |
| 122 | + | |
| 123 | +/* deleted */ | |
| 124 | + | |
| 125 | +/****************************************************/ | |
| 126 | +/* Router Object ID Definition */ | |
| 127 | +/****************************************************/ | |
| 128 | +#define ROUTER_OBJECT_ID_NONE 0x00 | |
| 129 | +#define ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL 0x01 | |
| 130 | + | |
| 131 | +/****************************************************/ | |
| 132 | +/* Generic Object ID Definition */ | |
| 133 | +/****************************************************/ | |
| 134 | +#define GENERIC_OBJECT_ID_NONE 0x00 | |
| 135 | +#define GENERIC_OBJECT_ID_GLSYNC 0x01 | |
| 136 | +#define GENERIC_OBJECT_ID_PX2_NON_DRIVABLE 0x02 | |
| 137 | +#define GENERIC_OBJECT_ID_MXM_OPM 0x03 | |
| 138 | +#define GENERIC_OBJECT_ID_STEREO_PIN 0x04 //This object could show up from Misc Object table, it follows ATOM_OBJECT format, and contains one ATOM_OBJECT_GPIO_CNTL_RECORD for the stereo pin | |
| 139 | + | |
| 140 | +/****************************************************/ | |
| 141 | +/* Graphics Object ENUM ID Definition */ | |
| 142 | +/****************************************************/ | |
| 143 | +#define GRAPH_OBJECT_ENUM_ID1 0x01 | |
| 144 | +#define GRAPH_OBJECT_ENUM_ID2 0x02 | |
| 145 | +#define GRAPH_OBJECT_ENUM_ID3 0x03 | |
| 146 | +#define GRAPH_OBJECT_ENUM_ID4 0x04 | |
| 147 | +#define GRAPH_OBJECT_ENUM_ID5 0x05 | |
| 148 | +#define GRAPH_OBJECT_ENUM_ID6 0x06 | |
| 149 | +#define GRAPH_OBJECT_ENUM_ID7 0x07 | |
| 150 | + | |
| 151 | +/****************************************************/ | |
| 152 | +/* Graphics Object ID Bit definition */ | |
| 153 | +/****************************************************/ | |
| 154 | +#define OBJECT_ID_MASK 0x00FF | |
| 155 | +#define ENUM_ID_MASK 0x0700 | |
| 156 | +#define RESERVED1_ID_MASK 0x0800 | |
| 157 | +#define OBJECT_TYPE_MASK 0x7000 | |
| 158 | +#define RESERVED2_ID_MASK 0x8000 | |
| 159 | + | |
| 160 | +#define OBJECT_ID_SHIFT 0x00 | |
| 161 | +#define ENUM_ID_SHIFT 0x08 | |
| 162 | +#define OBJECT_TYPE_SHIFT 0x0C | |
| 163 | + | |
| 164 | + | |
| 165 | +/****************************************************/ | |
| 166 | +/* Graphics Object family definition */ | |
| 167 | +/****************************************************/ | |
| 168 | +#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \ | |
| 169 | + GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT) | |
| 170 | +/****************************************************/ | |
| 171 | +/* GPU Object ID definition - Shared with BIOS */ | |
| 172 | +/****************************************************/ | |
| 173 | +#define GPU_ENUM_ID1 ( GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\ | |
| 174 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT) | |
| 175 | + | |
| 176 | +/****************************************************/ | |
| 177 | +/* Encoder Object ID definition - Shared with BIOS */ | |
| 178 | +/****************************************************/ | |
| 179 | +/* | |
| 180 | +#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101 | |
| 181 | +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 0x2102 | |
| 182 | +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 0x2103 | |
| 183 | +#define ENCODER_INTERNAL_DAC1_ENUM_ID1 0x2104 | |
| 184 | +#define ENCODER_INTERNAL_DAC2_ENUM_ID1 0x2105 | |
| 185 | +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 0x2106 | |
| 186 | +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 0x2107 | |
| 187 | +#define ENCODER_SIL170B_ENUM_ID1 0x2108 | |
| 188 | +#define ENCODER_CH7303_ENUM_ID1 0x2109 | |
| 189 | +#define ENCODER_CH7301_ENUM_ID1 0x210A | |
| 190 | +#define ENCODER_INTERNAL_DVO1_ENUM_ID1 0x210B | |
| 191 | +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 0x210C | |
| 192 | +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 0x210D | |
| 193 | +#define ENCODER_TITFP513_ENUM_ID1 0x210E | |
| 194 | +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 0x210F | |
| 195 | +#define ENCODER_VT1623_ENUM_ID1 0x2110 | |
| 196 | +#define ENCODER_HDMI_SI1930_ENUM_ID1 0x2111 | |
| 197 | +#define ENCODER_HDMI_INTERNAL_ENUM_ID1 0x2112 | |
| 198 | +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 0x2113 | |
| 199 | +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 0x2114 | |
| 200 | +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 0x2115 | |
| 201 | +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116 | |
| 202 | +#define ENCODER_SI178_ENUM_ID1 0x2117 | |
| 203 | +#define ENCODER_MVPU_FPGA_ENUM_ID1 0x2118 | |
| 204 | +#define ENCODER_INTERNAL_DDI_ENUM_ID1 0x2119 | |
| 205 | +#define ENCODER_VT1625_ENUM_ID1 0x211A | |
| 206 | +#define ENCODER_HDMI_SI1932_ENUM_ID1 0x211B | |
| 207 | +#define ENCODER_ENCODER_DP_AN9801_ENUM_ID1 0x211C | |
| 208 | +#define ENCODER_DP_DP501_ENUM_ID1 0x211D | |
| 209 | +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 0x211E | |
| 210 | +*/ | |
| 211 | +#define ENCODER_INTERNAL_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 212 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 213 | + ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT) | |
| 214 | + | |
| 215 | +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 216 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 217 | + ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT) | |
| 218 | + | |
| 219 | +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 220 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 221 | + ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT) | |
| 222 | + | |
| 223 | +#define ENCODER_INTERNAL_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 224 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 225 | + ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT) | |
| 226 | + | |
| 227 | +#define ENCODER_INTERNAL_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 228 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 229 | + ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT) | |
| 230 | + | |
| 231 | +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 232 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 233 | + ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) | |
| 234 | + | |
| 235 | +#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 236 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 237 | + ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) | |
| 238 | + | |
| 239 | +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 240 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 241 | + ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT) | |
| 242 | + | |
| 243 | +#define ENCODER_SIL170B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 244 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 245 | + ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT) | |
| 246 | + | |
| 247 | +#define ENCODER_CH7303_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 248 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 249 | + ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT) | |
| 250 | + | |
| 251 | +#define ENCODER_CH7301_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 252 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 253 | + ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT) | |
| 254 | + | |
| 255 | +#define ENCODER_INTERNAL_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 256 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 257 | + ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT) | |
| 258 | + | |
| 259 | +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 260 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 261 | + ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) | |
| 262 | + | |
| 263 | +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 264 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 265 | + ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) | |
| 266 | + | |
| 267 | + | |
| 268 | +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 269 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 270 | + ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT) | |
| 271 | + | |
| 272 | + | |
| 273 | +#define ENCODER_TITFP513_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 274 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 275 | + ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT) | |
| 276 | + | |
| 277 | +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 278 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 279 | + ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT) | |
| 280 | + | |
| 281 | +#define ENCODER_VT1623_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 282 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 283 | + ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT) | |
| 284 | + | |
| 285 | +#define ENCODER_HDMI_SI1930_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 286 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 287 | + ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT) | |
| 288 | + | |
| 289 | +#define ENCODER_HDMI_INTERNAL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 290 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 291 | + ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT) | |
| 292 | + | |
| 293 | +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 294 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 295 | + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) | |
| 296 | + | |
| 297 | + | |
| 298 | +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 299 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 300 | + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) | |
| 301 | + | |
| 302 | + | |
| 303 | +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 304 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 305 | + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT) | |
| 306 | + | |
| 307 | +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 308 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 309 | + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT) | |
| 310 | + | |
| 311 | +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 312 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 313 | + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) // Shared with CV/TV and CRT | |
| 314 | + | |
| 315 | +#define ENCODER_SI178_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 316 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 317 | + ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT) | |
| 318 | + | |
| 319 | +#define ENCODER_MVPU_FPGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 320 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 321 | + ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT) | |
| 322 | + | |
| 323 | +#define ENCODER_INTERNAL_DDI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 324 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 325 | + ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT) | |
| 326 | + | |
| 327 | +#define ENCODER_VT1625_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 328 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 329 | + ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT) | |
| 330 | + | |
| 331 | +#define ENCODER_HDMI_SI1932_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 332 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 333 | + ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT) | |
| 334 | + | |
| 335 | +#define ENCODER_DP_DP501_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 336 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 337 | + ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT) | |
| 338 | + | |
| 339 | +#define ENCODER_DP_AN9801_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 340 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 341 | + ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT) | |
| 342 | + | |
| 343 | +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 344 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 345 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) | |
| 346 | + | |
| 347 | +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 348 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 349 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) | |
| 350 | + | |
| 351 | +#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 352 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 353 | + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT) | |
| 354 | + | |
| 355 | +#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 356 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 357 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT) | |
| 358 | + | |
| 359 | +#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 360 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 361 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT) | |
| 362 | + | |
| 363 | +#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 364 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 365 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT) | |
| 366 | + | |
| 367 | +#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 368 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 369 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT) | |
| 370 | + | |
| 371 | +#define ENCODER_INTERNAL_UNIPHY3_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 372 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 373 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 << OBJECT_ID_SHIFT) | |
| 374 | + | |
| 375 | +#define ENCODER_INTERNAL_UNIPHY3_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 376 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 377 | + ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 << OBJECT_ID_SHIFT) | |
| 378 | + | |
| 379 | +#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 380 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 381 | + ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT) | |
| 382 | + | |
| 383 | +#define ENCODER_ALMOND_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 384 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 385 | + ENCODER_OBJECT_ID_ALMOND << OBJECT_ID_SHIFT) | |
| 386 | + | |
| 387 | +#define ENCODER_ALMOND_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 388 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 389 | + ENCODER_OBJECT_ID_ALMOND << OBJECT_ID_SHIFT) | |
| 390 | + | |
| 391 | +#define ENCODER_TRAVIS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 392 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 393 | + ENCODER_OBJECT_ID_TRAVIS << OBJECT_ID_SHIFT) | |
| 394 | + | |
| 395 | +#define ENCODER_TRAVIS_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 396 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 397 | + ENCODER_OBJECT_ID_TRAVIS << OBJECT_ID_SHIFT) | |
| 398 | + | |
| 399 | +#define ENCODER_NUTMEG_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 400 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 401 | + ENCODER_OBJECT_ID_NUTMEG << OBJECT_ID_SHIFT) | |
| 402 | + | |
| 403 | +#define ENCODER_VCE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 404 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 405 | + ENCODER_OBJECT_ID_INTERNAL_VCE << OBJECT_ID_SHIFT) | |
| 406 | + | |
| 407 | +#define ENCODER_HDMI_ANX9805_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ | |
| 408 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 409 | + ENCODER_OBJECT_ID_HDMI_ANX9805 << OBJECT_ID_SHIFT) | |
| 410 | + | |
| 411 | +/****************************************************/ | |
| 412 | +/* Connector Object ID definition - Shared with BIOS */ | |
| 413 | +/****************************************************/ | |
| 414 | +/* | |
| 415 | +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 0x3101 | |
| 416 | +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 0x3102 | |
| 417 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 0x3103 | |
| 418 | +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 0x3104 | |
| 419 | +#define CONNECTOR_VGA_ENUM_ID1 0x3105 | |
| 420 | +#define CONNECTOR_COMPOSITE_ENUM_ID1 0x3106 | |
| 421 | +#define CONNECTOR_SVIDEO_ENUM_ID1 0x3107 | |
| 422 | +#define CONNECTOR_YPbPr_ENUM_ID1 0x3108 | |
| 423 | +#define CONNECTOR_D_CONNECTORE_ENUM_ID1 0x3109 | |
| 424 | +#define CONNECTOR_9PIN_DIN_ENUM_ID1 0x310A | |
| 425 | +#define CONNECTOR_SCART_ENUM_ID1 0x310B | |
| 426 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 0x310C | |
| 427 | +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 0x310D | |
| 428 | +#define CONNECTOR_LVDS_ENUM_ID1 0x310E | |
| 429 | +#define CONNECTOR_7PIN_DIN_ENUM_ID1 0x310F | |
| 430 | +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 0x3110 | |
| 431 | +*/ | |
| 432 | +#define CONNECTOR_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 433 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 434 | + CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT) | |
| 435 | + | |
| 436 | +#define CONNECTOR_LVDS_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 437 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 438 | + CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT) | |
| 439 | + | |
| 440 | +#define CONNECTOR_eDP_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 441 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 442 | + CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT) | |
| 443 | + | |
| 444 | +#define CONNECTOR_eDP_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 445 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 446 | + CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT) | |
| 447 | + | |
| 448 | +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 449 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 450 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) | |
| 451 | + | |
| 452 | +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 453 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 454 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) | |
| 455 | + | |
| 456 | +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 457 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 458 | + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) | |
| 459 | + | |
| 460 | +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 461 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 462 | + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) | |
| 463 | + | |
| 464 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 465 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 466 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 467 | + | |
| 468 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 469 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 470 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 471 | + | |
| 472 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 473 | + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ | |
| 474 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 475 | + | |
| 476 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 477 | + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ | |
| 478 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 479 | + | |
| 480 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 481 | + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ | |
| 482 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 483 | + | |
| 484 | +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 485 | + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ | |
| 486 | + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 487 | + | |
| 488 | +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 489 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 490 | + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 491 | + | |
| 492 | +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 493 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 494 | + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 495 | + | |
| 496 | +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 497 | + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ | |
| 498 | + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 499 | + | |
| 500 | +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 501 | + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ | |
| 502 | + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) | |
| 503 | + | |
| 504 | +#define CONNECTOR_VGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 505 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 506 | + CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) | |
| 507 | + | |
| 508 | +#define CONNECTOR_VGA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 509 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 510 | + CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) | |
| 511 | + | |
| 512 | +#define CONNECTOR_COMPOSITE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 513 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 514 | + CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT) | |
| 515 | + | |
| 516 | +#define CONNECTOR_COMPOSITE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 517 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 518 | + CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT) | |
| 519 | + | |
| 520 | +#define CONNECTOR_SVIDEO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 521 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 522 | + CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT) | |
| 523 | + | |
| 524 | +#define CONNECTOR_SVIDEO_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 525 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 526 | + CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT) | |
| 527 | + | |
| 528 | +#define CONNECTOR_YPbPr_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 529 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 530 | + CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT) | |
| 531 | + | |
| 532 | +#define CONNECTOR_YPbPr_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 533 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 534 | + CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT) | |
| 535 | + | |
| 536 | +#define CONNECTOR_D_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 537 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 538 | + CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT) | |
| 539 | + | |
| 540 | +#define CONNECTOR_D_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 541 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 542 | + CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT) | |
| 543 | + | |
| 544 | +#define CONNECTOR_9PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 545 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 546 | + CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT) | |
| 547 | + | |
| 548 | +#define CONNECTOR_9PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 549 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 550 | + CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT) | |
| 551 | + | |
| 552 | +#define CONNECTOR_SCART_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 553 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 554 | + CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT) | |
| 555 | + | |
| 556 | +#define CONNECTOR_SCART_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 557 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 558 | + CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT) | |
| 559 | + | |
| 560 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 561 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 562 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) | |
| 563 | + | |
| 564 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 565 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 566 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) | |
| 567 | + | |
| 568 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 569 | + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ | |
| 570 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) | |
| 571 | + | |
| 572 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 573 | + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ | |
| 574 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) | |
| 575 | + | |
| 576 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 577 | + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ | |
| 578 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) | |
| 579 | + | |
| 580 | +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 581 | + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ | |
| 582 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) | |
| 583 | + | |
| 584 | +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 585 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 586 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT) | |
| 587 | + | |
| 588 | +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 589 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 590 | + CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT) | |
| 591 | + | |
| 592 | +#define CONNECTOR_7PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 593 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 594 | + CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT) | |
| 595 | + | |
| 596 | +#define CONNECTOR_7PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 597 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 598 | + CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT) | |
| 599 | + | |
| 600 | +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 601 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 602 | + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) | |
| 603 | + | |
| 604 | +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 605 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 606 | + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) | |
| 607 | + | |
| 608 | +#define CONNECTOR_CROSSFIRE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 609 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 610 | + CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) | |
| 611 | + | |
| 612 | +#define CONNECTOR_CROSSFIRE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 613 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 614 | + CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) | |
| 615 | + | |
| 616 | + | |
| 617 | +#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 618 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 619 | + CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) | |
| 620 | + | |
| 621 | +#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 622 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 623 | + CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) | |
| 624 | + | |
| 625 | +#define CONNECTOR_DISPLAYPORT_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 626 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 627 | + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) | |
| 628 | + | |
| 629 | +#define CONNECTOR_DISPLAYPORT_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 630 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 631 | + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) | |
| 632 | + | |
| 633 | +#define CONNECTOR_DISPLAYPORT_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 634 | + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ | |
| 635 | + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) | |
| 636 | + | |
| 637 | +#define CONNECTOR_DISPLAYPORT_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 638 | + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ | |
| 639 | + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) | |
| 640 | + | |
| 641 | +#define CONNECTOR_DISPLAYPORT_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 642 | + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ | |
| 643 | + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) | |
| 644 | + | |
| 645 | +#define CONNECTOR_DISPLAYPORT_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 646 | + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ | |
| 647 | + CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) | |
| 648 | + | |
| 649 | +#define CONNECTOR_MXM_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 650 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 651 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_A | |
| 652 | + | |
| 653 | +#define CONNECTOR_MXM_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 654 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 655 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_B | |
| 656 | + | |
| 657 | +#define CONNECTOR_MXM_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 658 | + GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ | |
| 659 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_C | |
| 660 | + | |
| 661 | +#define CONNECTOR_MXM_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 662 | + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ | |
| 663 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_D | |
| 664 | + | |
| 665 | +#define CONNECTOR_MXM_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 666 | + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ | |
| 667 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_TXxx | |
| 668 | + | |
| 669 | +#define CONNECTOR_MXM_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 670 | + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ | |
| 671 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_UXxx | |
| 672 | + | |
| 673 | +#define CONNECTOR_MXM_ENUM_ID7 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 674 | + GRAPH_OBJECT_ENUM_ID7 << ENUM_ID_SHIFT |\ | |
| 675 | + CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DAC | |
| 676 | + | |
| 677 | +#define CONNECTOR_LVDS_eDP_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 678 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 679 | + CONNECTOR_OBJECT_ID_LVDS_eDP << OBJECT_ID_SHIFT) | |
| 680 | + | |
| 681 | +#define CONNECTOR_LVDS_eDP_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ | |
| 682 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 683 | + CONNECTOR_OBJECT_ID_LVDS_eDP << OBJECT_ID_SHIFT) | |
| 684 | + | |
| 685 | +/****************************************************/ | |
| 686 | +/* Router Object ID definition - Shared with BIOS */ | |
| 687 | +/****************************************************/ | |
| 688 | +#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\ | |
| 689 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 690 | + ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT) | |
| 691 | + | |
| 692 | +/* deleted */ | |
| 693 | + | |
| 694 | +/****************************************************/ | |
| 695 | +/* Generic Object ID definition - Shared with BIOS */ | |
| 696 | +/****************************************************/ | |
| 697 | +#define GENERICOBJECT_GLSYNC_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ | |
| 698 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 699 | + GENERIC_OBJECT_ID_GLSYNC << OBJECT_ID_SHIFT) | |
| 700 | + | |
| 701 | +#define GENERICOBJECT_PX2_NON_DRIVABLE_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ | |
| 702 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 703 | + GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT) | |
| 704 | + | |
| 705 | +#define GENERICOBJECT_PX2_NON_DRIVABLE_ID2 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ | |
| 706 | + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ | |
| 707 | + GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT) | |
| 708 | + | |
| 709 | +#define GENERICOBJECT_MXM_OPM_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ | |
| 710 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 711 | + GENERIC_OBJECT_ID_MXM_OPM << OBJECT_ID_SHIFT) | |
| 712 | + | |
| 713 | +#define GENERICOBJECT_STEREO_PIN_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\ | |
| 714 | + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ | |
| 715 | + GENERIC_OBJECT_ID_STEREO_PIN << OBJECT_ID_SHIFT) | |
| 716 | + | |
| 717 | +/****************************************************/ | |
| 718 | +/* Object Cap definition - Shared with BIOS */ | |
| 719 | +/****************************************************/ | |
| 720 | +#define GRAPHICS_OBJECT_CAP_I2C 0x00000001L | |
| 721 | +#define GRAPHICS_OBJECT_CAP_TABLE_ID 0x00000002L | |
| 722 | + | |
| 723 | + | |
| 724 | +#define GRAPHICS_OBJECT_I2CCOMMAND_TABLE_ID 0x01 | |
| 725 | +#define GRAPHICS_OBJECT_HOTPLUGDETECTIONINTERUPT_TABLE_ID 0x02 | |
| 726 | +#define GRAPHICS_OBJECT_ENCODER_OUTPUT_PROTECTION_TABLE_ID 0x03 | |
| 727 | + | |
| 728 | +#if defined(_X86_) | |
| 729 | +#pragma pack() | |
| 730 | +#endif | |
| 731 | + | |
| 732 | +#endif /*GRAPHICTYPE */ | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| @@ -0,0 +1,2337 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + */ | |
| 28 | +#ifndef __AMDGPU_H__ | |
| 29 | +#define __AMDGPU_H__ | |
| 30 | + | |
| 31 | +#include <linux/atomic.h> | |
| 32 | +#include <linux/wait.h> | |
| 33 | +#include <linux/list.h> | |
| 34 | +#include <linux/kref.h> | |
| 35 | +#include <linux/interval_tree.h> | |
| 36 | +#include <linux/hashtable.h> | |
| 37 | +#include <linux/fence.h> | |
| 38 | + | |
| 39 | +#include <ttm/ttm_bo_api.h> | |
| 40 | +#include <ttm/ttm_bo_driver.h> | |
| 41 | +#include <ttm/ttm_placement.h> | |
| 42 | +#include <ttm/ttm_module.h> | |
| 43 | +#include <ttm/ttm_execbuf_util.h> | |
| 44 | + | |
| 45 | +#include <drm/drm_gem.h> | |
| 46 | +#include <drm/amdgpu_drm.h> | |
| 47 | + | |
| 48 | +#include "amd_shared.h" | |
| 49 | +#include "amdgpu_family.h" | |
| 50 | +#include "amdgpu_mode.h" | |
| 51 | +#include "amdgpu_ih.h" | |
| 52 | +#include "amdgpu_irq.h" | |
| 53 | +#include "amdgpu_ucode.h" | |
| 54 | +#include "amdgpu_gds.h" | |
| 55 | + | |
| 56 | +/* | |
| 57 | + * Modules parameters. | |
| 58 | + */ | |
| 59 | +extern int amdgpu_modeset; | |
| 60 | +extern int amdgpu_vram_limit; | |
| 61 | +extern int amdgpu_gart_size; | |
| 62 | +extern int amdgpu_benchmarking; | |
| 63 | +extern int amdgpu_testing; | |
| 64 | +extern int amdgpu_audio; | |
| 65 | +extern int amdgpu_disp_priority; | |
| 66 | +extern int amdgpu_hw_i2c; | |
| 67 | +extern int amdgpu_pcie_gen2; | |
| 68 | +extern int amdgpu_msi; | |
| 69 | +extern int amdgpu_lockup_timeout; | |
| 70 | +extern int amdgpu_dpm; | |
| 71 | +extern int amdgpu_smc_load_fw; | |
| 72 | +extern int amdgpu_aspm; | |
| 73 | +extern int amdgpu_runtime_pm; | |
| 74 | +extern int amdgpu_hard_reset; | |
| 75 | +extern unsigned amdgpu_ip_block_mask; | |
| 76 | +extern int amdgpu_bapm; | |
| 77 | +extern int amdgpu_deep_color; | |
| 78 | +extern int amdgpu_vm_size; | |
| 79 | +extern int amdgpu_vm_block_size; | |
| 80 | + | |
| 81 | +#define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */ | |
| 82 | +#define AMDGPU_FENCE_JIFFIES_TIMEOUT (HZ / 2) | |
| 83 | +/* AMDGPU_IB_POOL_SIZE must be a power of 2 */ | |
| 84 | +#define AMDGPU_IB_POOL_SIZE 16 | |
| 85 | +#define AMDGPU_DEBUGFS_MAX_COMPONENTS 32 | |
| 86 | +#define AMDGPUFB_CONN_LIMIT 4 | |
| 87 | +#define AMDGPU_BIOS_NUM_SCRATCH 8 | |
| 88 | + | |
| 89 | +/* max number of rings */ | |
| 90 | +#define AMDGPU_MAX_RINGS 16 | |
| 91 | +#define AMDGPU_MAX_GFX_RINGS 1 | |
| 92 | +#define AMDGPU_MAX_COMPUTE_RINGS 8 | |
| 93 | +#define AMDGPU_MAX_VCE_RINGS 2 | |
| 94 | + | |
| 95 | +/* number of hw syncs before falling back on blocking */ | |
| 96 | +#define AMDGPU_NUM_SYNCS 4 | |
| 97 | + | |
| 98 | +/* hardcode that limit for now */ | |
| 99 | +#define AMDGPU_VA_RESERVED_SIZE (8 << 20) | |
| 100 | + | |
| 101 | +/* hard reset data */ | |
| 102 | +#define AMDGPU_ASIC_RESET_DATA 0x39d5e86b | |
| 103 | + | |
| 104 | +/* reset flags */ | |
| 105 | +#define AMDGPU_RESET_GFX (1 << 0) | |
| 106 | +#define AMDGPU_RESET_COMPUTE (1 << 1) | |
| 107 | +#define AMDGPU_RESET_DMA (1 << 2) | |
| 108 | +#define AMDGPU_RESET_CP (1 << 3) | |
| 109 | +#define AMDGPU_RESET_GRBM (1 << 4) | |
| 110 | +#define AMDGPU_RESET_DMA1 (1 << 5) | |
| 111 | +#define AMDGPU_RESET_RLC (1 << 6) | |
| 112 | +#define AMDGPU_RESET_SEM (1 << 7) | |
| 113 | +#define AMDGPU_RESET_IH (1 << 8) | |
| 114 | +#define AMDGPU_RESET_VMC (1 << 9) | |
| 115 | +#define AMDGPU_RESET_MC (1 << 10) | |
| 116 | +#define AMDGPU_RESET_DISPLAY (1 << 11) | |
| 117 | +#define AMDGPU_RESET_UVD (1 << 12) | |
| 118 | +#define AMDGPU_RESET_VCE (1 << 13) | |
| 119 | +#define AMDGPU_RESET_VCE1 (1 << 14) | |
| 120 | + | |
| 121 | +/* CG block flags */ | |
| 122 | +#define AMDGPU_CG_BLOCK_GFX (1 << 0) | |
| 123 | +#define AMDGPU_CG_BLOCK_MC (1 << 1) | |
| 124 | +#define AMDGPU_CG_BLOCK_SDMA (1 << 2) | |
| 125 | +#define AMDGPU_CG_BLOCK_UVD (1 << 3) | |
| 126 | +#define AMDGPU_CG_BLOCK_VCE (1 << 4) | |
| 127 | +#define AMDGPU_CG_BLOCK_HDP (1 << 5) | |
| 128 | +#define AMDGPU_CG_BLOCK_BIF (1 << 6) | |
| 129 | + | |
| 130 | +/* CG flags */ | |
| 131 | +#define AMDGPU_CG_SUPPORT_GFX_MGCG (1 << 0) | |
| 132 | +#define AMDGPU_CG_SUPPORT_GFX_MGLS (1 << 1) | |
| 133 | +#define AMDGPU_CG_SUPPORT_GFX_CGCG (1 << 2) | |
| 134 | +#define AMDGPU_CG_SUPPORT_GFX_CGLS (1 << 3) | |
| 135 | +#define AMDGPU_CG_SUPPORT_GFX_CGTS (1 << 4) | |
| 136 | +#define AMDGPU_CG_SUPPORT_GFX_CGTS_LS (1 << 5) | |
| 137 | +#define AMDGPU_CG_SUPPORT_GFX_CP_LS (1 << 6) | |
| 138 | +#define AMDGPU_CG_SUPPORT_GFX_RLC_LS (1 << 7) | |
| 139 | +#define AMDGPU_CG_SUPPORT_MC_LS (1 << 8) | |
| 140 | +#define AMDGPU_CG_SUPPORT_MC_MGCG (1 << 9) | |
| 141 | +#define AMDGPU_CG_SUPPORT_SDMA_LS (1 << 10) | |
| 142 | +#define AMDGPU_CG_SUPPORT_SDMA_MGCG (1 << 11) | |
| 143 | +#define AMDGPU_CG_SUPPORT_BIF_LS (1 << 12) | |
| 144 | +#define AMDGPU_CG_SUPPORT_UVD_MGCG (1 << 13) | |
| 145 | +#define AMDGPU_CG_SUPPORT_VCE_MGCG (1 << 14) | |
| 146 | +#define AMDGPU_CG_SUPPORT_HDP_LS (1 << 15) | |
| 147 | +#define AMDGPU_CG_SUPPORT_HDP_MGCG (1 << 16) | |
| 148 | + | |
| 149 | +/* PG flags */ | |
| 150 | +#define AMDGPU_PG_SUPPORT_GFX_PG (1 << 0) | |
| 151 | +#define AMDGPU_PG_SUPPORT_GFX_SMG (1 << 1) | |
| 152 | +#define AMDGPU_PG_SUPPORT_GFX_DMG (1 << 2) | |
| 153 | +#define AMDGPU_PG_SUPPORT_UVD (1 << 3) | |
| 154 | +#define AMDGPU_PG_SUPPORT_VCE (1 << 4) | |
| 155 | +#define AMDGPU_PG_SUPPORT_CP (1 << 5) | |
| 156 | +#define AMDGPU_PG_SUPPORT_GDS (1 << 6) | |
| 157 | +#define AMDGPU_PG_SUPPORT_RLC_SMU_HS (1 << 7) | |
| 158 | +#define AMDGPU_PG_SUPPORT_SDMA (1 << 8) | |
| 159 | +#define AMDGPU_PG_SUPPORT_ACP (1 << 9) | |
| 160 | +#define AMDGPU_PG_SUPPORT_SAMU (1 << 10) | |
| 161 | + | |
| 162 | +/* GFX current status */ | |
| 163 | +#define AMDGPU_GFX_NORMAL_MODE 0x00000000L | |
| 164 | +#define AMDGPU_GFX_SAFE_MODE 0x00000001L | |
| 165 | +#define AMDGPU_GFX_PG_DISABLED_MODE 0x00000002L | |
| 166 | +#define AMDGPU_GFX_CG_DISABLED_MODE 0x00000004L | |
| 167 | +#define AMDGPU_GFX_LBPW_DISABLED_MODE 0x00000008L | |
| 168 | + | |
| 169 | +/* max cursor sizes (in pixels) */ | |
| 170 | +#define CIK_CURSOR_WIDTH 128 | |
| 171 | +#define CIK_CURSOR_HEIGHT 128 | |
| 172 | + | |
| 173 | +struct amdgpu_device; | |
| 174 | +struct amdgpu_fence; | |
| 175 | +struct amdgpu_ib; | |
| 176 | +struct amdgpu_vm; | |
| 177 | +struct amdgpu_ring; | |
| 178 | +struct amdgpu_semaphore; | |
| 179 | +struct amdgpu_cs_parser; | |
| 180 | +struct amdgpu_irq_src; | |
| 181 | + | |
| 182 | +enum amdgpu_cp_irq { | |
| 183 | + AMDGPU_CP_IRQ_GFX_EOP = 0, | |
| 184 | + AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP, | |
| 185 | + AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE1_EOP, | |
| 186 | + AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE2_EOP, | |
| 187 | + AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE3_EOP, | |
| 188 | + AMDGPU_CP_IRQ_COMPUTE_MEC2_PIPE0_EOP, | |
| 189 | + AMDGPU_CP_IRQ_COMPUTE_MEC2_PIPE1_EOP, | |
| 190 | + AMDGPU_CP_IRQ_COMPUTE_MEC2_PIPE2_EOP, | |
| 191 | + AMDGPU_CP_IRQ_COMPUTE_MEC2_PIPE3_EOP, | |
| 192 | + | |
| 193 | + AMDGPU_CP_IRQ_LAST | |
| 194 | +}; | |
| 195 | + | |
| 196 | +enum amdgpu_sdma_irq { | |
| 197 | + AMDGPU_SDMA_IRQ_TRAP0 = 0, | |
| 198 | + AMDGPU_SDMA_IRQ_TRAP1, | |
| 199 | + | |
| 200 | + AMDGPU_SDMA_IRQ_LAST | |
| 201 | +}; | |
| 202 | + | |
| 203 | +enum amdgpu_thermal_irq { | |
| 204 | + AMDGPU_THERMAL_IRQ_LOW_TO_HIGH = 0, | |
| 205 | + AMDGPU_THERMAL_IRQ_HIGH_TO_LOW, | |
| 206 | + | |
| 207 | + AMDGPU_THERMAL_IRQ_LAST | |
| 208 | +}; | |
| 209 | + | |
| 210 | +int amdgpu_set_clockgating_state(struct amdgpu_device *adev, | |
| 211 | + enum amd_ip_block_type block_type, | |
| 212 | + enum amd_clockgating_state state); | |
| 213 | +int amdgpu_set_powergating_state(struct amdgpu_device *adev, | |
| 214 | + enum amd_ip_block_type block_type, | |
| 215 | + enum amd_powergating_state state); | |
| 216 | + | |
| 217 | +struct amdgpu_ip_block_version { | |
| 218 | + enum amd_ip_block_type type; | |
| 219 | + u32 major; | |
| 220 | + u32 minor; | |
| 221 | + u32 rev; | |
| 222 | + const struct amd_ip_funcs *funcs; | |
| 223 | +}; | |
| 224 | + | |
| 225 | +int amdgpu_ip_block_version_cmp(struct amdgpu_device *adev, | |
| 226 | + enum amd_ip_block_type type, | |
| 227 | + u32 major, u32 minor); | |
| 228 | + | |
| 229 | +const struct amdgpu_ip_block_version * amdgpu_get_ip_block( | |
| 230 | + struct amdgpu_device *adev, | |
| 231 | + enum amd_ip_block_type type); | |
| 232 | + | |
| 233 | +/* provided by hw blocks that can move/clear data. e.g., gfx or sdma */ | |
| 234 | +struct amdgpu_buffer_funcs { | |
| 235 | + /* maximum bytes in a single operation */ | |
| 236 | + uint32_t copy_max_bytes; | |
| 237 | + | |
| 238 | + /* number of dw to reserve per operation */ | |
| 239 | + unsigned copy_num_dw; | |
| 240 | + | |
| 241 | + /* used for buffer migration */ | |
| 242 | + void (*emit_copy_buffer)(struct amdgpu_ring *ring, | |
| 243 | + /* src addr in bytes */ | |
| 244 | + uint64_t src_offset, | |
| 245 | + /* dst addr in bytes */ | |
| 246 | + uint64_t dst_offset, | |
| 247 | + /* number of byte to transfer */ | |
| 248 | + uint32_t byte_count); | |
| 249 | + | |
| 250 | + /* maximum bytes in a single operation */ | |
| 251 | + uint32_t fill_max_bytes; | |
| 252 | + | |
| 253 | + /* number of dw to reserve per operation */ | |
| 254 | + unsigned fill_num_dw; | |
| 255 | + | |
| 256 | + /* used for buffer clearing */ | |
| 257 | + void (*emit_fill_buffer)(struct amdgpu_ring *ring, | |
| 258 | + /* value to write to memory */ | |
| 259 | + uint32_t src_data, | |
| 260 | + /* dst addr in bytes */ | |
| 261 | + uint64_t dst_offset, | |
| 262 | + /* number of byte to fill */ | |
| 263 | + uint32_t byte_count); | |
| 264 | +}; | |
| 265 | + | |
| 266 | +/* provided by hw blocks that can write ptes, e.g., sdma */ | |
| 267 | +struct amdgpu_vm_pte_funcs { | |
| 268 | + /* copy pte entries from GART */ | |
| 269 | + void (*copy_pte)(struct amdgpu_ib *ib, | |
| 270 | + uint64_t pe, uint64_t src, | |
| 271 | + unsigned count); | |
| 272 | + /* write pte one entry at a time with addr mapping */ | |
| 273 | + void (*write_pte)(struct amdgpu_ib *ib, | |
| 274 | + uint64_t pe, | |
| 275 | + uint64_t addr, unsigned count, | |
| 276 | + uint32_t incr, uint32_t flags); | |
| 277 | + /* for linear pte/pde updates without addr mapping */ | |
| 278 | + void (*set_pte_pde)(struct amdgpu_ib *ib, | |
| 279 | + uint64_t pe, | |
| 280 | + uint64_t addr, unsigned count, | |
| 281 | + uint32_t incr, uint32_t flags); | |
| 282 | + /* pad the indirect buffer to the necessary number of dw */ | |
| 283 | + void (*pad_ib)(struct amdgpu_ib *ib); | |
| 284 | +}; | |
| 285 | + | |
| 286 | +/* provided by the gmc block */ | |
| 287 | +struct amdgpu_gart_funcs { | |
| 288 | + /* flush the vm tlb via mmio */ | |
| 289 | + void (*flush_gpu_tlb)(struct amdgpu_device *adev, | |
| 290 | + uint32_t vmid); | |
| 291 | + /* write pte/pde updates using the cpu */ | |
| 292 | + int (*set_pte_pde)(struct amdgpu_device *adev, | |
| 293 | + void *cpu_pt_addr, /* cpu addr of page table */ | |
| 294 | + uint32_t gpu_page_idx, /* pte/pde to update */ | |
| 295 | + uint64_t addr, /* addr to write into pte/pde */ | |
| 296 | + uint32_t flags); /* access flags */ | |
| 297 | +}; | |
| 298 | + | |
| 299 | +/* provided by the ih block */ | |
| 300 | +struct amdgpu_ih_funcs { | |
| 301 | + /* ring read/write ptr handling, called from interrupt context */ | |
| 302 | + u32 (*get_wptr)(struct amdgpu_device *adev); | |
| 303 | + void (*decode_iv)(struct amdgpu_device *adev, | |
| 304 | + struct amdgpu_iv_entry *entry); | |
| 305 | + void (*set_rptr)(struct amdgpu_device *adev); | |
| 306 | +}; | |
| 307 | + | |
| 308 | +/* provided by hw blocks that expose a ring buffer for commands */ | |
| 309 | +struct amdgpu_ring_funcs { | |
| 310 | + /* ring read/write ptr handling */ | |
| 311 | + u32 (*get_rptr)(struct amdgpu_ring *ring); | |
| 312 | + u32 (*get_wptr)(struct amdgpu_ring *ring); | |
| 313 | + void (*set_wptr)(struct amdgpu_ring *ring); | |
| 314 | + /* validating and patching of IBs */ | |
| 315 | + int (*parse_cs)(struct amdgpu_cs_parser *p, uint32_t ib_idx); | |
| 316 | + /* command emit functions */ | |
| 317 | + void (*emit_ib)(struct amdgpu_ring *ring, | |
| 318 | + struct amdgpu_ib *ib); | |
| 319 | + void (*emit_fence)(struct amdgpu_ring *ring, uint64_t addr, | |
| 320 | + uint64_t seq, unsigned flags); | |
| 321 | + bool (*emit_semaphore)(struct amdgpu_ring *ring, | |
| 322 | + struct amdgpu_semaphore *semaphore, | |
| 323 | + bool emit_wait); | |
| 324 | + void (*emit_vm_flush)(struct amdgpu_ring *ring, unsigned vm_id, | |
| 325 | + uint64_t pd_addr); | |
| 326 | + void (*emit_hdp_flush)(struct amdgpu_ring *ring); | |
| 327 | + void (*emit_gds_switch)(struct amdgpu_ring *ring, uint32_t vmid, | |
| 328 | + uint32_t gds_base, uint32_t gds_size, | |
| 329 | + uint32_t gws_base, uint32_t gws_size, | |
| 330 | + uint32_t oa_base, uint32_t oa_size); | |
| 331 | + /* testing functions */ | |
| 332 | + int (*test_ring)(struct amdgpu_ring *ring); | |
| 333 | + int (*test_ib)(struct amdgpu_ring *ring); | |
| 334 | + bool (*is_lockup)(struct amdgpu_ring *ring); | |
| 335 | +}; | |
| 336 | + | |
| 337 | +/* | |
| 338 | + * BIOS. | |
| 339 | + */ | |
| 340 | +bool amdgpu_get_bios(struct amdgpu_device *adev); | |
| 341 | +bool amdgpu_read_bios(struct amdgpu_device *adev); | |
| 342 | + | |
| 343 | +/* | |
| 344 | + * Dummy page | |
| 345 | + */ | |
| 346 | +struct amdgpu_dummy_page { | |
| 347 | + struct page *page; | |
| 348 | + dma_addr_t addr; | |
| 349 | +}; | |
| 350 | +int amdgpu_dummy_page_init(struct amdgpu_device *adev); | |
| 351 | +void amdgpu_dummy_page_fini(struct amdgpu_device *adev); | |
| 352 | + | |
| 353 | + | |
| 354 | +/* | |
| 355 | + * Clocks | |
| 356 | + */ | |
| 357 | + | |
| 358 | +#define AMDGPU_MAX_PPLL 3 | |
| 359 | + | |
| 360 | +struct amdgpu_clock { | |
| 361 | + struct amdgpu_pll ppll[AMDGPU_MAX_PPLL]; | |
| 362 | + struct amdgpu_pll spll; | |
| 363 | + struct amdgpu_pll mpll; | |
| 364 | + /* 10 Khz units */ | |
| 365 | + uint32_t default_mclk; | |
| 366 | + uint32_t default_sclk; | |
| 367 | + uint32_t default_dispclk; | |
| 368 | + uint32_t current_dispclk; | |
| 369 | + uint32_t dp_extclk; | |
| 370 | + uint32_t max_pixel_clock; | |
| 371 | +}; | |
| 372 | + | |
| 373 | +/* | |
| 374 | + * Fences. | |
| 375 | + */ | |
| 376 | +struct amdgpu_fence_driver { | |
| 377 | + struct amdgpu_ring *ring; | |
| 378 | + uint64_t gpu_addr; | |
| 379 | + volatile uint32_t *cpu_addr; | |
| 380 | + /* sync_seq is protected by ring emission lock */ | |
| 381 | + uint64_t sync_seq[AMDGPU_MAX_RINGS]; | |
| 382 | + atomic64_t last_seq; | |
| 383 | + bool initialized; | |
| 384 | + bool delayed_irq; | |
| 385 | + struct amdgpu_irq_src *irq_src; | |
| 386 | + unsigned irq_type; | |
| 387 | + struct delayed_work lockup_work; | |
| 388 | +}; | |
| 389 | + | |
| 390 | +/* some special values for the owner field */ | |
| 391 | +#define AMDGPU_FENCE_OWNER_UNDEFINED ((void*)0ul) | |
| 392 | +#define AMDGPU_FENCE_OWNER_VM ((void*)1ul) | |
| 393 | +#define AMDGPU_FENCE_OWNER_MOVE ((void*)2ul) | |
| 394 | + | |
| 395 | +#define AMDGPU_FENCE_FLAG_64BIT (1 << 0) | |
| 396 | +#define AMDGPU_FENCE_FLAG_INT (1 << 1) | |
| 397 | + | |
| 398 | +struct amdgpu_fence { | |
| 399 | + struct fence base; | |
| 400 | + | |
| 401 | + /* RB, DMA, etc. */ | |
| 402 | + struct amdgpu_ring *ring; | |
| 403 | + uint64_t seq; | |
| 404 | + | |
| 405 | + /* filp or special value for fence creator */ | |
| 406 | + void *owner; | |
| 407 | + | |
| 408 | + wait_queue_t fence_wake; | |
| 409 | +}; | |
| 410 | + | |
| 411 | +struct amdgpu_user_fence { | |
| 412 | + /* write-back bo */ | |
| 413 | + struct amdgpu_bo *bo; | |
| 414 | + /* write-back address offset to bo start */ | |
| 415 | + uint32_t offset; | |
| 416 | +}; | |
| 417 | + | |
| 418 | +int amdgpu_fence_driver_init(struct amdgpu_device *adev); | |
| 419 | +void amdgpu_fence_driver_fini(struct amdgpu_device *adev); | |
| 420 | +void amdgpu_fence_driver_force_completion(struct amdgpu_device *adev); | |
| 421 | + | |
| 422 | +void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring); | |
| 423 | +int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, | |
| 424 | + struct amdgpu_irq_src *irq_src, | |
| 425 | + unsigned irq_type); | |
| 426 | +int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner, | |
| 427 | + struct amdgpu_fence **fence); | |
| 428 | +void amdgpu_fence_process(struct amdgpu_ring *ring); | |
| 429 | +int amdgpu_fence_wait_next(struct amdgpu_ring *ring); | |
| 430 | +int amdgpu_fence_wait_empty(struct amdgpu_ring *ring); | |
| 431 | +unsigned amdgpu_fence_count_emitted(struct amdgpu_ring *ring); | |
| 432 | + | |
| 433 | +bool amdgpu_fence_signaled(struct amdgpu_fence *fence); | |
| 434 | +int amdgpu_fence_wait(struct amdgpu_fence *fence, bool interruptible); | |
| 435 | +int amdgpu_fence_wait_any(struct amdgpu_device *adev, | |
| 436 | + struct amdgpu_fence **fences, | |
| 437 | + bool intr); | |
| 438 | +long amdgpu_fence_wait_seq_timeout(struct amdgpu_device *adev, | |
| 439 | + u64 *target_seq, bool intr, | |
| 440 | + long timeout); | |
| 441 | +struct amdgpu_fence *amdgpu_fence_ref(struct amdgpu_fence *fence); | |
| 442 | +void amdgpu_fence_unref(struct amdgpu_fence **fence); | |
| 443 | + | |
| 444 | +bool amdgpu_fence_need_sync(struct amdgpu_fence *fence, | |
| 445 | + struct amdgpu_ring *ring); | |
| 446 | +void amdgpu_fence_note_sync(struct amdgpu_fence *fence, | |
| 447 | + struct amdgpu_ring *ring); | |
| 448 | + | |
| 449 | +static inline struct amdgpu_fence *amdgpu_fence_later(struct amdgpu_fence *a, | |
| 450 | + struct amdgpu_fence *b) | |
| 451 | +{ | |
| 452 | + if (!a) { | |
| 453 | + return b; | |
| 454 | + } | |
| 455 | + | |
| 456 | + if (!b) { | |
| 457 | + return a; | |
| 458 | + } | |
| 459 | + | |
| 460 | + BUG_ON(a->ring != b->ring); | |
| 461 | + | |
| 462 | + if (a->seq > b->seq) { | |
| 463 | + return a; | |
| 464 | + } else { | |
| 465 | + return b; | |
| 466 | + } | |
| 467 | +} | |
| 468 | + | |
| 469 | +static inline bool amdgpu_fence_is_earlier(struct amdgpu_fence *a, | |
| 470 | + struct amdgpu_fence *b) | |
| 471 | +{ | |
| 472 | + if (!a) { | |
| 473 | + return false; | |
| 474 | + } | |
| 475 | + | |
| 476 | + if (!b) { | |
| 477 | + return true; | |
| 478 | + } | |
| 479 | + | |
| 480 | + BUG_ON(a->ring != b->ring); | |
| 481 | + | |
| 482 | + return a->seq < b->seq; | |
| 483 | +} | |
| 484 | + | |
| 485 | +int amdgpu_user_fence_emit(struct amdgpu_ring *ring, struct amdgpu_user_fence *user, | |
| 486 | + void *owner, struct amdgpu_fence **fence); | |
| 487 | + | |
| 488 | +/* | |
| 489 | + * TTM. | |
| 490 | + */ | |
| 491 | +struct amdgpu_mman { | |
| 492 | + struct ttm_bo_global_ref bo_global_ref; | |
| 493 | + struct drm_global_reference mem_global_ref; | |
| 494 | + struct ttm_bo_device bdev; | |
| 495 | + bool mem_global_referenced; | |
| 496 | + bool initialized; | |
| 497 | + | |
| 498 | +#if defined(CONFIG_DEBUG_FS) | |
| 499 | + struct dentry *vram; | |
| 500 | + struct dentry *gtt; | |
| 501 | +#endif | |
| 502 | + | |
| 503 | + /* buffer handling */ | |
| 504 | + const struct amdgpu_buffer_funcs *buffer_funcs; | |
| 505 | + struct amdgpu_ring *buffer_funcs_ring; | |
| 506 | +}; | |
| 507 | + | |
| 508 | +int amdgpu_copy_buffer(struct amdgpu_ring *ring, | |
| 509 | + uint64_t src_offset, | |
| 510 | + uint64_t dst_offset, | |
| 511 | + uint32_t byte_count, | |
| 512 | + struct reservation_object *resv, | |
| 513 | + struct amdgpu_fence **fence); | |
| 514 | +int amdgpu_mmap(struct file *filp, struct vm_area_struct *vma); | |
| 515 | + | |
| 516 | +struct amdgpu_bo_list_entry { | |
| 517 | + struct amdgpu_bo *robj; | |
| 518 | + struct ttm_validate_buffer tv; | |
| 519 | + struct amdgpu_bo_va *bo_va; | |
| 520 | + unsigned prefered_domains; | |
| 521 | + unsigned allowed_domains; | |
| 522 | + uint32_t priority; | |
| 523 | +}; | |
| 524 | + | |
| 525 | +struct amdgpu_bo_va_mapping { | |
| 526 | + struct list_head list; | |
| 527 | + struct interval_tree_node it; | |
| 528 | + uint64_t offset; | |
| 529 | + uint32_t flags; | |
| 530 | +}; | |
| 531 | + | |
| 532 | +/* bo virtual addresses in a specific vm */ | |
| 533 | +struct amdgpu_bo_va { | |
| 534 | + /* protected by bo being reserved */ | |
| 535 | + struct list_head bo_list; | |
| 536 | + uint64_t addr; | |
| 537 | + struct amdgpu_fence *last_pt_update; | |
| 538 | + unsigned ref_count; | |
| 539 | + | |
| 540 | + /* protected by vm mutex */ | |
| 541 | + struct list_head mappings; | |
| 542 | + struct list_head vm_status; | |
| 543 | + | |
| 544 | + /* constant after initialization */ | |
| 545 | + struct amdgpu_vm *vm; | |
| 546 | + struct amdgpu_bo *bo; | |
| 547 | +}; | |
| 548 | + | |
| 549 | +#define AMDGPU_GEM_DOMAIN_MAX 0x3 | |
| 550 | + | |
| 551 | +struct amdgpu_bo { | |
| 552 | + /* Protected by gem.mutex */ | |
| 553 | + struct list_head list; | |
| 554 | + /* Protected by tbo.reserved */ | |
| 555 | + u32 initial_domain; | |
| 556 | + struct ttm_place placements[AMDGPU_GEM_DOMAIN_MAX + 1]; | |
| 557 | + struct ttm_placement placement; | |
| 558 | + struct ttm_buffer_object tbo; | |
| 559 | + struct ttm_bo_kmap_obj kmap; | |
| 560 | + u64 flags; | |
| 561 | + unsigned pin_count; | |
| 562 | + void *kptr; | |
| 563 | + u64 tiling_flags; | |
| 564 | + u64 metadata_flags; | |
| 565 | + void *metadata; | |
| 566 | + u32 metadata_size; | |
| 567 | + /* list of all virtual address to which this bo | |
| 568 | + * is associated to | |
| 569 | + */ | |
| 570 | + struct list_head va; | |
| 571 | + /* Constant after initialization */ | |
| 572 | + struct amdgpu_device *adev; | |
| 573 | + struct drm_gem_object gem_base; | |
| 574 | + | |
| 575 | + struct ttm_bo_kmap_obj dma_buf_vmap; | |
| 576 | + pid_t pid; | |
| 577 | + struct amdgpu_mn *mn; | |
| 578 | + struct list_head mn_list; | |
| 579 | +}; | |
| 580 | +#define gem_to_amdgpu_bo(gobj) container_of((gobj), struct amdgpu_bo, gem_base) | |
| 581 | + | |
| 582 | +void amdgpu_gem_object_free(struct drm_gem_object *obj); | |
| 583 | +int amdgpu_gem_object_open(struct drm_gem_object *obj, | |
| 584 | + struct drm_file *file_priv); | |
| 585 | +void amdgpu_gem_object_close(struct drm_gem_object *obj, | |
| 586 | + struct drm_file *file_priv); | |
| 587 | +unsigned long amdgpu_gem_timeout(uint64_t timeout_ns); | |
| 588 | +struct sg_table *amdgpu_gem_prime_get_sg_table(struct drm_gem_object *obj); | |
| 589 | +struct drm_gem_object *amdgpu_gem_prime_import_sg_table(struct drm_device *dev, | |
| 590 | + struct dma_buf_attachment *attach, | |
| 591 | + struct sg_table *sg); | |
| 592 | +struct dma_buf *amdgpu_gem_prime_export(struct drm_device *dev, | |
| 593 | + struct drm_gem_object *gobj, | |
| 594 | + int flags); | |
| 595 | +int amdgpu_gem_prime_pin(struct drm_gem_object *obj); | |
| 596 | +void amdgpu_gem_prime_unpin(struct drm_gem_object *obj); | |
| 597 | +struct reservation_object *amdgpu_gem_prime_res_obj(struct drm_gem_object *); | |
| 598 | +void *amdgpu_gem_prime_vmap(struct drm_gem_object *obj); | |
| 599 | +void amdgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); | |
| 600 | +int amdgpu_gem_debugfs_init(struct amdgpu_device *adev); | |
| 601 | + | |
| 602 | +/* sub-allocation manager, it has to be protected by another lock. | |
| 603 | + * By conception this is an helper for other part of the driver | |
| 604 | + * like the indirect buffer or semaphore, which both have their | |
| 605 | + * locking. | |
| 606 | + * | |
| 607 | + * Principe is simple, we keep a list of sub allocation in offset | |
| 608 | + * order (first entry has offset == 0, last entry has the highest | |
| 609 | + * offset). | |
| 610 | + * | |
| 611 | + * When allocating new object we first check if there is room at | |
| 612 | + * the end total_size - (last_object_offset + last_object_size) >= | |
| 613 | + * alloc_size. If so we allocate new object there. | |
| 614 | + * | |
| 615 | + * When there is not enough room at the end, we start waiting for | |
| 616 | + * each sub object until we reach object_offset+object_size >= | |
| 617 | + * alloc_size, this object then become the sub object we return. | |
| 618 | + * | |
| 619 | + * Alignment can't be bigger than page size. | |
| 620 | + * | |
| 621 | + * Hole are not considered for allocation to keep things simple. | |
| 622 | + * Assumption is that there won't be hole (all object on same | |
| 623 | + * alignment). | |
| 624 | + */ | |
| 625 | +struct amdgpu_sa_manager { | |
| 626 | + wait_queue_head_t wq; | |
| 627 | + struct amdgpu_bo *bo; | |
| 628 | + struct list_head *hole; | |
| 629 | + struct list_head flist[AMDGPU_MAX_RINGS]; | |
| 630 | + struct list_head olist; | |
| 631 | + unsigned size; | |
| 632 | + uint64_t gpu_addr; | |
| 633 | + void *cpu_ptr; | |
| 634 | + uint32_t domain; | |
| 635 | + uint32_t align; | |
| 636 | +}; | |
| 637 | + | |
| 638 | +struct amdgpu_sa_bo; | |
| 639 | + | |
| 640 | +/* sub-allocation buffer */ | |
| 641 | +struct amdgpu_sa_bo { | |
| 642 | + struct list_head olist; | |
| 643 | + struct list_head flist; | |
| 644 | + struct amdgpu_sa_manager *manager; | |
| 645 | + unsigned soffset; | |
| 646 | + unsigned eoffset; | |
| 647 | + struct amdgpu_fence *fence; | |
| 648 | +}; | |
| 649 | + | |
| 650 | +/* | |
| 651 | + * GEM objects. | |
| 652 | + */ | |
| 653 | +struct amdgpu_gem { | |
| 654 | + struct mutex mutex; | |
| 655 | + struct list_head objects; | |
| 656 | +}; | |
| 657 | + | |
| 658 | +int amdgpu_gem_init(struct amdgpu_device *adev); | |
| 659 | +void amdgpu_gem_fini(struct amdgpu_device *adev); | |
| 660 | +int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, | |
| 661 | + int alignment, u32 initial_domain, | |
| 662 | + u64 flags, bool kernel, | |
| 663 | + struct drm_gem_object **obj); | |
| 664 | + | |
| 665 | +int amdgpu_mode_dumb_create(struct drm_file *file_priv, | |
| 666 | + struct drm_device *dev, | |
| 667 | + struct drm_mode_create_dumb *args); | |
| 668 | +int amdgpu_mode_dumb_mmap(struct drm_file *filp, | |
| 669 | + struct drm_device *dev, | |
| 670 | + uint32_t handle, uint64_t *offset_p); | |
| 671 | + | |
| 672 | +/* | |
| 673 | + * Semaphores. | |
| 674 | + */ | |
| 675 | +struct amdgpu_semaphore { | |
| 676 | + struct amdgpu_sa_bo *sa_bo; | |
| 677 | + signed waiters; | |
| 678 | + uint64_t gpu_addr; | |
| 679 | +}; | |
| 680 | + | |
| 681 | +int amdgpu_semaphore_create(struct amdgpu_device *adev, | |
| 682 | + struct amdgpu_semaphore **semaphore); | |
| 683 | +bool amdgpu_semaphore_emit_signal(struct amdgpu_ring *ring, | |
| 684 | + struct amdgpu_semaphore *semaphore); | |
| 685 | +bool amdgpu_semaphore_emit_wait(struct amdgpu_ring *ring, | |
| 686 | + struct amdgpu_semaphore *semaphore); | |
| 687 | +void amdgpu_semaphore_free(struct amdgpu_device *adev, | |
| 688 | + struct amdgpu_semaphore **semaphore, | |
| 689 | + struct amdgpu_fence *fence); | |
| 690 | + | |
| 691 | +/* | |
| 692 | + * Synchronization | |
| 693 | + */ | |
| 694 | +struct amdgpu_sync { | |
| 695 | + struct amdgpu_semaphore *semaphores[AMDGPU_NUM_SYNCS]; | |
| 696 | + struct amdgpu_fence *sync_to[AMDGPU_MAX_RINGS]; | |
| 697 | + struct amdgpu_fence *last_vm_update; | |
| 698 | +}; | |
| 699 | + | |
| 700 | +void amdgpu_sync_create(struct amdgpu_sync *sync); | |
| 701 | +void amdgpu_sync_fence(struct amdgpu_sync *sync, | |
| 702 | + struct amdgpu_fence *fence); | |
| 703 | +int amdgpu_sync_resv(struct amdgpu_device *adev, | |
| 704 | + struct amdgpu_sync *sync, | |
| 705 | + struct reservation_object *resv, | |
| 706 | + void *owner); | |
| 707 | +int amdgpu_sync_rings(struct amdgpu_sync *sync, | |
| 708 | + struct amdgpu_ring *ring); | |
| 709 | +void amdgpu_sync_free(struct amdgpu_device *adev, struct amdgpu_sync *sync, | |
| 710 | + struct amdgpu_fence *fence); | |
| 711 | + | |
| 712 | +/* | |
| 713 | + * GART structures, functions & helpers | |
| 714 | + */ | |
| 715 | +struct amdgpu_mc; | |
| 716 | + | |
| 717 | +#define AMDGPU_GPU_PAGE_SIZE 4096 | |
| 718 | +#define AMDGPU_GPU_PAGE_MASK (AMDGPU_GPU_PAGE_SIZE - 1) | |
| 719 | +#define AMDGPU_GPU_PAGE_SHIFT 12 | |
| 720 | +#define AMDGPU_GPU_PAGE_ALIGN(a) (((a) + AMDGPU_GPU_PAGE_MASK) & ~AMDGPU_GPU_PAGE_MASK) | |
| 721 | + | |
| 722 | +struct amdgpu_gart { | |
| 723 | + dma_addr_t table_addr; | |
| 724 | + struct amdgpu_bo *robj; | |
| 725 | + void *ptr; | |
| 726 | + unsigned num_gpu_pages; | |
| 727 | + unsigned num_cpu_pages; | |
| 728 | + unsigned table_size; | |
| 729 | + struct page **pages; | |
| 730 | + dma_addr_t *pages_addr; | |
| 731 | + bool ready; | |
| 732 | + const struct amdgpu_gart_funcs *gart_funcs; | |
| 733 | +}; | |
| 734 | + | |
| 735 | +int amdgpu_gart_table_ram_alloc(struct amdgpu_device *adev); | |
| 736 | +void amdgpu_gart_table_ram_free(struct amdgpu_device *adev); | |
| 737 | +int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev); | |
| 738 | +void amdgpu_gart_table_vram_free(struct amdgpu_device *adev); | |
| 739 | +int amdgpu_gart_table_vram_pin(struct amdgpu_device *adev); | |
| 740 | +void amdgpu_gart_table_vram_unpin(struct amdgpu_device *adev); | |
| 741 | +int amdgpu_gart_init(struct amdgpu_device *adev); | |
| 742 | +void amdgpu_gart_fini(struct amdgpu_device *adev); | |
| 743 | +void amdgpu_gart_unbind(struct amdgpu_device *adev, unsigned offset, | |
| 744 | + int pages); | |
| 745 | +int amdgpu_gart_bind(struct amdgpu_device *adev, unsigned offset, | |
| 746 | + int pages, struct page **pagelist, | |
| 747 | + dma_addr_t *dma_addr, uint32_t flags); | |
| 748 | + | |
| 749 | +/* | |
| 750 | + * GPU MC structures, functions & helpers | |
| 751 | + */ | |
| 752 | +struct amdgpu_mc { | |
| 753 | + resource_size_t aper_size; | |
| 754 | + resource_size_t aper_base; | |
| 755 | + resource_size_t agp_base; | |
| 756 | + /* for some chips with <= 32MB we need to lie | |
| 757 | + * about vram size near mc fb location */ | |
| 758 | + u64 mc_vram_size; | |
| 759 | + u64 visible_vram_size; | |
| 760 | + u64 gtt_size; | |
| 761 | + u64 gtt_start; | |
| 762 | + u64 gtt_end; | |
| 763 | + u64 vram_start; | |
| 764 | + u64 vram_end; | |
| 765 | + unsigned vram_width; | |
| 766 | + u64 real_vram_size; | |
| 767 | + int vram_mtrr; | |
| 768 | + u64 gtt_base_align; | |
| 769 | + u64 mc_mask; | |
| 770 | + const struct firmware *fw; /* MC firmware */ | |
| 771 | + uint32_t fw_version; | |
| 772 | + struct amdgpu_irq_src vm_fault; | |
| 773 | + uint32_t vram_type; | |
| 774 | +}; | |
| 775 | + | |
| 776 | +/* | |
| 777 | + * GPU doorbell structures, functions & helpers | |
| 778 | + */ | |
| 779 | +typedef enum _AMDGPU_DOORBELL_ASSIGNMENT | |
| 780 | +{ | |
| 781 | + AMDGPU_DOORBELL_KIQ = 0x000, | |
| 782 | + AMDGPU_DOORBELL_HIQ = 0x001, | |
| 783 | + AMDGPU_DOORBELL_DIQ = 0x002, | |
| 784 | + AMDGPU_DOORBELL_MEC_RING0 = 0x010, | |
| 785 | + AMDGPU_DOORBELL_MEC_RING1 = 0x011, | |
| 786 | + AMDGPU_DOORBELL_MEC_RING2 = 0x012, | |
| 787 | + AMDGPU_DOORBELL_MEC_RING3 = 0x013, | |
| 788 | + AMDGPU_DOORBELL_MEC_RING4 = 0x014, | |
| 789 | + AMDGPU_DOORBELL_MEC_RING5 = 0x015, | |
| 790 | + AMDGPU_DOORBELL_MEC_RING6 = 0x016, | |
| 791 | + AMDGPU_DOORBELL_MEC_RING7 = 0x017, | |
| 792 | + AMDGPU_DOORBELL_GFX_RING0 = 0x020, | |
| 793 | + AMDGPU_DOORBELL_sDMA_ENGINE0 = 0x1E0, | |
| 794 | + AMDGPU_DOORBELL_sDMA_ENGINE1 = 0x1E1, | |
| 795 | + AMDGPU_DOORBELL_IH = 0x1E8, | |
| 796 | + AMDGPU_DOORBELL_MAX_ASSIGNMENT = 0x3FF, | |
| 797 | + AMDGPU_DOORBELL_INVALID = 0xFFFF | |
| 798 | +} AMDGPU_DOORBELL_ASSIGNMENT; | |
| 799 | + | |
| 800 | +struct amdgpu_doorbell { | |
| 801 | + /* doorbell mmio */ | |
| 802 | + resource_size_t base; | |
| 803 | + resource_size_t size; | |
| 804 | + u32 __iomem *ptr; | |
| 805 | + u32 num_doorbells; /* Number of doorbells actually reserved for amdgpu. */ | |
| 806 | +}; | |
| 807 | + | |
| 808 | +void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev, | |
| 809 | + phys_addr_t *aperture_base, | |
| 810 | + size_t *aperture_size, | |
| 811 | + size_t *start_offset); | |
| 812 | + | |
| 813 | +/* | |
| 814 | + * IRQS. | |
| 815 | + */ | |
| 816 | + | |
| 817 | +struct amdgpu_flip_work { | |
| 818 | + struct work_struct flip_work; | |
| 819 | + struct work_struct unpin_work; | |
| 820 | + struct amdgpu_device *adev; | |
| 821 | + int crtc_id; | |
| 822 | + uint64_t base; | |
| 823 | + struct drm_pending_vblank_event *event; | |
| 824 | + struct amdgpu_bo *old_rbo; | |
| 825 | + struct fence *fence; | |
| 826 | +}; | |
| 827 | + | |
| 828 | + | |
| 829 | +/* | |
| 830 | + * CP & rings. | |
| 831 | + */ | |
| 832 | + | |
| 833 | +struct amdgpu_ib { | |
| 834 | + struct amdgpu_sa_bo *sa_bo; | |
| 835 | + uint32_t length_dw; | |
| 836 | + uint64_t gpu_addr; | |
| 837 | + uint32_t *ptr; | |
| 838 | + struct amdgpu_ring *ring; | |
| 839 | + struct amdgpu_fence *fence; | |
| 840 | + struct amdgpu_user_fence *user; | |
| 841 | + struct amdgpu_vm *vm; | |
| 842 | + struct amdgpu_ctx *ctx; | |
| 843 | + struct amdgpu_sync sync; | |
| 844 | + uint32_t gds_base, gds_size; | |
| 845 | + uint32_t gws_base, gws_size; | |
| 846 | + uint32_t oa_base, oa_size; | |
| 847 | + uint32_t flags; | |
| 848 | +}; | |
| 849 | + | |
| 850 | +enum amdgpu_ring_type { | |
| 851 | + AMDGPU_RING_TYPE_GFX, | |
| 852 | + AMDGPU_RING_TYPE_COMPUTE, | |
| 853 | + AMDGPU_RING_TYPE_SDMA, | |
| 854 | + AMDGPU_RING_TYPE_UVD, | |
| 855 | + AMDGPU_RING_TYPE_VCE | |
| 856 | +}; | |
| 857 | + | |
| 858 | +struct amdgpu_ring { | |
| 859 | + struct amdgpu_device *adev; | |
| 860 | + const struct amdgpu_ring_funcs *funcs; | |
| 861 | + struct amdgpu_fence_driver fence_drv; | |
| 862 | + | |
| 863 | + struct mutex *ring_lock; | |
| 864 | + struct amdgpu_bo *ring_obj; | |
| 865 | + volatile uint32_t *ring; | |
| 866 | + unsigned rptr_offs; | |
| 867 | + u64 next_rptr_gpu_addr; | |
| 868 | + volatile u32 *next_rptr_cpu_addr; | |
| 869 | + unsigned wptr; | |
| 870 | + unsigned wptr_old; | |
| 871 | + unsigned ring_size; | |
| 872 | + unsigned ring_free_dw; | |
| 873 | + int count_dw; | |
| 874 | + atomic_t last_rptr; | |
| 875 | + atomic64_t last_activity; | |
| 876 | + uint64_t gpu_addr; | |
| 877 | + uint32_t align_mask; | |
| 878 | + uint32_t ptr_mask; | |
| 879 | + bool ready; | |
| 880 | + u32 nop; | |
| 881 | + u32 idx; | |
| 882 | + u64 last_semaphore_signal_addr; | |
| 883 | + u64 last_semaphore_wait_addr; | |
| 884 | + u32 me; | |
| 885 | + u32 pipe; | |
| 886 | + u32 queue; | |
| 887 | + struct amdgpu_bo *mqd_obj; | |
| 888 | + u32 doorbell_index; | |
| 889 | + bool use_doorbell; | |
| 890 | + unsigned wptr_offs; | |
| 891 | + unsigned next_rptr_offs; | |
| 892 | + unsigned fence_offs; | |
| 893 | + struct amdgpu_ctx *current_ctx; | |
| 894 | + enum amdgpu_ring_type type; | |
| 895 | + char name[16]; | |
| 896 | +}; | |
| 897 | + | |
| 898 | +/* | |
| 899 | + * VM | |
| 900 | + */ | |
| 901 | + | |
| 902 | +/* maximum number of VMIDs */ | |
| 903 | +#define AMDGPU_NUM_VM 16 | |
| 904 | + | |
| 905 | +/* number of entries in page table */ | |
| 906 | +#define AMDGPU_VM_PTE_COUNT (1 << amdgpu_vm_block_size) | |
| 907 | + | |
| 908 | +/* PTBs (Page Table Blocks) need to be aligned to 32K */ | |
| 909 | +#define AMDGPU_VM_PTB_ALIGN_SIZE 32768 | |
| 910 | +#define AMDGPU_VM_PTB_ALIGN_MASK (AMDGPU_VM_PTB_ALIGN_SIZE - 1) | |
| 911 | +#define AMDGPU_VM_PTB_ALIGN(a) (((a) + AMDGPU_VM_PTB_ALIGN_MASK) & ~AMDGPU_VM_PTB_ALIGN_MASK) | |
| 912 | + | |
| 913 | +#define AMDGPU_PTE_VALID (1 << 0) | |
| 914 | +#define AMDGPU_PTE_SYSTEM (1 << 1) | |
| 915 | +#define AMDGPU_PTE_SNOOPED (1 << 2) | |
| 916 | + | |
| 917 | +/* VI only */ | |
| 918 | +#define AMDGPU_PTE_EXECUTABLE (1 << 4) | |
| 919 | + | |
| 920 | +#define AMDGPU_PTE_READABLE (1 << 5) | |
| 921 | +#define AMDGPU_PTE_WRITEABLE (1 << 6) | |
| 922 | + | |
| 923 | +/* PTE (Page Table Entry) fragment field for different page sizes */ | |
| 924 | +#define AMDGPU_PTE_FRAG_4KB (0 << 7) | |
| 925 | +#define AMDGPU_PTE_FRAG_64KB (4 << 7) | |
| 926 | +#define AMDGPU_LOG2_PAGES_PER_FRAG 4 | |
| 927 | + | |
| 928 | +struct amdgpu_vm_pt { | |
| 929 | + struct amdgpu_bo *bo; | |
| 930 | + uint64_t addr; | |
| 931 | +}; | |
| 932 | + | |
| 933 | +struct amdgpu_vm_id { | |
| 934 | + unsigned id; | |
| 935 | + uint64_t pd_gpu_addr; | |
| 936 | + /* last flushed PD/PT update */ | |
| 937 | + struct amdgpu_fence *flushed_updates; | |
| 938 | + /* last use of vmid */ | |
| 939 | + struct amdgpu_fence *last_id_use; | |
| 940 | +}; | |
| 941 | + | |
| 942 | +struct amdgpu_vm { | |
| 943 | + struct mutex mutex; | |
| 944 | + | |
| 945 | + struct rb_root va; | |
| 946 | + | |
| 947 | + /* protecting invalidated and freed */ | |
| 948 | + spinlock_t status_lock; | |
| 949 | + | |
| 950 | + /* BOs moved, but not yet updated in the PT */ | |
| 951 | + struct list_head invalidated; | |
| 952 | + | |
| 953 | + /* BOs freed, but not yet updated in the PT */ | |
| 954 | + struct list_head freed; | |
| 955 | + | |
| 956 | + /* contains the page directory */ | |
| 957 | + struct amdgpu_bo *page_directory; | |
| 958 | + unsigned max_pde_used; | |
| 959 | + | |
| 960 | + /* array of page tables, one for each page directory entry */ | |
| 961 | + struct amdgpu_vm_pt *page_tables; | |
| 962 | + | |
| 963 | + /* for id and flush management per ring */ | |
| 964 | + struct amdgpu_vm_id ids[AMDGPU_MAX_RINGS]; | |
| 965 | +}; | |
| 966 | + | |
| 967 | +struct amdgpu_vm_manager { | |
| 968 | + struct amdgpu_fence *active[AMDGPU_NUM_VM]; | |
| 969 | + uint32_t max_pfn; | |
| 970 | + /* number of VMIDs */ | |
| 971 | + unsigned nvm; | |
| 972 | + /* vram base address for page table entry */ | |
| 973 | + u64 vram_base_offset; | |
| 974 | + /* is vm enabled? */ | |
| 975 | + bool enabled; | |
| 976 | + /* for hw to save the PD addr on suspend/resume */ | |
| 977 | + uint32_t saved_table_addr[AMDGPU_NUM_VM]; | |
| 978 | + /* vm pte handling */ | |
| 979 | + const struct amdgpu_vm_pte_funcs *vm_pte_funcs; | |
| 980 | + struct amdgpu_ring *vm_pte_funcs_ring; | |
| 981 | +}; | |
| 982 | + | |
| 983 | +/* | |
| 984 | + * context related structures | |
| 985 | + */ | |
| 986 | + | |
| 987 | +struct amdgpu_ctx_state { | |
| 988 | + uint64_t flags; | |
| 989 | + uint32_t hangs; | |
| 990 | +}; | |
| 991 | + | |
| 992 | +struct amdgpu_ctx { | |
| 993 | + /* call kref_get()before CS start and kref_put() after CS fence signaled */ | |
| 994 | + struct kref refcount; | |
| 995 | + struct amdgpu_fpriv *fpriv; | |
| 996 | + struct amdgpu_ctx_state state; | |
| 997 | + uint32_t id; | |
| 998 | + unsigned reset_counter; | |
| 999 | +}; | |
| 1000 | + | |
| 1001 | +struct amdgpu_ctx_mgr { | |
| 1002 | + struct amdgpu_device *adev; | |
| 1003 | + struct idr ctx_handles; | |
| 1004 | + /* lock for IDR system */ | |
| 1005 | + struct mutex lock; | |
| 1006 | +}; | |
| 1007 | + | |
| 1008 | +/* | |
| 1009 | + * file private structure | |
| 1010 | + */ | |
| 1011 | + | |
| 1012 | +struct amdgpu_fpriv { | |
| 1013 | + struct amdgpu_vm vm; | |
| 1014 | + struct mutex bo_list_lock; | |
| 1015 | + struct idr bo_list_handles; | |
| 1016 | + struct amdgpu_ctx_mgr ctx_mgr; | |
| 1017 | +}; | |
| 1018 | + | |
| 1019 | +/* | |
| 1020 | + * residency list | |
| 1021 | + */ | |
| 1022 | + | |
| 1023 | +struct amdgpu_bo_list { | |
| 1024 | + struct mutex lock; | |
| 1025 | + struct amdgpu_bo *gds_obj; | |
| 1026 | + struct amdgpu_bo *gws_obj; | |
| 1027 | + struct amdgpu_bo *oa_obj; | |
| 1028 | + bool has_userptr; | |
| 1029 | + unsigned num_entries; | |
| 1030 | + struct amdgpu_bo_list_entry *array; | |
| 1031 | +}; | |
| 1032 | + | |
| 1033 | +struct amdgpu_bo_list * | |
| 1034 | +amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id); | |
| 1035 | +void amdgpu_bo_list_put(struct amdgpu_bo_list *list); | |
| 1036 | +void amdgpu_bo_list_free(struct amdgpu_bo_list *list); | |
| 1037 | + | |
| 1038 | +/* | |
| 1039 | + * GFX stuff | |
| 1040 | + */ | |
| 1041 | +#include "clearstate_defs.h" | |
| 1042 | + | |
| 1043 | +struct amdgpu_rlc { | |
| 1044 | + /* for power gating */ | |
| 1045 | + struct amdgpu_bo *save_restore_obj; | |
| 1046 | + uint64_t save_restore_gpu_addr; | |
| 1047 | + volatile uint32_t *sr_ptr; | |
| 1048 | + const u32 *reg_list; | |
| 1049 | + u32 reg_list_size; | |
| 1050 | + /* for clear state */ | |
| 1051 | + struct amdgpu_bo *clear_state_obj; | |
| 1052 | + uint64_t clear_state_gpu_addr; | |
| 1053 | + volatile uint32_t *cs_ptr; | |
| 1054 | + const struct cs_section_def *cs_data; | |
| 1055 | + u32 clear_state_size; | |
| 1056 | + /* for cp tables */ | |
| 1057 | + struct amdgpu_bo *cp_table_obj; | |
| 1058 | + uint64_t cp_table_gpu_addr; | |
| 1059 | + volatile uint32_t *cp_table_ptr; | |
| 1060 | + u32 cp_table_size; | |
| 1061 | +}; | |
| 1062 | + | |
| 1063 | +struct amdgpu_mec { | |
| 1064 | + struct amdgpu_bo *hpd_eop_obj; | |
| 1065 | + u64 hpd_eop_gpu_addr; | |
| 1066 | + u32 num_pipe; | |
| 1067 | + u32 num_mec; | |
| 1068 | + u32 num_queue; | |
| 1069 | +}; | |
| 1070 | + | |
| 1071 | +/* | |
| 1072 | + * GPU scratch registers structures, functions & helpers | |
| 1073 | + */ | |
| 1074 | +struct amdgpu_scratch { | |
| 1075 | + unsigned num_reg; | |
| 1076 | + uint32_t reg_base; | |
| 1077 | + bool free[32]; | |
| 1078 | + uint32_t reg[32]; | |
| 1079 | +}; | |
| 1080 | + | |
| 1081 | +/* | |
| 1082 | + * GFX configurations | |
| 1083 | + */ | |
| 1084 | +struct amdgpu_gca_config { | |
| 1085 | + unsigned max_shader_engines; | |
| 1086 | + unsigned max_tile_pipes; | |
| 1087 | + unsigned max_cu_per_sh; | |
| 1088 | + unsigned max_sh_per_se; | |
| 1089 | + unsigned max_backends_per_se; | |
| 1090 | + unsigned max_texture_channel_caches; | |
| 1091 | + unsigned max_gprs; | |
| 1092 | + unsigned max_gs_threads; | |
| 1093 | + unsigned max_hw_contexts; | |
| 1094 | + unsigned sc_prim_fifo_size_frontend; | |
| 1095 | + unsigned sc_prim_fifo_size_backend; | |
| 1096 | + unsigned sc_hiz_tile_fifo_size; | |
| 1097 | + unsigned sc_earlyz_tile_fifo_size; | |
| 1098 | + | |
| 1099 | + unsigned num_tile_pipes; | |
| 1100 | + unsigned backend_enable_mask; | |
| 1101 | + unsigned mem_max_burst_length_bytes; | |
| 1102 | + unsigned mem_row_size_in_kb; | |
| 1103 | + unsigned shader_engine_tile_size; | |
| 1104 | + unsigned num_gpus; | |
| 1105 | + unsigned multi_gpu_tile_size; | |
| 1106 | + unsigned mc_arb_ramcfg; | |
| 1107 | + unsigned gb_addr_config; | |
| 1108 | + | |
| 1109 | + uint32_t tile_mode_array[32]; | |
| 1110 | + uint32_t macrotile_mode_array[16]; | |
| 1111 | +}; | |
| 1112 | + | |
| 1113 | +struct amdgpu_gfx { | |
| 1114 | + struct mutex gpu_clock_mutex; | |
| 1115 | + struct amdgpu_gca_config config; | |
| 1116 | + struct amdgpu_rlc rlc; | |
| 1117 | + struct amdgpu_mec mec; | |
| 1118 | + struct amdgpu_scratch scratch; | |
| 1119 | + const struct firmware *me_fw; /* ME firmware */ | |
| 1120 | + uint32_t me_fw_version; | |
| 1121 | + const struct firmware *pfp_fw; /* PFP firmware */ | |
| 1122 | + uint32_t pfp_fw_version; | |
| 1123 | + const struct firmware *ce_fw; /* CE firmware */ | |
| 1124 | + uint32_t ce_fw_version; | |
| 1125 | + const struct firmware *rlc_fw; /* RLC firmware */ | |
| 1126 | + uint32_t rlc_fw_version; | |
| 1127 | + const struct firmware *mec_fw; /* MEC firmware */ | |
| 1128 | + uint32_t mec_fw_version; | |
| 1129 | + const struct firmware *mec2_fw; /* MEC2 firmware */ | |
| 1130 | + uint32_t mec2_fw_version; | |
| 1131 | + uint32_t me_feature_version; | |
| 1132 | + uint32_t ce_feature_version; | |
| 1133 | + uint32_t pfp_feature_version; | |
| 1134 | + struct amdgpu_ring gfx_ring[AMDGPU_MAX_GFX_RINGS]; | |
| 1135 | + unsigned num_gfx_rings; | |
| 1136 | + struct amdgpu_ring compute_ring[AMDGPU_MAX_COMPUTE_RINGS]; | |
| 1137 | + unsigned num_compute_rings; | |
| 1138 | + struct amdgpu_irq_src eop_irq; | |
| 1139 | + struct amdgpu_irq_src priv_reg_irq; | |
| 1140 | + struct amdgpu_irq_src priv_inst_irq; | |
| 1141 | + /* gfx status */ | |
| 1142 | + uint32_t gfx_current_status; | |
| 1143 | + /* sync signal for const engine */ | |
| 1144 | + unsigned ce_sync_offs; | |
| 1145 | + /* ce ram size*/ | |
| 1146 | + unsigned ce_ram_size; | |
| 1147 | +}; | |
| 1148 | + | |
| 1149 | +int amdgpu_ib_get(struct amdgpu_ring *ring, struct amdgpu_vm *vm, | |
| 1150 | + unsigned size, struct amdgpu_ib *ib); | |
| 1151 | +void amdgpu_ib_free(struct amdgpu_device *adev, struct amdgpu_ib *ib); | |
| 1152 | +int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs, | |
| 1153 | + struct amdgpu_ib *ib, void *owner); | |
| 1154 | +int amdgpu_ib_pool_init(struct amdgpu_device *adev); | |
| 1155 | +void amdgpu_ib_pool_fini(struct amdgpu_device *adev); | |
| 1156 | +int amdgpu_ib_ring_tests(struct amdgpu_device *adev); | |
| 1157 | +/* Ring access between begin & end cannot sleep */ | |
| 1158 | +void amdgpu_ring_free_size(struct amdgpu_ring *ring); | |
| 1159 | +int amdgpu_ring_alloc(struct amdgpu_ring *ring, unsigned ndw); | |
| 1160 | +int amdgpu_ring_lock(struct amdgpu_ring *ring, unsigned ndw); | |
| 1161 | +void amdgpu_ring_commit(struct amdgpu_ring *ring); | |
| 1162 | +void amdgpu_ring_unlock_commit(struct amdgpu_ring *ring); | |
| 1163 | +void amdgpu_ring_undo(struct amdgpu_ring *ring); | |
| 1164 | +void amdgpu_ring_unlock_undo(struct amdgpu_ring *ring); | |
| 1165 | +void amdgpu_ring_lockup_update(struct amdgpu_ring *ring); | |
| 1166 | +bool amdgpu_ring_test_lockup(struct amdgpu_ring *ring); | |
| 1167 | +unsigned amdgpu_ring_backup(struct amdgpu_ring *ring, | |
| 1168 | + uint32_t **data); | |
| 1169 | +int amdgpu_ring_restore(struct amdgpu_ring *ring, | |
| 1170 | + unsigned size, uint32_t *data); | |
| 1171 | +int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, | |
| 1172 | + unsigned ring_size, u32 nop, u32 align_mask, | |
| 1173 | + struct amdgpu_irq_src *irq_src, unsigned irq_type, | |
| 1174 | + enum amdgpu_ring_type ring_type); | |
| 1175 | +void amdgpu_ring_fini(struct amdgpu_ring *ring); | |
| 1176 | + | |
| 1177 | +/* | |
| 1178 | + * CS. | |
| 1179 | + */ | |
| 1180 | +struct amdgpu_cs_chunk { | |
| 1181 | + uint32_t chunk_id; | |
| 1182 | + uint32_t length_dw; | |
| 1183 | + uint32_t *kdata; | |
| 1184 | + void __user *user_ptr; | |
| 1185 | +}; | |
| 1186 | + | |
| 1187 | +struct amdgpu_cs_parser { | |
| 1188 | + struct amdgpu_device *adev; | |
| 1189 | + struct drm_file *filp; | |
| 1190 | + struct amdgpu_ctx *ctx; | |
| 1191 | + struct amdgpu_bo_list *bo_list; | |
| 1192 | + /* chunks */ | |
| 1193 | + unsigned nchunks; | |
| 1194 | + struct amdgpu_cs_chunk *chunks; | |
| 1195 | + /* relocations */ | |
| 1196 | + struct amdgpu_bo_list_entry *vm_bos; | |
| 1197 | + struct list_head validated; | |
| 1198 | + | |
| 1199 | + struct amdgpu_ib *ibs; | |
| 1200 | + uint32_t num_ibs; | |
| 1201 | + | |
| 1202 | + struct ww_acquire_ctx ticket; | |
| 1203 | + | |
| 1204 | + /* user fence */ | |
| 1205 | + struct amdgpu_user_fence uf; | |
| 1206 | +}; | |
| 1207 | + | |
| 1208 | +static inline u32 amdgpu_get_ib_value(struct amdgpu_cs_parser *p, uint32_t ib_idx, int idx) | |
| 1209 | +{ | |
| 1210 | + return p->ibs[ib_idx].ptr[idx]; | |
| 1211 | +} | |
| 1212 | + | |
| 1213 | +/* | |
| 1214 | + * Writeback | |
| 1215 | + */ | |
| 1216 | +#define AMDGPU_MAX_WB 1024 /* Reserve at most 1024 WB slots for amdgpu-owned rings. */ | |
| 1217 | + | |
| 1218 | +struct amdgpu_wb { | |
| 1219 | + struct amdgpu_bo *wb_obj; | |
| 1220 | + volatile uint32_t *wb; | |
| 1221 | + uint64_t gpu_addr; | |
| 1222 | + u32 num_wb; /* Number of wb slots actually reserved for amdgpu. */ | |
| 1223 | + unsigned long used[DIV_ROUND_UP(AMDGPU_MAX_WB, BITS_PER_LONG)]; | |
| 1224 | +}; | |
| 1225 | + | |
| 1226 | +int amdgpu_wb_get(struct amdgpu_device *adev, u32 *wb); | |
| 1227 | +void amdgpu_wb_free(struct amdgpu_device *adev, u32 wb); | |
| 1228 | + | |
| 1229 | +/** | |
| 1230 | + * struct amdgpu_pm - power management datas | |
| 1231 | + * It keeps track of various data needed to take powermanagement decision. | |
| 1232 | + */ | |
| 1233 | + | |
| 1234 | +enum amdgpu_pm_state_type { | |
| 1235 | + /* not used for dpm */ | |
| 1236 | + POWER_STATE_TYPE_DEFAULT, | |
| 1237 | + POWER_STATE_TYPE_POWERSAVE, | |
| 1238 | + /* user selectable states */ | |
| 1239 | + POWER_STATE_TYPE_BATTERY, | |
| 1240 | + POWER_STATE_TYPE_BALANCED, | |
| 1241 | + POWER_STATE_TYPE_PERFORMANCE, | |
| 1242 | + /* internal states */ | |
| 1243 | + POWER_STATE_TYPE_INTERNAL_UVD, | |
| 1244 | + POWER_STATE_TYPE_INTERNAL_UVD_SD, | |
| 1245 | + POWER_STATE_TYPE_INTERNAL_UVD_HD, | |
| 1246 | + POWER_STATE_TYPE_INTERNAL_UVD_HD2, | |
| 1247 | + POWER_STATE_TYPE_INTERNAL_UVD_MVC, | |
| 1248 | + POWER_STATE_TYPE_INTERNAL_BOOT, | |
| 1249 | + POWER_STATE_TYPE_INTERNAL_THERMAL, | |
| 1250 | + POWER_STATE_TYPE_INTERNAL_ACPI, | |
| 1251 | + POWER_STATE_TYPE_INTERNAL_ULV, | |
| 1252 | + POWER_STATE_TYPE_INTERNAL_3DPERF, | |
| 1253 | +}; | |
| 1254 | + | |
| 1255 | +enum amdgpu_int_thermal_type { | |
| 1256 | + THERMAL_TYPE_NONE, | |
| 1257 | + THERMAL_TYPE_EXTERNAL, | |
| 1258 | + THERMAL_TYPE_EXTERNAL_GPIO, | |
| 1259 | + THERMAL_TYPE_RV6XX, | |
| 1260 | + THERMAL_TYPE_RV770, | |
| 1261 | + THERMAL_TYPE_ADT7473_WITH_INTERNAL, | |
| 1262 | + THERMAL_TYPE_EVERGREEN, | |
| 1263 | + THERMAL_TYPE_SUMO, | |
| 1264 | + THERMAL_TYPE_NI, | |
| 1265 | + THERMAL_TYPE_SI, | |
| 1266 | + THERMAL_TYPE_EMC2103_WITH_INTERNAL, | |
| 1267 | + THERMAL_TYPE_CI, | |
| 1268 | + THERMAL_TYPE_KV, | |
| 1269 | +}; | |
| 1270 | + | |
| 1271 | +enum amdgpu_dpm_auto_throttle_src { | |
| 1272 | + AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL, | |
| 1273 | + AMDGPU_DPM_AUTO_THROTTLE_SRC_EXTERNAL | |
| 1274 | +}; | |
| 1275 | + | |
| 1276 | +enum amdgpu_dpm_event_src { | |
| 1277 | + AMDGPU_DPM_EVENT_SRC_ANALOG = 0, | |
| 1278 | + AMDGPU_DPM_EVENT_SRC_EXTERNAL = 1, | |
| 1279 | + AMDGPU_DPM_EVENT_SRC_DIGITAL = 2, | |
| 1280 | + AMDGPU_DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3, | |
| 1281 | + AMDGPU_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL = 4 | |
| 1282 | +}; | |
| 1283 | + | |
| 1284 | +#define AMDGPU_MAX_VCE_LEVELS 6 | |
| 1285 | + | |
| 1286 | +enum amdgpu_vce_level { | |
| 1287 | + AMDGPU_VCE_LEVEL_AC_ALL = 0, /* AC, All cases */ | |
| 1288 | + AMDGPU_VCE_LEVEL_DC_EE = 1, /* DC, entropy encoding */ | |
| 1289 | + AMDGPU_VCE_LEVEL_DC_LL_LOW = 2, /* DC, low latency queue, res <= 720 */ | |
| 1290 | + AMDGPU_VCE_LEVEL_DC_LL_HIGH = 3, /* DC, low latency queue, 1080 >= res > 720 */ | |
| 1291 | + AMDGPU_VCE_LEVEL_DC_GP_LOW = 4, /* DC, general purpose queue, res <= 720 */ | |
| 1292 | + AMDGPU_VCE_LEVEL_DC_GP_HIGH = 5, /* DC, general purpose queue, 1080 >= res > 720 */ | |
| 1293 | +}; | |
| 1294 | + | |
| 1295 | +struct amdgpu_ps { | |
| 1296 | + u32 caps; /* vbios flags */ | |
| 1297 | + u32 class; /* vbios flags */ | |
| 1298 | + u32 class2; /* vbios flags */ | |
| 1299 | + /* UVD clocks */ | |
| 1300 | + u32 vclk; | |
| 1301 | + u32 dclk; | |
| 1302 | + /* VCE clocks */ | |
| 1303 | + u32 evclk; | |
| 1304 | + u32 ecclk; | |
| 1305 | + bool vce_active; | |
| 1306 | + enum amdgpu_vce_level vce_level; | |
| 1307 | + /* asic priv */ | |
| 1308 | + void *ps_priv; | |
| 1309 | +}; | |
| 1310 | + | |
| 1311 | +struct amdgpu_dpm_thermal { | |
| 1312 | + /* thermal interrupt work */ | |
| 1313 | + struct work_struct work; | |
| 1314 | + /* low temperature threshold */ | |
| 1315 | + int min_temp; | |
| 1316 | + /* high temperature threshold */ | |
| 1317 | + int max_temp; | |
| 1318 | + /* was last interrupt low to high or high to low */ | |
| 1319 | + bool high_to_low; | |
| 1320 | + /* interrupt source */ | |
| 1321 | + struct amdgpu_irq_src irq; | |
| 1322 | +}; | |
| 1323 | + | |
| 1324 | +enum amdgpu_clk_action | |
| 1325 | +{ | |
| 1326 | + AMDGPU_SCLK_UP = 1, | |
| 1327 | + AMDGPU_SCLK_DOWN | |
| 1328 | +}; | |
| 1329 | + | |
| 1330 | +struct amdgpu_blacklist_clocks | |
| 1331 | +{ | |
| 1332 | + u32 sclk; | |
| 1333 | + u32 mclk; | |
| 1334 | + enum amdgpu_clk_action action; | |
| 1335 | +}; | |
| 1336 | + | |
| 1337 | +struct amdgpu_clock_and_voltage_limits { | |
| 1338 | + u32 sclk; | |
| 1339 | + u32 mclk; | |
| 1340 | + u16 vddc; | |
| 1341 | + u16 vddci; | |
| 1342 | +}; | |
| 1343 | + | |
| 1344 | +struct amdgpu_clock_array { | |
| 1345 | + u32 count; | |
| 1346 | + u32 *values; | |
| 1347 | +}; | |
| 1348 | + | |
| 1349 | +struct amdgpu_clock_voltage_dependency_entry { | |
| 1350 | + u32 clk; | |
| 1351 | + u16 v; | |
| 1352 | +}; | |
| 1353 | + | |
| 1354 | +struct amdgpu_clock_voltage_dependency_table { | |
| 1355 | + u32 count; | |
| 1356 | + struct amdgpu_clock_voltage_dependency_entry *entries; | |
| 1357 | +}; | |
| 1358 | + | |
| 1359 | +union amdgpu_cac_leakage_entry { | |
| 1360 | + struct { | |
| 1361 | + u16 vddc; | |
| 1362 | + u32 leakage; | |
| 1363 | + }; | |
| 1364 | + struct { | |
| 1365 | + u16 vddc1; | |
| 1366 | + u16 vddc2; | |
| 1367 | + u16 vddc3; | |
| 1368 | + }; | |
| 1369 | +}; | |
| 1370 | + | |
| 1371 | +struct amdgpu_cac_leakage_table { | |
| 1372 | + u32 count; | |
| 1373 | + union amdgpu_cac_leakage_entry *entries; | |
| 1374 | +}; | |
| 1375 | + | |
| 1376 | +struct amdgpu_phase_shedding_limits_entry { | |
| 1377 | + u16 voltage; | |
| 1378 | + u32 sclk; | |
| 1379 | + u32 mclk; | |
| 1380 | +}; | |
| 1381 | + | |
| 1382 | +struct amdgpu_phase_shedding_limits_table { | |
| 1383 | + u32 count; | |
| 1384 | + struct amdgpu_phase_shedding_limits_entry *entries; | |
| 1385 | +}; | |
| 1386 | + | |
| 1387 | +struct amdgpu_uvd_clock_voltage_dependency_entry { | |
| 1388 | + u32 vclk; | |
| 1389 | + u32 dclk; | |
| 1390 | + u16 v; | |
| 1391 | +}; | |
| 1392 | + | |
| 1393 | +struct amdgpu_uvd_clock_voltage_dependency_table { | |
| 1394 | + u8 count; | |
| 1395 | + struct amdgpu_uvd_clock_voltage_dependency_entry *entries; | |
| 1396 | +}; | |
| 1397 | + | |
| 1398 | +struct amdgpu_vce_clock_voltage_dependency_entry { | |
| 1399 | + u32 ecclk; | |
| 1400 | + u32 evclk; | |
| 1401 | + u16 v; | |
| 1402 | +}; | |
| 1403 | + | |
| 1404 | +struct amdgpu_vce_clock_voltage_dependency_table { | |
| 1405 | + u8 count; | |
| 1406 | + struct amdgpu_vce_clock_voltage_dependency_entry *entries; | |
| 1407 | +}; | |
| 1408 | + | |
| 1409 | +struct amdgpu_ppm_table { | |
| 1410 | + u8 ppm_design; | |
| 1411 | + u16 cpu_core_number; | |
| 1412 | + u32 platform_tdp; | |
| 1413 | + u32 small_ac_platform_tdp; | |
| 1414 | + u32 platform_tdc; | |
| 1415 | + u32 small_ac_platform_tdc; | |
| 1416 | + u32 apu_tdp; | |
| 1417 | + u32 dgpu_tdp; | |
| 1418 | + u32 dgpu_ulv_power; | |
| 1419 | + u32 tj_max; | |
| 1420 | +}; | |
| 1421 | + | |
| 1422 | +struct amdgpu_cac_tdp_table { | |
| 1423 | + u16 tdp; | |
| 1424 | + u16 configurable_tdp; | |
| 1425 | + u16 tdc; | |
| 1426 | + u16 battery_power_limit; | |
| 1427 | + u16 small_power_limit; | |
| 1428 | + u16 low_cac_leakage; | |
| 1429 | + u16 high_cac_leakage; | |
| 1430 | + u16 maximum_power_delivery_limit; | |
| 1431 | +}; | |
| 1432 | + | |
| 1433 | +struct amdgpu_dpm_dynamic_state { | |
| 1434 | + struct amdgpu_clock_voltage_dependency_table vddc_dependency_on_sclk; | |
| 1435 | + struct amdgpu_clock_voltage_dependency_table vddci_dependency_on_mclk; | |
| 1436 | + struct amdgpu_clock_voltage_dependency_table vddc_dependency_on_mclk; | |
| 1437 | + struct amdgpu_clock_voltage_dependency_table mvdd_dependency_on_mclk; | |
| 1438 | + struct amdgpu_clock_voltage_dependency_table vddc_dependency_on_dispclk; | |
| 1439 | + struct amdgpu_uvd_clock_voltage_dependency_table uvd_clock_voltage_dependency_table; | |
| 1440 | + struct amdgpu_vce_clock_voltage_dependency_table vce_clock_voltage_dependency_table; | |
| 1441 | + struct amdgpu_clock_voltage_dependency_table samu_clock_voltage_dependency_table; | |
| 1442 | + struct amdgpu_clock_voltage_dependency_table acp_clock_voltage_dependency_table; | |
| 1443 | + struct amdgpu_clock_voltage_dependency_table vddgfx_dependency_on_sclk; | |
| 1444 | + struct amdgpu_clock_array valid_sclk_values; | |
| 1445 | + struct amdgpu_clock_array valid_mclk_values; | |
| 1446 | + struct amdgpu_clock_and_voltage_limits max_clock_voltage_on_dc; | |
| 1447 | + struct amdgpu_clock_and_voltage_limits max_clock_voltage_on_ac; | |
| 1448 | + u32 mclk_sclk_ratio; | |
| 1449 | + u32 sclk_mclk_delta; | |
| 1450 | + u16 vddc_vddci_delta; | |
| 1451 | + u16 min_vddc_for_pcie_gen2; | |
| 1452 | + struct amdgpu_cac_leakage_table cac_leakage_table; | |
| 1453 | + struct amdgpu_phase_shedding_limits_table phase_shedding_limits_table; | |
| 1454 | + struct amdgpu_ppm_table *ppm_table; | |
| 1455 | + struct amdgpu_cac_tdp_table *cac_tdp_table; | |
| 1456 | +}; | |
| 1457 | + | |
| 1458 | +struct amdgpu_dpm_fan { | |
| 1459 | + u16 t_min; | |
| 1460 | + u16 t_med; | |
| 1461 | + u16 t_high; | |
| 1462 | + u16 pwm_min; | |
| 1463 | + u16 pwm_med; | |
| 1464 | + u16 pwm_high; | |
| 1465 | + u8 t_hyst; | |
| 1466 | + u32 cycle_delay; | |
| 1467 | + u16 t_max; | |
| 1468 | + u8 control_mode; | |
| 1469 | + u16 default_max_fan_pwm; | |
| 1470 | + u16 default_fan_output_sensitivity; | |
| 1471 | + u16 fan_output_sensitivity; | |
| 1472 | + bool ucode_fan_control; | |
| 1473 | +}; | |
| 1474 | + | |
| 1475 | +enum amdgpu_pcie_gen { | |
| 1476 | + AMDGPU_PCIE_GEN1 = 0, | |
| 1477 | + AMDGPU_PCIE_GEN2 = 1, | |
| 1478 | + AMDGPU_PCIE_GEN3 = 2, | |
| 1479 | + AMDGPU_PCIE_GEN_INVALID = 0xffff | |
| 1480 | +}; | |
| 1481 | + | |
| 1482 | +enum amdgpu_dpm_forced_level { | |
| 1483 | + AMDGPU_DPM_FORCED_LEVEL_AUTO = 0, | |
| 1484 | + AMDGPU_DPM_FORCED_LEVEL_LOW = 1, | |
| 1485 | + AMDGPU_DPM_FORCED_LEVEL_HIGH = 2, | |
| 1486 | +}; | |
| 1487 | + | |
| 1488 | +struct amdgpu_vce_state { | |
| 1489 | + /* vce clocks */ | |
| 1490 | + u32 evclk; | |
| 1491 | + u32 ecclk; | |
| 1492 | + /* gpu clocks */ | |
| 1493 | + u32 sclk; | |
| 1494 | + u32 mclk; | |
| 1495 | + u8 clk_idx; | |
| 1496 | + u8 pstate; | |
| 1497 | +}; | |
| 1498 | + | |
| 1499 | +struct amdgpu_dpm_funcs { | |
| 1500 | + int (*get_temperature)(struct amdgpu_device *adev); | |
| 1501 | + int (*pre_set_power_state)(struct amdgpu_device *adev); | |
| 1502 | + int (*set_power_state)(struct amdgpu_device *adev); | |
| 1503 | + void (*post_set_power_state)(struct amdgpu_device *adev); | |
| 1504 | + void (*display_configuration_changed)(struct amdgpu_device *adev); | |
| 1505 | + u32 (*get_sclk)(struct amdgpu_device *adev, bool low); | |
| 1506 | + u32 (*get_mclk)(struct amdgpu_device *adev, bool low); | |
| 1507 | + void (*print_power_state)(struct amdgpu_device *adev, struct amdgpu_ps *ps); | |
| 1508 | + void (*debugfs_print_current_performance_level)(struct amdgpu_device *adev, struct seq_file *m); | |
| 1509 | + int (*force_performance_level)(struct amdgpu_device *adev, enum amdgpu_dpm_forced_level level); | |
| 1510 | + bool (*vblank_too_short)(struct amdgpu_device *adev); | |
| 1511 | + void (*powergate_uvd)(struct amdgpu_device *adev, bool gate); | |
| 1512 | + void (*powergate_vce)(struct amdgpu_device *adev, bool gate); | |
| 1513 | + void (*enable_bapm)(struct amdgpu_device *adev, bool enable); | |
| 1514 | + void (*set_fan_control_mode)(struct amdgpu_device *adev, u32 mode); | |
| 1515 | + u32 (*get_fan_control_mode)(struct amdgpu_device *adev); | |
| 1516 | + int (*set_fan_speed_percent)(struct amdgpu_device *adev, u32 speed); | |
| 1517 | + int (*get_fan_speed_percent)(struct amdgpu_device *adev, u32 *speed); | |
| 1518 | +}; | |
| 1519 | + | |
| 1520 | +struct amdgpu_dpm { | |
| 1521 | + struct amdgpu_ps *ps; | |
| 1522 | + /* number of valid power states */ | |
| 1523 | + int num_ps; | |
| 1524 | + /* current power state that is active */ | |
| 1525 | + struct amdgpu_ps *current_ps; | |
| 1526 | + /* requested power state */ | |
| 1527 | + struct amdgpu_ps *requested_ps; | |
| 1528 | + /* boot up power state */ | |
| 1529 | + struct amdgpu_ps *boot_ps; | |
| 1530 | + /* default uvd power state */ | |
| 1531 | + struct amdgpu_ps *uvd_ps; | |
| 1532 | + /* vce requirements */ | |
| 1533 | + struct amdgpu_vce_state vce_states[AMDGPU_MAX_VCE_LEVELS]; | |
| 1534 | + enum amdgpu_vce_level vce_level; | |
| 1535 | + enum amdgpu_pm_state_type state; | |
| 1536 | + enum amdgpu_pm_state_type user_state; | |
| 1537 | + u32 platform_caps; | |
| 1538 | + u32 voltage_response_time; | |
| 1539 | + u32 backbias_response_time; | |
| 1540 | + void *priv; | |
| 1541 | + u32 new_active_crtcs; | |
| 1542 | + int new_active_crtc_count; | |
| 1543 | + u32 current_active_crtcs; | |
| 1544 | + int current_active_crtc_count; | |
| 1545 | + struct amdgpu_dpm_dynamic_state dyn_state; | |
| 1546 | + struct amdgpu_dpm_fan fan; | |
| 1547 | + u32 tdp_limit; | |
| 1548 | + u32 near_tdp_limit; | |
| 1549 | + u32 near_tdp_limit_adjusted; | |
| 1550 | + u32 sq_ramping_threshold; | |
| 1551 | + u32 cac_leakage; | |
| 1552 | + u16 tdp_od_limit; | |
| 1553 | + u32 tdp_adjustment; | |
| 1554 | + u16 load_line_slope; | |
| 1555 | + bool power_control; | |
| 1556 | + bool ac_power; | |
| 1557 | + /* special states active */ | |
| 1558 | + bool thermal_active; | |
| 1559 | + bool uvd_active; | |
| 1560 | + bool vce_active; | |
| 1561 | + /* thermal handling */ | |
| 1562 | + struct amdgpu_dpm_thermal thermal; | |
| 1563 | + /* forced levels */ | |
| 1564 | + enum amdgpu_dpm_forced_level forced_level; | |
| 1565 | +}; | |
| 1566 | + | |
| 1567 | +struct amdgpu_pm { | |
| 1568 | + struct mutex mutex; | |
| 1569 | + u32 current_sclk; | |
| 1570 | + u32 current_mclk; | |
| 1571 | + u32 default_sclk; | |
| 1572 | + u32 default_mclk; | |
| 1573 | + struct amdgpu_i2c_chan *i2c_bus; | |
| 1574 | + /* internal thermal controller on rv6xx+ */ | |
| 1575 | + enum amdgpu_int_thermal_type int_thermal_type; | |
| 1576 | + struct device *int_hwmon_dev; | |
| 1577 | + /* fan control parameters */ | |
| 1578 | + bool no_fan; | |
| 1579 | + u8 fan_pulses_per_revolution; | |
| 1580 | + u8 fan_min_rpm; | |
| 1581 | + u8 fan_max_rpm; | |
| 1582 | + /* dpm */ | |
| 1583 | + bool dpm_enabled; | |
| 1584 | + struct amdgpu_dpm dpm; | |
| 1585 | + const struct firmware *fw; /* SMC firmware */ | |
| 1586 | + uint32_t fw_version; | |
| 1587 | + const struct amdgpu_dpm_funcs *funcs; | |
| 1588 | +}; | |
| 1589 | + | |
| 1590 | +/* | |
| 1591 | + * UVD | |
| 1592 | + */ | |
| 1593 | +#define AMDGPU_MAX_UVD_HANDLES 10 | |
| 1594 | +#define AMDGPU_UVD_STACK_SIZE (1024*1024) | |
| 1595 | +#define AMDGPU_UVD_HEAP_SIZE (1024*1024) | |
| 1596 | +#define AMDGPU_UVD_FIRMWARE_OFFSET 256 | |
| 1597 | + | |
| 1598 | +struct amdgpu_uvd { | |
| 1599 | + struct amdgpu_bo *vcpu_bo; | |
| 1600 | + void *cpu_addr; | |
| 1601 | + uint64_t gpu_addr; | |
| 1602 | + void *saved_bo; | |
| 1603 | + atomic_t handles[AMDGPU_MAX_UVD_HANDLES]; | |
| 1604 | + struct drm_file *filp[AMDGPU_MAX_UVD_HANDLES]; | |
| 1605 | + struct delayed_work idle_work; | |
| 1606 | + const struct firmware *fw; /* UVD firmware */ | |
| 1607 | + struct amdgpu_ring ring; | |
| 1608 | + struct amdgpu_irq_src irq; | |
| 1609 | + bool address_64_bit; | |
| 1610 | +}; | |
| 1611 | + | |
| 1612 | +/* | |
| 1613 | + * VCE | |
| 1614 | + */ | |
| 1615 | +#define AMDGPU_MAX_VCE_HANDLES 16 | |
| 1616 | +#define AMDGPU_VCE_FIRMWARE_OFFSET 256 | |
| 1617 | + | |
| 1618 | +struct amdgpu_vce { | |
| 1619 | + struct amdgpu_bo *vcpu_bo; | |
| 1620 | + uint64_t gpu_addr; | |
| 1621 | + unsigned fw_version; | |
| 1622 | + unsigned fb_version; | |
| 1623 | + atomic_t handles[AMDGPU_MAX_VCE_HANDLES]; | |
| 1624 | + struct drm_file *filp[AMDGPU_MAX_VCE_HANDLES]; | |
| 1625 | + struct delayed_work idle_work; | |
| 1626 | + const struct firmware *fw; /* VCE firmware */ | |
| 1627 | + struct amdgpu_ring ring[AMDGPU_MAX_VCE_RINGS]; | |
| 1628 | + struct amdgpu_irq_src irq; | |
| 1629 | +}; | |
| 1630 | + | |
| 1631 | +/* | |
| 1632 | + * SDMA | |
| 1633 | + */ | |
| 1634 | +struct amdgpu_sdma { | |
| 1635 | + /* SDMA firmware */ | |
| 1636 | + const struct firmware *fw; | |
| 1637 | + uint32_t fw_version; | |
| 1638 | + | |
| 1639 | + struct amdgpu_ring ring; | |
| 1640 | +}; | |
| 1641 | + | |
| 1642 | +/* | |
| 1643 | + * Firmware | |
| 1644 | + */ | |
| 1645 | +struct amdgpu_firmware { | |
| 1646 | + struct amdgpu_firmware_info ucode[AMDGPU_UCODE_ID_MAXIMUM]; | |
| 1647 | + bool smu_load; | |
| 1648 | + struct amdgpu_bo *fw_buf; | |
| 1649 | + unsigned int fw_size; | |
| 1650 | +}; | |
| 1651 | + | |
| 1652 | +/* | |
| 1653 | + * Benchmarking | |
| 1654 | + */ | |
| 1655 | +void amdgpu_benchmark(struct amdgpu_device *adev, int test_number); | |
| 1656 | + | |
| 1657 | + | |
| 1658 | +/* | |
| 1659 | + * Testing | |
| 1660 | + */ | |
| 1661 | +void amdgpu_test_moves(struct amdgpu_device *adev); | |
| 1662 | +void amdgpu_test_ring_sync(struct amdgpu_device *adev, | |
| 1663 | + struct amdgpu_ring *cpA, | |
| 1664 | + struct amdgpu_ring *cpB); | |
| 1665 | +void amdgpu_test_syncing(struct amdgpu_device *adev); | |
| 1666 | + | |
| 1667 | +/* | |
| 1668 | + * MMU Notifier | |
| 1669 | + */ | |
| 1670 | +#if defined(CONFIG_MMU_NOTIFIER) | |
| 1671 | +int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr); | |
| 1672 | +void amdgpu_mn_unregister(struct amdgpu_bo *bo); | |
| 1673 | +#else | |
| 1674 | +static int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) | |
| 1675 | +{ | |
| 1676 | + return -ENODEV; | |
| 1677 | +} | |
| 1678 | +static void amdgpu_mn_unregister(struct amdgpu_bo *bo) {} | |
| 1679 | +#endif | |
| 1680 | + | |
| 1681 | +/* | |
| 1682 | + * Debugfs | |
| 1683 | + */ | |
| 1684 | +struct amdgpu_debugfs { | |
| 1685 | + struct drm_info_list *files; | |
| 1686 | + unsigned num_files; | |
| 1687 | +}; | |
| 1688 | + | |
| 1689 | +int amdgpu_debugfs_add_files(struct amdgpu_device *adev, | |
| 1690 | + struct drm_info_list *files, | |
| 1691 | + unsigned nfiles); | |
| 1692 | +int amdgpu_debugfs_fence_init(struct amdgpu_device *adev); | |
| 1693 | + | |
| 1694 | +#if defined(CONFIG_DEBUG_FS) | |
| 1695 | +int amdgpu_debugfs_init(struct drm_minor *minor); | |
| 1696 | +void amdgpu_debugfs_cleanup(struct drm_minor *minor); | |
| 1697 | +#endif | |
| 1698 | + | |
| 1699 | +/* | |
| 1700 | + * amdgpu smumgr functions | |
| 1701 | + */ | |
| 1702 | +struct amdgpu_smumgr_funcs { | |
| 1703 | + int (*check_fw_load_finish)(struct amdgpu_device *adev, uint32_t fwtype); | |
| 1704 | + int (*request_smu_load_fw)(struct amdgpu_device *adev); | |
| 1705 | + int (*request_smu_specific_fw)(struct amdgpu_device *adev, uint32_t fwtype); | |
| 1706 | +}; | |
| 1707 | + | |
| 1708 | +/* | |
| 1709 | + * amdgpu smumgr | |
| 1710 | + */ | |
| 1711 | +struct amdgpu_smumgr { | |
| 1712 | + struct amdgpu_bo *toc_buf; | |
| 1713 | + struct amdgpu_bo *smu_buf; | |
| 1714 | + /* asic priv smu data */ | |
| 1715 | + void *priv; | |
| 1716 | + spinlock_t smu_lock; | |
| 1717 | + /* smumgr functions */ | |
| 1718 | + const struct amdgpu_smumgr_funcs *smumgr_funcs; | |
| 1719 | + /* ucode loading complete flag */ | |
| 1720 | + uint32_t fw_flags; | |
| 1721 | +}; | |
| 1722 | + | |
| 1723 | +/* | |
| 1724 | + * ASIC specific register table accessible by UMD | |
| 1725 | + */ | |
| 1726 | +struct amdgpu_allowed_register_entry { | |
| 1727 | + uint32_t reg_offset; | |
| 1728 | + bool untouched; | |
| 1729 | + bool grbm_indexed; | |
| 1730 | +}; | |
| 1731 | + | |
| 1732 | +struct amdgpu_cu_info { | |
| 1733 | + uint32_t number; /* total active CU number */ | |
| 1734 | + uint32_t ao_cu_mask; | |
| 1735 | + uint32_t bitmap[4][4]; | |
| 1736 | +}; | |
| 1737 | + | |
| 1738 | + | |
| 1739 | +/* | |
| 1740 | + * ASIC specific functions. | |
| 1741 | + */ | |
| 1742 | +struct amdgpu_asic_funcs { | |
| 1743 | + bool (*read_disabled_bios)(struct amdgpu_device *adev); | |
| 1744 | + int (*read_register)(struct amdgpu_device *adev, u32 se_num, | |
| 1745 | + u32 sh_num, u32 reg_offset, u32 *value); | |
| 1746 | + void (*set_vga_state)(struct amdgpu_device *adev, bool state); | |
| 1747 | + int (*reset)(struct amdgpu_device *adev); | |
| 1748 | + /* wait for mc_idle */ | |
| 1749 | + int (*wait_for_mc_idle)(struct amdgpu_device *adev); | |
| 1750 | + /* get the reference clock */ | |
| 1751 | + u32 (*get_xclk)(struct amdgpu_device *adev); | |
| 1752 | + /* get the gpu clock counter */ | |
| 1753 | + uint64_t (*get_gpu_clock_counter)(struct amdgpu_device *adev); | |
| 1754 | + int (*get_cu_info)(struct amdgpu_device *adev, struct amdgpu_cu_info *info); | |
| 1755 | + /* MM block clocks */ | |
| 1756 | + int (*set_uvd_clocks)(struct amdgpu_device *adev, u32 vclk, u32 dclk); | |
| 1757 | + int (*set_vce_clocks)(struct amdgpu_device *adev, u32 evclk, u32 ecclk); | |
| 1758 | +}; | |
| 1759 | + | |
| 1760 | +/* | |
| 1761 | + * IOCTL. | |
| 1762 | + */ | |
| 1763 | +int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, | |
| 1764 | + struct drm_file *filp); | |
| 1765 | +int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, | |
| 1766 | + struct drm_file *filp); | |
| 1767 | + | |
| 1768 | +int amdgpu_gem_info_ioctl(struct drm_device *dev, void *data, | |
| 1769 | + struct drm_file *filp); | |
| 1770 | +int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, | |
| 1771 | + struct drm_file *filp); | |
| 1772 | +int amdgpu_gem_mmap_ioctl(struct drm_device *dev, void *data, | |
| 1773 | + struct drm_file *filp); | |
| 1774 | +int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data, | |
| 1775 | + struct drm_file *filp); | |
| 1776 | +int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, | |
| 1777 | + struct drm_file *filp); | |
| 1778 | +int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, | |
| 1779 | + struct drm_file *filp); | |
| 1780 | +int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); | |
| 1781 | +int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); | |
| 1782 | + | |
| 1783 | +int amdgpu_gem_metadata_ioctl(struct drm_device *dev, void *data, | |
| 1784 | + struct drm_file *filp); | |
| 1785 | + | |
| 1786 | +/* VRAM scratch page for HDP bug, default vram page */ | |
| 1787 | +struct amdgpu_vram_scratch { | |
| 1788 | + struct amdgpu_bo *robj; | |
| 1789 | + volatile uint32_t *ptr; | |
| 1790 | + u64 gpu_addr; | |
| 1791 | +}; | |
| 1792 | + | |
| 1793 | +/* | |
| 1794 | + * ACPI | |
| 1795 | + */ | |
| 1796 | +struct amdgpu_atif_notification_cfg { | |
| 1797 | + bool enabled; | |
| 1798 | + int command_code; | |
| 1799 | +}; | |
| 1800 | + | |
| 1801 | +struct amdgpu_atif_notifications { | |
| 1802 | + bool display_switch; | |
| 1803 | + bool expansion_mode_change; | |
| 1804 | + bool thermal_state; | |
| 1805 | + bool forced_power_state; | |
| 1806 | + bool system_power_state; | |
| 1807 | + bool display_conf_change; | |
| 1808 | + bool px_gfx_switch; | |
| 1809 | + bool brightness_change; | |
| 1810 | + bool dgpu_display_event; | |
| 1811 | +}; | |
| 1812 | + | |
| 1813 | +struct amdgpu_atif_functions { | |
| 1814 | + bool system_params; | |
| 1815 | + bool sbios_requests; | |
| 1816 | + bool select_active_disp; | |
| 1817 | + bool lid_state; | |
| 1818 | + bool get_tv_standard; | |
| 1819 | + bool set_tv_standard; | |
| 1820 | + bool get_panel_expansion_mode; | |
| 1821 | + bool set_panel_expansion_mode; | |
| 1822 | + bool temperature_change; | |
| 1823 | + bool graphics_device_types; | |
| 1824 | +}; | |
| 1825 | + | |
| 1826 | +struct amdgpu_atif { | |
| 1827 | + struct amdgpu_atif_notifications notifications; | |
| 1828 | + struct amdgpu_atif_functions functions; | |
| 1829 | + struct amdgpu_atif_notification_cfg notification_cfg; | |
| 1830 | + struct amdgpu_encoder *encoder_for_bl; | |
| 1831 | +}; | |
| 1832 | + | |
| 1833 | +struct amdgpu_atcs_functions { | |
| 1834 | + bool get_ext_state; | |
| 1835 | + bool pcie_perf_req; | |
| 1836 | + bool pcie_dev_rdy; | |
| 1837 | + bool pcie_bus_width; | |
| 1838 | +}; | |
| 1839 | + | |
| 1840 | +struct amdgpu_atcs { | |
| 1841 | + struct amdgpu_atcs_functions functions; | |
| 1842 | +}; | |
| 1843 | + | |
| 1844 | +int amdgpu_ctx_alloc(struct amdgpu_device *adev,struct amdgpu_fpriv *fpriv, | |
| 1845 | + uint32_t *id,uint32_t flags); | |
| 1846 | +int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, | |
| 1847 | + uint32_t id); | |
| 1848 | + | |
| 1849 | +void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv); | |
| 1850 | +struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id); | |
| 1851 | +int amdgpu_ctx_put(struct amdgpu_ctx *ctx); | |
| 1852 | + | |
| 1853 | +extern int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, | |
| 1854 | + struct drm_file *filp); | |
| 1855 | + | |
| 1856 | +/* | |
| 1857 | + * Core structure, functions and helpers. | |
| 1858 | + */ | |
| 1859 | +typedef uint32_t (*amdgpu_rreg_t)(struct amdgpu_device*, uint32_t); | |
| 1860 | +typedef void (*amdgpu_wreg_t)(struct amdgpu_device*, uint32_t, uint32_t); | |
| 1861 | + | |
| 1862 | +typedef uint32_t (*amdgpu_block_rreg_t)(struct amdgpu_device*, uint32_t, uint32_t); | |
| 1863 | +typedef void (*amdgpu_block_wreg_t)(struct amdgpu_device*, uint32_t, uint32_t, uint32_t); | |
| 1864 | + | |
| 1865 | +struct amdgpu_device { | |
| 1866 | + struct device *dev; | |
| 1867 | + struct drm_device *ddev; | |
| 1868 | + struct pci_dev *pdev; | |
| 1869 | + struct rw_semaphore exclusive_lock; | |
| 1870 | + | |
| 1871 | + /* ASIC */ | |
| 1872 | + enum amdgpu_asic_type asic_type; | |
| 1873 | + uint32_t family; | |
| 1874 | + uint32_t rev_id; | |
| 1875 | + uint32_t external_rev_id; | |
| 1876 | + unsigned long flags; | |
| 1877 | + int usec_timeout; | |
| 1878 | + const struct amdgpu_asic_funcs *asic_funcs; | |
| 1879 | + bool shutdown; | |
| 1880 | + bool suspend; | |
| 1881 | + bool need_dma32; | |
| 1882 | + bool accel_working; | |
| 1883 | + bool needs_reset; | |
| 1884 | + struct work_struct reset_work; | |
| 1885 | + struct notifier_block acpi_nb; | |
| 1886 | + struct amdgpu_i2c_chan *i2c_bus[AMDGPU_MAX_I2C_BUS]; | |
| 1887 | + struct amdgpu_debugfs debugfs[AMDGPU_DEBUGFS_MAX_COMPONENTS]; | |
| 1888 | + unsigned debugfs_count; | |
| 1889 | +#if defined(CONFIG_DEBUG_FS) | |
| 1890 | + struct dentry *debugfs_regs; | |
| 1891 | +#endif | |
| 1892 | + struct amdgpu_atif atif; | |
| 1893 | + struct amdgpu_atcs atcs; | |
| 1894 | + struct mutex srbm_mutex; | |
| 1895 | + /* GRBM index mutex. Protects concurrent access to GRBM index */ | |
| 1896 | + struct mutex grbm_idx_mutex; | |
| 1897 | + struct dev_pm_domain vga_pm_domain; | |
| 1898 | + bool have_disp_power_ref; | |
| 1899 | + | |
| 1900 | + /* BIOS */ | |
| 1901 | + uint8_t *bios; | |
| 1902 | + bool is_atom_bios; | |
| 1903 | + uint16_t bios_header_start; | |
| 1904 | + struct amdgpu_bo *stollen_vga_memory; | |
| 1905 | + uint32_t bios_scratch[AMDGPU_BIOS_NUM_SCRATCH]; | |
| 1906 | + | |
| 1907 | + /* Register/doorbell mmio */ | |
| 1908 | + resource_size_t rmmio_base; | |
| 1909 | + resource_size_t rmmio_size; | |
| 1910 | + void __iomem *rmmio; | |
| 1911 | + /* protects concurrent MM_INDEX/DATA based register access */ | |
| 1912 | + spinlock_t mmio_idx_lock; | |
| 1913 | + /* protects concurrent SMC based register access */ | |
| 1914 | + spinlock_t smc_idx_lock; | |
| 1915 | + amdgpu_rreg_t smc_rreg; | |
| 1916 | + amdgpu_wreg_t smc_wreg; | |
| 1917 | + /* protects concurrent PCIE register access */ | |
| 1918 | + spinlock_t pcie_idx_lock; | |
| 1919 | + amdgpu_rreg_t pcie_rreg; | |
| 1920 | + amdgpu_wreg_t pcie_wreg; | |
| 1921 | + /* protects concurrent UVD register access */ | |
| 1922 | + spinlock_t uvd_ctx_idx_lock; | |
| 1923 | + amdgpu_rreg_t uvd_ctx_rreg; | |
| 1924 | + amdgpu_wreg_t uvd_ctx_wreg; | |
| 1925 | + /* protects concurrent DIDT register access */ | |
| 1926 | + spinlock_t didt_idx_lock; | |
| 1927 | + amdgpu_rreg_t didt_rreg; | |
| 1928 | + amdgpu_wreg_t didt_wreg; | |
| 1929 | + /* protects concurrent ENDPOINT (audio) register access */ | |
| 1930 | + spinlock_t audio_endpt_idx_lock; | |
| 1931 | + amdgpu_block_rreg_t audio_endpt_rreg; | |
| 1932 | + amdgpu_block_wreg_t audio_endpt_wreg; | |
| 1933 | + void __iomem *rio_mem; | |
| 1934 | + resource_size_t rio_mem_size; | |
| 1935 | + struct amdgpu_doorbell doorbell; | |
| 1936 | + | |
| 1937 | + /* clock/pll info */ | |
| 1938 | + struct amdgpu_clock clock; | |
| 1939 | + | |
| 1940 | + /* MC */ | |
| 1941 | + struct amdgpu_mc mc; | |
| 1942 | + struct amdgpu_gart gart; | |
| 1943 | + struct amdgpu_dummy_page dummy_page; | |
| 1944 | + struct amdgpu_vm_manager vm_manager; | |
| 1945 | + | |
| 1946 | + /* memory management */ | |
| 1947 | + struct amdgpu_mman mman; | |
| 1948 | + struct amdgpu_gem gem; | |
| 1949 | + struct amdgpu_vram_scratch vram_scratch; | |
| 1950 | + struct amdgpu_wb wb; | |
| 1951 | + atomic64_t vram_usage; | |
| 1952 | + atomic64_t vram_vis_usage; | |
| 1953 | + atomic64_t gtt_usage; | |
| 1954 | + atomic64_t num_bytes_moved; | |
| 1955 | + atomic_t gpu_reset_counter; | |
| 1956 | + | |
| 1957 | + /* display */ | |
| 1958 | + struct amdgpu_mode_info mode_info; | |
| 1959 | + struct work_struct hotplug_work; | |
| 1960 | + struct amdgpu_irq_src crtc_irq; | |
| 1961 | + struct amdgpu_irq_src pageflip_irq; | |
| 1962 | + struct amdgpu_irq_src hpd_irq; | |
| 1963 | + | |
| 1964 | + /* rings */ | |
| 1965 | + wait_queue_head_t fence_queue; | |
| 1966 | + unsigned fence_context; | |
| 1967 | + struct mutex ring_lock; | |
| 1968 | + unsigned num_rings; | |
| 1969 | + struct amdgpu_ring *rings[AMDGPU_MAX_RINGS]; | |
| 1970 | + bool ib_pool_ready; | |
| 1971 | + struct amdgpu_sa_manager ring_tmp_bo; | |
| 1972 | + | |
| 1973 | + /* interrupts */ | |
| 1974 | + struct amdgpu_irq irq; | |
| 1975 | + | |
| 1976 | + /* dpm */ | |
| 1977 | + struct amdgpu_pm pm; | |
| 1978 | + u32 cg_flags; | |
| 1979 | + u32 pg_flags; | |
| 1980 | + | |
| 1981 | + /* amdgpu smumgr */ | |
| 1982 | + struct amdgpu_smumgr smu; | |
| 1983 | + | |
| 1984 | + /* gfx */ | |
| 1985 | + struct amdgpu_gfx gfx; | |
| 1986 | + | |
| 1987 | + /* sdma */ | |
| 1988 | + struct amdgpu_sdma sdma[2]; | |
| 1989 | + struct amdgpu_irq_src sdma_trap_irq; | |
| 1990 | + struct amdgpu_irq_src sdma_illegal_inst_irq; | |
| 1991 | + | |
| 1992 | + /* uvd */ | |
| 1993 | + bool has_uvd; | |
| 1994 | + struct amdgpu_uvd uvd; | |
| 1995 | + | |
| 1996 | + /* vce */ | |
| 1997 | + struct amdgpu_vce vce; | |
| 1998 | + | |
| 1999 | + /* firmwares */ | |
| 2000 | + struct amdgpu_firmware firmware; | |
| 2001 | + | |
| 2002 | + /* GDS */ | |
| 2003 | + struct amdgpu_gds gds; | |
| 2004 | + | |
| 2005 | + const struct amdgpu_ip_block_version *ip_blocks; | |
| 2006 | + int num_ip_blocks; | |
| 2007 | + bool *ip_block_enabled; | |
| 2008 | + struct mutex mn_lock; | |
| 2009 | + DECLARE_HASHTABLE(mn_hash, 7); | |
| 2010 | + | |
| 2011 | + /* tracking pinned memory */ | |
| 2012 | + u64 vram_pin_size; | |
| 2013 | + u64 gart_pin_size; | |
| 2014 | +}; | |
| 2015 | + | |
| 2016 | +bool amdgpu_device_is_px(struct drm_device *dev); | |
| 2017 | +int amdgpu_device_init(struct amdgpu_device *adev, | |
| 2018 | + struct drm_device *ddev, | |
| 2019 | + struct pci_dev *pdev, | |
| 2020 | + uint32_t flags); | |
| 2021 | +void amdgpu_device_fini(struct amdgpu_device *adev); | |
| 2022 | +int amdgpu_gpu_wait_for_idle(struct amdgpu_device *adev); | |
| 2023 | + | |
| 2024 | +uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, | |
| 2025 | + bool always_indirect); | |
| 2026 | +void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, | |
| 2027 | + bool always_indirect); | |
| 2028 | +u32 amdgpu_io_rreg(struct amdgpu_device *adev, u32 reg); | |
| 2029 | +void amdgpu_io_wreg(struct amdgpu_device *adev, u32 reg, u32 v); | |
| 2030 | + | |
| 2031 | +u32 amdgpu_mm_rdoorbell(struct amdgpu_device *adev, u32 index); | |
| 2032 | +void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v); | |
| 2033 | + | |
| 2034 | +/* | |
| 2035 | + * Cast helper | |
| 2036 | + */ | |
| 2037 | +extern const struct fence_ops amdgpu_fence_ops; | |
| 2038 | +static inline struct amdgpu_fence *to_amdgpu_fence(struct fence *f) | |
| 2039 | +{ | |
| 2040 | + struct amdgpu_fence *__f = container_of(f, struct amdgpu_fence, base); | |
| 2041 | + | |
| 2042 | + if (__f->base.ops == &amdgpu_fence_ops) | |
| 2043 | + return __f; | |
| 2044 | + | |
| 2045 | + return NULL; | |
| 2046 | +} | |
| 2047 | + | |
| 2048 | +/* | |
| 2049 | + * Registers read & write functions. | |
| 2050 | + */ | |
| 2051 | +#define RREG32(reg) amdgpu_mm_rreg(adev, (reg), false) | |
| 2052 | +#define RREG32_IDX(reg) amdgpu_mm_rreg(adev, (reg), true) | |
| 2053 | +#define DREG32(reg) printk(KERN_INFO "REGISTER: " #reg " : 0x%08X\n", amdgpu_mm_rreg(adev, (reg), false)) | |
| 2054 | +#define WREG32(reg, v) amdgpu_mm_wreg(adev, (reg), (v), false) | |
| 2055 | +#define WREG32_IDX(reg, v) amdgpu_mm_wreg(adev, (reg), (v), true) | |
| 2056 | +#define REG_SET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK) | |
| 2057 | +#define REG_GET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK) | |
| 2058 | +#define RREG32_PCIE(reg) adev->pcie_rreg(adev, (reg)) | |
| 2059 | +#define WREG32_PCIE(reg, v) adev->pcie_wreg(adev, (reg), (v)) | |
| 2060 | +#define RREG32_SMC(reg) adev->smc_rreg(adev, (reg)) | |
| 2061 | +#define WREG32_SMC(reg, v) adev->smc_wreg(adev, (reg), (v)) | |
| 2062 | +#define RREG32_UVD_CTX(reg) adev->uvd_ctx_rreg(adev, (reg)) | |
| 2063 | +#define WREG32_UVD_CTX(reg, v) adev->uvd_ctx_wreg(adev, (reg), (v)) | |
| 2064 | +#define RREG32_DIDT(reg) adev->didt_rreg(adev, (reg)) | |
| 2065 | +#define WREG32_DIDT(reg, v) adev->didt_wreg(adev, (reg), (v)) | |
| 2066 | +#define RREG32_AUDIO_ENDPT(block, reg) adev->audio_endpt_rreg(adev, (block), (reg)) | |
| 2067 | +#define WREG32_AUDIO_ENDPT(block, reg, v) adev->audio_endpt_wreg(adev, (block), (reg), (v)) | |
| 2068 | +#define WREG32_P(reg, val, mask) \ | |
| 2069 | + do { \ | |
| 2070 | + uint32_t tmp_ = RREG32(reg); \ | |
| 2071 | + tmp_ &= (mask); \ | |
| 2072 | + tmp_ |= ((val) & ~(mask)); \ | |
| 2073 | + WREG32(reg, tmp_); \ | |
| 2074 | + } while (0) | |
| 2075 | +#define WREG32_AND(reg, and) WREG32_P(reg, 0, and) | |
| 2076 | +#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or)) | |
| 2077 | +#define WREG32_PLL_P(reg, val, mask) \ | |
| 2078 | + do { \ | |
| 2079 | + uint32_t tmp_ = RREG32_PLL(reg); \ | |
| 2080 | + tmp_ &= (mask); \ | |
| 2081 | + tmp_ |= ((val) & ~(mask)); \ | |
| 2082 | + WREG32_PLL(reg, tmp_); \ | |
| 2083 | + } while (0) | |
| 2084 | +#define DREG32_SYS(sqf, adev, reg) seq_printf((sqf), #reg " : 0x%08X\n", amdgpu_mm_rreg((adev), (reg), false)) | |
| 2085 | +#define RREG32_IO(reg) amdgpu_io_rreg(adev, (reg)) | |
| 2086 | +#define WREG32_IO(reg, v) amdgpu_io_wreg(adev, (reg), (v)) | |
| 2087 | + | |
| 2088 | +#define RDOORBELL32(index) amdgpu_mm_rdoorbell(adev, (index)) | |
| 2089 | +#define WDOORBELL32(index, v) amdgpu_mm_wdoorbell(adev, (index), (v)) | |
| 2090 | + | |
| 2091 | +#define REG_FIELD_SHIFT(reg, field) reg##__##field##__SHIFT | |
| 2092 | +#define REG_FIELD_MASK(reg, field) reg##__##field##_MASK | |
| 2093 | + | |
| 2094 | +#define REG_SET_FIELD(orig_val, reg, field, field_val) \ | |
| 2095 | + (((orig_val) & ~REG_FIELD_MASK(reg, field)) | \ | |
| 2096 | + (REG_FIELD_MASK(reg, field) & ((field_val) << REG_FIELD_SHIFT(reg, field)))) | |
| 2097 | + | |
| 2098 | +#define REG_GET_FIELD(value, reg, field) \ | |
| 2099 | + (((value) & REG_FIELD_MASK(reg, field)) >> REG_FIELD_SHIFT(reg, field)) | |
| 2100 | + | |
| 2101 | +/* | |
| 2102 | + * BIOS helpers. | |
| 2103 | + */ | |
| 2104 | +#define RBIOS8(i) (adev->bios[i]) | |
| 2105 | +#define RBIOS16(i) (RBIOS8(i) | (RBIOS8((i)+1) << 8)) | |
| 2106 | +#define RBIOS32(i) ((RBIOS16(i)) | (RBIOS16((i)+2) << 16)) | |
| 2107 | + | |
| 2108 | +/* | |
| 2109 | + * RING helpers. | |
| 2110 | + */ | |
| 2111 | +static inline void amdgpu_ring_write(struct amdgpu_ring *ring, uint32_t v) | |
| 2112 | +{ | |
| 2113 | + if (ring->count_dw <= 0) | |
| 2114 | + DRM_ERROR("amdgpu: writing more dwords to the ring than expected!\n"); | |
| 2115 | + ring->ring[ring->wptr++] = v; | |
| 2116 | + ring->wptr &= ring->ptr_mask; | |
| 2117 | + ring->count_dw--; | |
| 2118 | + ring->ring_free_dw--; | |
| 2119 | +} | |
| 2120 | + | |
| 2121 | +/* | |
| 2122 | + * ASICs macro. | |
| 2123 | + */ | |
| 2124 | +#define amdgpu_asic_set_vga_state(adev, state) (adev)->asic_funcs->set_vga_state((adev), (state)) | |
| 2125 | +#define amdgpu_asic_reset(adev) (adev)->asic_funcs->reset((adev)) | |
| 2126 | +#define amdgpu_asic_wait_for_mc_idle(adev) (adev)->asic_funcs->wait_for_mc_idle((adev)) | |
| 2127 | +#define amdgpu_asic_get_xclk(adev) (adev)->asic_funcs->get_xclk((adev)) | |
| 2128 | +#define amdgpu_asic_set_uvd_clocks(adev, v, d) (adev)->asic_funcs->set_uvd_clocks((adev), (v), (d)) | |
| 2129 | +#define amdgpu_asic_set_vce_clocks(adev, ev, ec) (adev)->asic_funcs->set_vce_clocks((adev), (ev), (ec)) | |
| 2130 | +#define amdgpu_asic_get_gpu_clock_counter(adev) (adev)->asic_funcs->get_gpu_clock_counter((adev)) | |
| 2131 | +#define amdgpu_asic_read_disabled_bios(adev) (adev)->asic_funcs->read_disabled_bios((adev)) | |
| 2132 | +#define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v))) | |
| 2133 | +#define amdgpu_asic_get_cu_info(adev, info) (adev)->asic_funcs->get_cu_info((adev), (info)) | |
| 2134 | +#define amdgpu_gart_flush_gpu_tlb(adev, vmid) (adev)->gart.gart_funcs->flush_gpu_tlb((adev), (vmid)) | |
| 2135 | +#define amdgpu_gart_set_pte_pde(adev, pt, idx, addr, flags) (adev)->gart.gart_funcs->set_pte_pde((adev), (pt), (idx), (addr), (flags)) | |
| 2136 | +#define amdgpu_vm_copy_pte(adev, ib, pe, src, count) ((adev)->vm_manager.vm_pte_funcs->copy_pte((ib), (pe), (src), (count))) | |
| 2137 | +#define amdgpu_vm_write_pte(adev, ib, pe, addr, count, incr, flags) ((adev)->vm_manager.vm_pte_funcs->write_pte((ib), (pe), (addr), (count), (incr), (flags))) | |
| 2138 | +#define amdgpu_vm_set_pte_pde(adev, ib, pe, addr, count, incr, flags) ((adev)->vm_manager.vm_pte_funcs->set_pte_pde((ib), (pe), (addr), (count), (incr), (flags))) | |
| 2139 | +#define amdgpu_vm_pad_ib(adev, ib) ((adev)->vm_manager.vm_pte_funcs->pad_ib((ib))) | |
| 2140 | +#define amdgpu_ring_parse_cs(r, p, ib) ((r)->funcs->parse_cs((p), (ib))) | |
| 2141 | +#define amdgpu_ring_test_ring(r) (r)->funcs->test_ring((r)) | |
| 2142 | +#define amdgpu_ring_test_ib(r) (r)->funcs->test_ib((r)) | |
| 2143 | +#define amdgpu_ring_is_lockup(r) (r)->funcs->is_lockup((r)) | |
| 2144 | +#define amdgpu_ring_get_rptr(r) (r)->funcs->get_rptr((r)) | |
| 2145 | +#define amdgpu_ring_get_wptr(r) (r)->funcs->get_wptr((r)) | |
| 2146 | +#define amdgpu_ring_set_wptr(r) (r)->funcs->set_wptr((r)) | |
| 2147 | +#define amdgpu_ring_emit_ib(r, ib) (r)->funcs->emit_ib((r), (ib)) | |
| 2148 | +#define amdgpu_ring_emit_vm_flush(r, vmid, addr) (r)->funcs->emit_vm_flush((r), (vmid), (addr)) | |
| 2149 | +#define amdgpu_ring_emit_fence(r, addr, seq, flags) (r)->funcs->emit_fence((r), (addr), (seq), (flags)) | |
| 2150 | +#define amdgpu_ring_emit_semaphore(r, semaphore, emit_wait) (r)->funcs->emit_semaphore((r), (semaphore), (emit_wait)) | |
| 2151 | +#define amdgpu_ring_emit_gds_switch(r, v, db, ds, wb, ws, ab, as) (r)->funcs->emit_gds_switch((r), (v), (db), (ds), (wb), (ws), (ab), (as)) | |
| 2152 | +#define amdgpu_ring_emit_hdp_flush(r) (r)->funcs->emit_hdp_flush((r)) | |
| 2153 | +#define amdgpu_ih_get_wptr(adev) (adev)->irq.ih_funcs->get_wptr((adev)) | |
| 2154 | +#define amdgpu_ih_decode_iv(adev, iv) (adev)->irq.ih_funcs->decode_iv((adev), (iv)) | |
| 2155 | +#define amdgpu_ih_set_rptr(adev) (adev)->irq.ih_funcs->set_rptr((adev)) | |
| 2156 | +#define amdgpu_display_set_vga_render_state(adev, r) (adev)->mode_info.funcs->set_vga_render_state((adev), (r)) | |
| 2157 | +#define amdgpu_display_vblank_get_counter(adev, crtc) (adev)->mode_info.funcs->vblank_get_counter((adev), (crtc)) | |
| 2158 | +#define amdgpu_display_vblank_wait(adev, crtc) (adev)->mode_info.funcs->vblank_wait((adev), (crtc)) | |
| 2159 | +#define amdgpu_display_is_display_hung(adev) (adev)->mode_info.funcs->is_display_hung((adev)) | |
| 2160 | +#define amdgpu_display_backlight_set_level(adev, e, l) (adev)->mode_info.funcs->backlight_set_level((e), (l)) | |
| 2161 | +#define amdgpu_display_backlight_get_level(adev, e) (adev)->mode_info.funcs->backlight_get_level((e)) | |
| 2162 | +#define amdgpu_display_hpd_sense(adev, h) (adev)->mode_info.funcs->hpd_sense((adev), (h)) | |
| 2163 | +#define amdgpu_display_hpd_set_polarity(adev, h) (adev)->mode_info.funcs->hpd_set_polarity((adev), (h)) | |
| 2164 | +#define amdgpu_display_hpd_get_gpio_reg(adev) (adev)->mode_info.funcs->hpd_get_gpio_reg((adev)) | |
| 2165 | +#define amdgpu_display_bandwidth_update(adev) (adev)->mode_info.funcs->bandwidth_update((adev)) | |
| 2166 | +#define amdgpu_display_page_flip(adev, crtc, base) (adev)->mode_info.funcs->page_flip((adev), (crtc), (base)) | |
| 2167 | +#define amdgpu_display_page_flip_get_scanoutpos(adev, crtc, vbl, pos) (adev)->mode_info.funcs->page_flip_get_scanoutpos((adev), (crtc), (vbl), (pos)) | |
| 2168 | +#define amdgpu_display_add_encoder(adev, e, s, c) (adev)->mode_info.funcs->add_encoder((adev), (e), (s), (c)) | |
| 2169 | +#define amdgpu_display_add_connector(adev, ci, sd, ct, ib, coi, h, r) (adev)->mode_info.funcs->add_connector((adev), (ci), (sd), (ct), (ib), (coi), (h), (r)) | |
| 2170 | +#define amdgpu_display_stop_mc_access(adev, s) (adev)->mode_info.funcs->stop_mc_access((adev), (s)) | |
| 2171 | +#define amdgpu_display_resume_mc_access(adev, s) (adev)->mode_info.funcs->resume_mc_access((adev), (s)) | |
| 2172 | +#define amdgpu_emit_copy_buffer(adev, r, s, d, b) (adev)->mman.buffer_funcs->emit_copy_buffer((r), (s), (d), (b)) | |
| 2173 | +#define amdgpu_emit_fill_buffer(adev, r, s, d, b) (adev)->mman.buffer_funcs->emit_fill_buffer((r), (s), (d), (b)) | |
| 2174 | +#define amdgpu_dpm_get_temperature(adev) (adev)->pm.funcs->get_temperature((adev)) | |
| 2175 | +#define amdgpu_dpm_pre_set_power_state(adev) (adev)->pm.funcs->pre_set_power_state((adev)) | |
| 2176 | +#define amdgpu_dpm_set_power_state(adev) (adev)->pm.funcs->set_power_state((adev)) | |
| 2177 | +#define amdgpu_dpm_post_set_power_state(adev) (adev)->pm.funcs->post_set_power_state((adev)) | |
| 2178 | +#define amdgpu_dpm_display_configuration_changed(adev) (adev)->pm.funcs->display_configuration_changed((adev)) | |
| 2179 | +#define amdgpu_dpm_get_sclk(adev, l) (adev)->pm.funcs->get_sclk((adev), (l)) | |
| 2180 | +#define amdgpu_dpm_get_mclk(adev, l) (adev)->pm.funcs->get_mclk((adev), (l)) | |
| 2181 | +#define amdgpu_dpm_print_power_state(adev, ps) (adev)->pm.funcs->print_power_state((adev), (ps)) | |
| 2182 | +#define amdgpu_dpm_debugfs_print_current_performance_level(adev, m) (adev)->pm.funcs->debugfs_print_current_performance_level((adev), (m)) | |
| 2183 | +#define amdgpu_dpm_force_performance_level(adev, l) (adev)->pm.funcs->force_performance_level((adev), (l)) | |
| 2184 | +#define amdgpu_dpm_vblank_too_short(adev) (adev)->pm.funcs->vblank_too_short((adev)) | |
| 2185 | +#define amdgpu_dpm_powergate_uvd(adev, g) (adev)->pm.funcs->powergate_uvd((adev), (g)) | |
| 2186 | +#define amdgpu_dpm_powergate_vce(adev, g) (adev)->pm.funcs->powergate_vce((adev), (g)) | |
| 2187 | +#define amdgpu_dpm_enable_bapm(adev, e) (adev)->pm.funcs->enable_bapm((adev), (e)) | |
| 2188 | +#define amdgpu_dpm_set_fan_control_mode(adev, m) (adev)->pm.funcs->set_fan_control_mode((adev), (m)) | |
| 2189 | +#define amdgpu_dpm_get_fan_control_mode(adev) (adev)->pm.funcs->get_fan_control_mode((adev)) | |
| 2190 | +#define amdgpu_dpm_set_fan_speed_percent(adev, s) (adev)->pm.funcs->set_fan_speed_percent((adev), (s)) | |
| 2191 | +#define amdgpu_dpm_get_fan_speed_percent(adev, s) (adev)->pm.funcs->get_fan_speed_percent((adev), (s)) | |
| 2192 | + | |
| 2193 | +#define amdgpu_gds_switch(adev, r, v, d, w, a) (adev)->gds.funcs->patch_gds_switch((r), (v), (d), (w), (a)) | |
| 2194 | + | |
| 2195 | +/* Common functions */ | |
| 2196 | +int amdgpu_gpu_reset(struct amdgpu_device *adev); | |
| 2197 | +void amdgpu_pci_config_reset(struct amdgpu_device *adev); | |
| 2198 | +bool amdgpu_card_posted(struct amdgpu_device *adev); | |
| 2199 | +void amdgpu_update_display_priority(struct amdgpu_device *adev); | |
| 2200 | +bool amdgpu_boot_test_post_card(struct amdgpu_device *adev); | |
| 2201 | +int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data); | |
| 2202 | +int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type, | |
| 2203 | + u32 ip_instance, u32 ring, | |
| 2204 | + struct amdgpu_ring **out_ring); | |
| 2205 | +void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *rbo, u32 domain); | |
| 2206 | +bool amdgpu_ttm_bo_is_amdgpu_bo(struct ttm_buffer_object *bo); | |
| 2207 | +int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr, | |
| 2208 | + uint32_t flags); | |
| 2209 | +bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm); | |
| 2210 | +bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm); | |
| 2211 | +uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm, | |
| 2212 | + struct ttm_mem_reg *mem); | |
| 2213 | +void amdgpu_vram_location(struct amdgpu_device *adev, struct amdgpu_mc *mc, u64 base); | |
| 2214 | +void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc); | |
| 2215 | +void amdgpu_ttm_set_active_vram_size(struct amdgpu_device *adev, u64 size); | |
| 2216 | +void amdgpu_program_register_sequence(struct amdgpu_device *adev, | |
| 2217 | + const u32 *registers, | |
| 2218 | + const u32 array_size); | |
| 2219 | + | |
| 2220 | +bool amdgpu_device_is_px(struct drm_device *dev); | |
| 2221 | +/* atpx handler */ | |
| 2222 | +#if defined(CONFIG_VGA_SWITCHEROO) | |
| 2223 | +void amdgpu_register_atpx_handler(void); | |
| 2224 | +void amdgpu_unregister_atpx_handler(void); | |
| 2225 | +#else | |
| 2226 | +static inline void amdgpu_register_atpx_handler(void) {} | |
| 2227 | +static inline void amdgpu_unregister_atpx_handler(void) {} | |
| 2228 | +#endif | |
| 2229 | + | |
| 2230 | +/* | |
| 2231 | + * KMS | |
| 2232 | + */ | |
| 2233 | +extern const struct drm_ioctl_desc amdgpu_ioctls_kms[]; | |
| 2234 | +extern int amdgpu_max_kms_ioctl; | |
| 2235 | + | |
| 2236 | +int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags); | |
| 2237 | +int amdgpu_driver_unload_kms(struct drm_device *dev); | |
| 2238 | +void amdgpu_driver_lastclose_kms(struct drm_device *dev); | |
| 2239 | +int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv); | |
| 2240 | +void amdgpu_driver_postclose_kms(struct drm_device *dev, | |
| 2241 | + struct drm_file *file_priv); | |
| 2242 | +void amdgpu_driver_preclose_kms(struct drm_device *dev, | |
| 2243 | + struct drm_file *file_priv); | |
| 2244 | +int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon); | |
| 2245 | +int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon); | |
| 2246 | +u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, int crtc); | |
| 2247 | +int amdgpu_enable_vblank_kms(struct drm_device *dev, int crtc); | |
| 2248 | +void amdgpu_disable_vblank_kms(struct drm_device *dev, int crtc); | |
| 2249 | +int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, | |
| 2250 | + int *max_error, | |
| 2251 | + struct timeval *vblank_time, | |
| 2252 | + unsigned flags); | |
| 2253 | +long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd, | |
| 2254 | + unsigned long arg); | |
| 2255 | + | |
| 2256 | +/* | |
| 2257 | + * vm | |
| 2258 | + */ | |
| 2259 | +int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm); | |
| 2260 | +void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm); | |
| 2261 | +struct amdgpu_bo_list_entry *amdgpu_vm_get_bos(struct amdgpu_device *adev, | |
| 2262 | + struct amdgpu_vm *vm, | |
| 2263 | + struct list_head *head); | |
| 2264 | +struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring, | |
| 2265 | + struct amdgpu_vm *vm); | |
| 2266 | +void amdgpu_vm_flush(struct amdgpu_ring *ring, | |
| 2267 | + struct amdgpu_vm *vm, | |
| 2268 | + struct amdgpu_fence *updates); | |
| 2269 | +void amdgpu_vm_fence(struct amdgpu_device *adev, | |
| 2270 | + struct amdgpu_vm *vm, | |
| 2271 | + struct amdgpu_fence *fence); | |
| 2272 | +uint64_t amdgpu_vm_map_gart(struct amdgpu_device *adev, uint64_t addr); | |
| 2273 | +int amdgpu_vm_update_page_directory(struct amdgpu_device *adev, | |
| 2274 | + struct amdgpu_vm *vm); | |
| 2275 | +int amdgpu_vm_clear_freed(struct amdgpu_device *adev, | |
| 2276 | + struct amdgpu_vm *vm); | |
| 2277 | +int amdgpu_vm_clear_invalids(struct amdgpu_device *adev, | |
| 2278 | + struct amdgpu_vm *vm, struct amdgpu_sync *sync); | |
| 2279 | +int amdgpu_vm_bo_update(struct amdgpu_device *adev, | |
| 2280 | + struct amdgpu_bo_va *bo_va, | |
| 2281 | + struct ttm_mem_reg *mem); | |
| 2282 | +void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev, | |
| 2283 | + struct amdgpu_bo *bo); | |
| 2284 | +struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm, | |
| 2285 | + struct amdgpu_bo *bo); | |
| 2286 | +struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev, | |
| 2287 | + struct amdgpu_vm *vm, | |
| 2288 | + struct amdgpu_bo *bo); | |
| 2289 | +int amdgpu_vm_bo_map(struct amdgpu_device *adev, | |
| 2290 | + struct amdgpu_bo_va *bo_va, | |
| 2291 | + uint64_t addr, uint64_t offset, | |
| 2292 | + uint64_t size, uint32_t flags); | |
| 2293 | +int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, | |
| 2294 | + struct amdgpu_bo_va *bo_va, | |
| 2295 | + uint64_t addr); | |
| 2296 | +void amdgpu_vm_bo_rmv(struct amdgpu_device *adev, | |
| 2297 | + struct amdgpu_bo_va *bo_va); | |
| 2298 | + | |
| 2299 | +/* | |
| 2300 | + * functions used by amdgpu_encoder.c | |
| 2301 | + */ | |
| 2302 | +struct amdgpu_afmt_acr { | |
| 2303 | + u32 clock; | |
| 2304 | + | |
| 2305 | + int n_32khz; | |
| 2306 | + int cts_32khz; | |
| 2307 | + | |
| 2308 | + int n_44_1khz; | |
| 2309 | + int cts_44_1khz; | |
| 2310 | + | |
| 2311 | + int n_48khz; | |
| 2312 | + int cts_48khz; | |
| 2313 | + | |
| 2314 | +}; | |
| 2315 | + | |
| 2316 | +struct amdgpu_afmt_acr amdgpu_afmt_acr(uint32_t clock); | |
| 2317 | + | |
| 2318 | +/* amdgpu_acpi.c */ | |
| 2319 | +#if defined(CONFIG_ACPI) | |
| 2320 | +int amdgpu_acpi_init(struct amdgpu_device *adev); | |
| 2321 | +void amdgpu_acpi_fini(struct amdgpu_device *adev); | |
| 2322 | +bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev); | |
| 2323 | +int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev, | |
| 2324 | + u8 perf_req, bool advertise); | |
| 2325 | +int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev); | |
| 2326 | +#else | |
| 2327 | +static inline int amdgpu_acpi_init(struct amdgpu_device *adev) { return 0; } | |
| 2328 | +static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { } | |
| 2329 | +#endif | |
| 2330 | + | |
| 2331 | +struct amdgpu_bo_va_mapping * | |
| 2332 | +amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser, | |
| 2333 | + uint64_t addr, struct amdgpu_bo **bo); | |
| 2334 | + | |
| 2335 | +#include "amdgpu_object.h" | |
| 2336 | + | |
| 2337 | +#endif | |
| @@ -0,0 +1,768 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2012 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#include <linux/pci.h> | |
| 25 | +#include <linux/acpi.h> | |
| 26 | +#include <linux/slab.h> | |
| 27 | +#include <linux/power_supply.h> | |
| 28 | +#include <linux/vga_switcheroo.h> | |
| 29 | +#include <acpi/video.h> | |
| 30 | +#include <drm/drmP.h> | |
| 31 | +#include <drm/drm_crtc_helper.h> | |
| 32 | +#include "amdgpu.h" | |
| 33 | +#include "amdgpu_acpi.h" | |
| 34 | +#include "atom.h" | |
| 35 | + | |
| 36 | +#define ACPI_AC_CLASS "ac_adapter" | |
| 37 | + | |
| 38 | +extern void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev); | |
| 39 | + | |
| 40 | +struct atif_verify_interface { | |
| 41 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 42 | + u16 version; /* version */ | |
| 43 | + u32 notification_mask; /* supported notifications mask */ | |
| 44 | + u32 function_bits; /* supported functions bit vector */ | |
| 45 | +} __packed; | |
| 46 | + | |
| 47 | +struct atif_system_params { | |
| 48 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 49 | + u32 valid_mask; /* valid flags mask */ | |
| 50 | + u32 flags; /* flags */ | |
| 51 | + u8 command_code; /* notify command code */ | |
| 52 | +} __packed; | |
| 53 | + | |
| 54 | +struct atif_sbios_requests { | |
| 55 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 56 | + u32 pending; /* pending sbios requests */ | |
| 57 | + u8 panel_exp_mode; /* panel expansion mode */ | |
| 58 | + u8 thermal_gfx; /* thermal state: target gfx controller */ | |
| 59 | + u8 thermal_state; /* thermal state: state id (0: exit state, non-0: state) */ | |
| 60 | + u8 forced_power_gfx; /* forced power state: target gfx controller */ | |
| 61 | + u8 forced_power_state; /* forced power state: state id */ | |
| 62 | + u8 system_power_src; /* system power source */ | |
| 63 | + u8 backlight_level; /* panel backlight level (0-255) */ | |
| 64 | +} __packed; | |
| 65 | + | |
| 66 | +#define ATIF_NOTIFY_MASK 0x3 | |
| 67 | +#define ATIF_NOTIFY_NONE 0 | |
| 68 | +#define ATIF_NOTIFY_81 1 | |
| 69 | +#define ATIF_NOTIFY_N 2 | |
| 70 | + | |
| 71 | +struct atcs_verify_interface { | |
| 72 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 73 | + u16 version; /* version */ | |
| 74 | + u32 function_bits; /* supported functions bit vector */ | |
| 75 | +} __packed; | |
| 76 | + | |
| 77 | +#define ATCS_VALID_FLAGS_MASK 0x3 | |
| 78 | + | |
| 79 | +struct atcs_pref_req_input { | |
| 80 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 81 | + u16 client_id; /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */ | |
| 82 | + u16 valid_flags_mask; /* valid flags mask */ | |
| 83 | + u16 flags; /* flags */ | |
| 84 | + u8 req_type; /* request type */ | |
| 85 | + u8 perf_req; /* performance request */ | |
| 86 | +} __packed; | |
| 87 | + | |
| 88 | +struct atcs_pref_req_output { | |
| 89 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 90 | + u8 ret_val; /* return value */ | |
| 91 | +} __packed; | |
| 92 | + | |
| 93 | +/* Call the ATIF method | |
| 94 | + */ | |
| 95 | +/** | |
| 96 | + * amdgpu_atif_call - call an ATIF method | |
| 97 | + * | |
| 98 | + * @handle: acpi handle | |
| 99 | + * @function: the ATIF function to execute | |
| 100 | + * @params: ATIF function params | |
| 101 | + * | |
| 102 | + * Executes the requested ATIF function (all asics). | |
| 103 | + * Returns a pointer to the acpi output buffer. | |
| 104 | + */ | |
| 105 | +static union acpi_object *amdgpu_atif_call(acpi_handle handle, int function, | |
| 106 | + struct acpi_buffer *params) | |
| 107 | +{ | |
| 108 | + acpi_status status; | |
| 109 | + union acpi_object atif_arg_elements[2]; | |
| 110 | + struct acpi_object_list atif_arg; | |
| 111 | + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | |
| 112 | + | |
| 113 | + atif_arg.count = 2; | |
| 114 | + atif_arg.pointer = &atif_arg_elements[0]; | |
| 115 | + | |
| 116 | + atif_arg_elements[0].type = ACPI_TYPE_INTEGER; | |
| 117 | + atif_arg_elements[0].integer.value = function; | |
| 118 | + | |
| 119 | + if (params) { | |
| 120 | + atif_arg_elements[1].type = ACPI_TYPE_BUFFER; | |
| 121 | + atif_arg_elements[1].buffer.length = params->length; | |
| 122 | + atif_arg_elements[1].buffer.pointer = params->pointer; | |
| 123 | + } else { | |
| 124 | + /* We need a second fake parameter */ | |
| 125 | + atif_arg_elements[1].type = ACPI_TYPE_INTEGER; | |
| 126 | + atif_arg_elements[1].integer.value = 0; | |
| 127 | + } | |
| 128 | + | |
| 129 | + status = acpi_evaluate_object(handle, "ATIF", &atif_arg, &buffer); | |
| 130 | + | |
| 131 | + /* Fail only if calling the method fails and ATIF is supported */ | |
| 132 | + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { | |
| 133 | + DRM_DEBUG_DRIVER("failed to evaluate ATIF got %s\n", | |
| 134 | + acpi_format_exception(status)); | |
| 135 | + kfree(buffer.pointer); | |
| 136 | + return NULL; | |
| 137 | + } | |
| 138 | + | |
| 139 | + return buffer.pointer; | |
| 140 | +} | |
| 141 | + | |
| 142 | +/** | |
| 143 | + * amdgpu_atif_parse_notification - parse supported notifications | |
| 144 | + * | |
| 145 | + * @n: supported notifications struct | |
| 146 | + * @mask: supported notifications mask from ATIF | |
| 147 | + * | |
| 148 | + * Use the supported notifications mask from ATIF function | |
| 149 | + * ATIF_FUNCTION_VERIFY_INTERFACE to determine what notifications | |
| 150 | + * are supported (all asics). | |
| 151 | + */ | |
| 152 | +static void amdgpu_atif_parse_notification(struct amdgpu_atif_notifications *n, u32 mask) | |
| 153 | +{ | |
| 154 | + n->display_switch = mask & ATIF_DISPLAY_SWITCH_REQUEST_SUPPORTED; | |
| 155 | + n->expansion_mode_change = mask & ATIF_EXPANSION_MODE_CHANGE_REQUEST_SUPPORTED; | |
| 156 | + n->thermal_state = mask & ATIF_THERMAL_STATE_CHANGE_REQUEST_SUPPORTED; | |
| 157 | + n->forced_power_state = mask & ATIF_FORCED_POWER_STATE_CHANGE_REQUEST_SUPPORTED; | |
| 158 | + n->system_power_state = mask & ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST_SUPPORTED; | |
| 159 | + n->display_conf_change = mask & ATIF_DISPLAY_CONF_CHANGE_REQUEST_SUPPORTED; | |
| 160 | + n->px_gfx_switch = mask & ATIF_PX_GFX_SWITCH_REQUEST_SUPPORTED; | |
| 161 | + n->brightness_change = mask & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED; | |
| 162 | + n->dgpu_display_event = mask & ATIF_DGPU_DISPLAY_EVENT_SUPPORTED; | |
| 163 | +} | |
| 164 | + | |
| 165 | +/** | |
| 166 | + * amdgpu_atif_parse_functions - parse supported functions | |
| 167 | + * | |
| 168 | + * @f: supported functions struct | |
| 169 | + * @mask: supported functions mask from ATIF | |
| 170 | + * | |
| 171 | + * Use the supported functions mask from ATIF function | |
| 172 | + * ATIF_FUNCTION_VERIFY_INTERFACE to determine what functions | |
| 173 | + * are supported (all asics). | |
| 174 | + */ | |
| 175 | +static void amdgpu_atif_parse_functions(struct amdgpu_atif_functions *f, u32 mask) | |
| 176 | +{ | |
| 177 | + f->system_params = mask & ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED; | |
| 178 | + f->sbios_requests = mask & ATIF_GET_SYSTEM_BIOS_REQUESTS_SUPPORTED; | |
| 179 | + f->select_active_disp = mask & ATIF_SELECT_ACTIVE_DISPLAYS_SUPPORTED; | |
| 180 | + f->lid_state = mask & ATIF_GET_LID_STATE_SUPPORTED; | |
| 181 | + f->get_tv_standard = mask & ATIF_GET_TV_STANDARD_FROM_CMOS_SUPPORTED; | |
| 182 | + f->set_tv_standard = mask & ATIF_SET_TV_STANDARD_IN_CMOS_SUPPORTED; | |
| 183 | + f->get_panel_expansion_mode = mask & ATIF_GET_PANEL_EXPANSION_MODE_FROM_CMOS_SUPPORTED; | |
| 184 | + f->set_panel_expansion_mode = mask & ATIF_SET_PANEL_EXPANSION_MODE_IN_CMOS_SUPPORTED; | |
| 185 | + f->temperature_change = mask & ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED; | |
| 186 | + f->graphics_device_types = mask & ATIF_GET_GRAPHICS_DEVICE_TYPES_SUPPORTED; | |
| 187 | +} | |
| 188 | + | |
| 189 | +/** | |
| 190 | + * amdgpu_atif_verify_interface - verify ATIF | |
| 191 | + * | |
| 192 | + * @handle: acpi handle | |
| 193 | + * @atif: amdgpu atif struct | |
| 194 | + * | |
| 195 | + * Execute the ATIF_FUNCTION_VERIFY_INTERFACE ATIF function | |
| 196 | + * to initialize ATIF and determine what features are supported | |
| 197 | + * (all asics). | |
| 198 | + * returns 0 on success, error on failure. | |
| 199 | + */ | |
| 200 | +static int amdgpu_atif_verify_interface(acpi_handle handle, | |
| 201 | + struct amdgpu_atif *atif) | |
| 202 | +{ | |
| 203 | + union acpi_object *info; | |
| 204 | + struct atif_verify_interface output; | |
| 205 | + size_t size; | |
| 206 | + int err = 0; | |
| 207 | + | |
| 208 | + info = amdgpu_atif_call(handle, ATIF_FUNCTION_VERIFY_INTERFACE, NULL); | |
| 209 | + if (!info) | |
| 210 | + return -EIO; | |
| 211 | + | |
| 212 | + memset(&output, 0, sizeof(output)); | |
| 213 | + | |
| 214 | + size = *(u16 *) info->buffer.pointer; | |
| 215 | + if (size < 12) { | |
| 216 | + DRM_INFO("ATIF buffer is too small: %zu\n", size); | |
| 217 | + err = -EINVAL; | |
| 218 | + goto out; | |
| 219 | + } | |
| 220 | + size = min(sizeof(output), size); | |
| 221 | + | |
| 222 | + memcpy(&output, info->buffer.pointer, size); | |
| 223 | + | |
| 224 | + /* TODO: check version? */ | |
| 225 | + DRM_DEBUG_DRIVER("ATIF version %u\n", output.version); | |
| 226 | + | |
| 227 | + amdgpu_atif_parse_notification(&atif->notifications, output.notification_mask); | |
| 228 | + amdgpu_atif_parse_functions(&atif->functions, output.function_bits); | |
| 229 | + | |
| 230 | +out: | |
| 231 | + kfree(info); | |
| 232 | + return err; | |
| 233 | +} | |
| 234 | + | |
| 235 | +/** | |
| 236 | + * amdgpu_atif_get_notification_params - determine notify configuration | |
| 237 | + * | |
| 238 | + * @handle: acpi handle | |
| 239 | + * @n: atif notification configuration struct | |
| 240 | + * | |
| 241 | + * Execute the ATIF_FUNCTION_GET_SYSTEM_PARAMETERS ATIF function | |
| 242 | + * to determine if a notifier is used and if so which one | |
| 243 | + * (all asics). This is either Notify(VGA, 0x81) or Notify(VGA, n) | |
| 244 | + * where n is specified in the result if a notifier is used. | |
| 245 | + * Returns 0 on success, error on failure. | |
| 246 | + */ | |
| 247 | +static int amdgpu_atif_get_notification_params(acpi_handle handle, | |
| 248 | + struct amdgpu_atif_notification_cfg *n) | |
| 249 | +{ | |
| 250 | + union acpi_object *info; | |
| 251 | + struct atif_system_params params; | |
| 252 | + size_t size; | |
| 253 | + int err = 0; | |
| 254 | + | |
| 255 | + info = amdgpu_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS, NULL); | |
| 256 | + if (!info) { | |
| 257 | + err = -EIO; | |
| 258 | + goto out; | |
| 259 | + } | |
| 260 | + | |
| 261 | + size = *(u16 *) info->buffer.pointer; | |
| 262 | + if (size < 10) { | |
| 263 | + err = -EINVAL; | |
| 264 | + goto out; | |
| 265 | + } | |
| 266 | + | |
| 267 | + memset(¶ms, 0, sizeof(params)); | |
| 268 | + size = min(sizeof(params), size); | |
| 269 | + memcpy(¶ms, info->buffer.pointer, size); | |
| 270 | + | |
| 271 | + DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n", | |
| 272 | + params.flags, params.valid_mask); | |
| 273 | + params.flags = params.flags & params.valid_mask; | |
| 274 | + | |
| 275 | + if ((params.flags & ATIF_NOTIFY_MASK) == ATIF_NOTIFY_NONE) { | |
| 276 | + n->enabled = false; | |
| 277 | + n->command_code = 0; | |
| 278 | + } else if ((params.flags & ATIF_NOTIFY_MASK) == ATIF_NOTIFY_81) { | |
| 279 | + n->enabled = true; | |
| 280 | + n->command_code = 0x81; | |
| 281 | + } else { | |
| 282 | + if (size < 11) { | |
| 283 | + err = -EINVAL; | |
| 284 | + goto out; | |
| 285 | + } | |
| 286 | + n->enabled = true; | |
| 287 | + n->command_code = params.command_code; | |
| 288 | + } | |
| 289 | + | |
| 290 | +out: | |
| 291 | + DRM_DEBUG_DRIVER("Notification %s, command code = %#x\n", | |
| 292 | + (n->enabled ? "enabled" : "disabled"), | |
| 293 | + n->command_code); | |
| 294 | + kfree(info); | |
| 295 | + return err; | |
| 296 | +} | |
| 297 | + | |
| 298 | +/** | |
| 299 | + * amdgpu_atif_get_sbios_requests - get requested sbios event | |
| 300 | + * | |
| 301 | + * @handle: acpi handle | |
| 302 | + * @req: atif sbios request struct | |
| 303 | + * | |
| 304 | + * Execute the ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS ATIF function | |
| 305 | + * to determine what requests the sbios is making to the driver | |
| 306 | + * (all asics). | |
| 307 | + * Returns 0 on success, error on failure. | |
| 308 | + */ | |
| 309 | +static int amdgpu_atif_get_sbios_requests(acpi_handle handle, | |
| 310 | + struct atif_sbios_requests *req) | |
| 311 | +{ | |
| 312 | + union acpi_object *info; | |
| 313 | + size_t size; | |
| 314 | + int count = 0; | |
| 315 | + | |
| 316 | + info = amdgpu_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS, NULL); | |
| 317 | + if (!info) | |
| 318 | + return -EIO; | |
| 319 | + | |
| 320 | + size = *(u16 *)info->buffer.pointer; | |
| 321 | + if (size < 0xd) { | |
| 322 | + count = -EINVAL; | |
| 323 | + goto out; | |
| 324 | + } | |
| 325 | + memset(req, 0, sizeof(*req)); | |
| 326 | + | |
| 327 | + size = min(sizeof(*req), size); | |
| 328 | + memcpy(req, info->buffer.pointer, size); | |
| 329 | + DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending); | |
| 330 | + | |
| 331 | + count = hweight32(req->pending); | |
| 332 | + | |
| 333 | +out: | |
| 334 | + kfree(info); | |
| 335 | + return count; | |
| 336 | +} | |
| 337 | + | |
| 338 | +/** | |
| 339 | + * amdgpu_atif_handler - handle ATIF notify requests | |
| 340 | + * | |
| 341 | + * @adev: amdgpu_device pointer | |
| 342 | + * @event: atif sbios request struct | |
| 343 | + * | |
| 344 | + * Checks the acpi event and if it matches an atif event, | |
| 345 | + * handles it. | |
| 346 | + * Returns NOTIFY code | |
| 347 | + */ | |
| 348 | +int amdgpu_atif_handler(struct amdgpu_device *adev, | |
| 349 | + struct acpi_bus_event *event) | |
| 350 | +{ | |
| 351 | + struct amdgpu_atif *atif = &adev->atif; | |
| 352 | + struct atif_sbios_requests req; | |
| 353 | + acpi_handle handle; | |
| 354 | + int count; | |
| 355 | + | |
| 356 | + DRM_DEBUG_DRIVER("event, device_class = %s, type = %#x\n", | |
| 357 | + event->device_class, event->type); | |
| 358 | + | |
| 359 | + if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) | |
| 360 | + return NOTIFY_DONE; | |
| 361 | + | |
| 362 | + if (!atif->notification_cfg.enabled || | |
| 363 | + event->type != atif->notification_cfg.command_code) | |
| 364 | + /* Not our event */ | |
| 365 | + return NOTIFY_DONE; | |
| 366 | + | |
| 367 | + /* Check pending SBIOS requests */ | |
| 368 | + handle = ACPI_HANDLE(&adev->pdev->dev); | |
| 369 | + count = amdgpu_atif_get_sbios_requests(handle, &req); | |
| 370 | + | |
| 371 | + if (count <= 0) | |
| 372 | + return NOTIFY_DONE; | |
| 373 | + | |
| 374 | + DRM_DEBUG_DRIVER("ATIF: %d pending SBIOS requests\n", count); | |
| 375 | + | |
| 376 | + if (req.pending & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST) { | |
| 377 | + struct amdgpu_encoder *enc = atif->encoder_for_bl; | |
| 378 | + | |
| 379 | + if (enc) { | |
| 380 | + struct amdgpu_encoder_atom_dig *dig = enc->enc_priv; | |
| 381 | + | |
| 382 | + DRM_DEBUG_DRIVER("Changing brightness to %d\n", | |
| 383 | + req.backlight_level); | |
| 384 | + | |
| 385 | + amdgpu_display_backlight_set_level(adev, enc, req.backlight_level); | |
| 386 | + | |
| 387 | +#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) | |
| 388 | + backlight_force_update(dig->bl_dev, | |
| 389 | + BACKLIGHT_UPDATE_HOTKEY); | |
| 390 | +#endif | |
| 391 | + } | |
| 392 | + } | |
| 393 | + /* TODO: check other events */ | |
| 394 | + | |
| 395 | + /* We've handled the event, stop the notifier chain. The ACPI interface | |
| 396 | + * overloads ACPI_VIDEO_NOTIFY_PROBE, we don't want to send that to | |
| 397 | + * userspace if the event was generated only to signal a SBIOS | |
| 398 | + * request. | |
| 399 | + */ | |
| 400 | + return NOTIFY_BAD; | |
| 401 | +} | |
| 402 | + | |
| 403 | +/* Call the ATCS method | |
| 404 | + */ | |
| 405 | +/** | |
| 406 | + * amdgpu_atcs_call - call an ATCS method | |
| 407 | + * | |
| 408 | + * @handle: acpi handle | |
| 409 | + * @function: the ATCS function to execute | |
| 410 | + * @params: ATCS function params | |
| 411 | + * | |
| 412 | + * Executes the requested ATCS function (all asics). | |
| 413 | + * Returns a pointer to the acpi output buffer. | |
| 414 | + */ | |
| 415 | +static union acpi_object *amdgpu_atcs_call(acpi_handle handle, int function, | |
| 416 | + struct acpi_buffer *params) | |
| 417 | +{ | |
| 418 | + acpi_status status; | |
| 419 | + union acpi_object atcs_arg_elements[2]; | |
| 420 | + struct acpi_object_list atcs_arg; | |
| 421 | + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | |
| 422 | + | |
| 423 | + atcs_arg.count = 2; | |
| 424 | + atcs_arg.pointer = &atcs_arg_elements[0]; | |
| 425 | + | |
| 426 | + atcs_arg_elements[0].type = ACPI_TYPE_INTEGER; | |
| 427 | + atcs_arg_elements[0].integer.value = function; | |
| 428 | + | |
| 429 | + if (params) { | |
| 430 | + atcs_arg_elements[1].type = ACPI_TYPE_BUFFER; | |
| 431 | + atcs_arg_elements[1].buffer.length = params->length; | |
| 432 | + atcs_arg_elements[1].buffer.pointer = params->pointer; | |
| 433 | + } else { | |
| 434 | + /* We need a second fake parameter */ | |
| 435 | + atcs_arg_elements[1].type = ACPI_TYPE_INTEGER; | |
| 436 | + atcs_arg_elements[1].integer.value = 0; | |
| 437 | + } | |
| 438 | + | |
| 439 | + status = acpi_evaluate_object(handle, "ATCS", &atcs_arg, &buffer); | |
| 440 | + | |
| 441 | + /* Fail only if calling the method fails and ATIF is supported */ | |
| 442 | + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { | |
| 443 | + DRM_DEBUG_DRIVER("failed to evaluate ATCS got %s\n", | |
| 444 | + acpi_format_exception(status)); | |
| 445 | + kfree(buffer.pointer); | |
| 446 | + return NULL; | |
| 447 | + } | |
| 448 | + | |
| 449 | + return buffer.pointer; | |
| 450 | +} | |
| 451 | + | |
| 452 | +/** | |
| 453 | + * amdgpu_atcs_parse_functions - parse supported functions | |
| 454 | + * | |
| 455 | + * @f: supported functions struct | |
| 456 | + * @mask: supported functions mask from ATCS | |
| 457 | + * | |
| 458 | + * Use the supported functions mask from ATCS function | |
| 459 | + * ATCS_FUNCTION_VERIFY_INTERFACE to determine what functions | |
| 460 | + * are supported (all asics). | |
| 461 | + */ | |
| 462 | +static void amdgpu_atcs_parse_functions(struct amdgpu_atcs_functions *f, u32 mask) | |
| 463 | +{ | |
| 464 | + f->get_ext_state = mask & ATCS_GET_EXTERNAL_STATE_SUPPORTED; | |
| 465 | + f->pcie_perf_req = mask & ATCS_PCIE_PERFORMANCE_REQUEST_SUPPORTED; | |
| 466 | + f->pcie_dev_rdy = mask & ATCS_PCIE_DEVICE_READY_NOTIFICATION_SUPPORTED; | |
| 467 | + f->pcie_bus_width = mask & ATCS_SET_PCIE_BUS_WIDTH_SUPPORTED; | |
| 468 | +} | |
| 469 | + | |
| 470 | +/** | |
| 471 | + * amdgpu_atcs_verify_interface - verify ATCS | |
| 472 | + * | |
| 473 | + * @handle: acpi handle | |
| 474 | + * @atcs: amdgpu atcs struct | |
| 475 | + * | |
| 476 | + * Execute the ATCS_FUNCTION_VERIFY_INTERFACE ATCS function | |
| 477 | + * to initialize ATCS and determine what features are supported | |
| 478 | + * (all asics). | |
| 479 | + * returns 0 on success, error on failure. | |
| 480 | + */ | |
| 481 | +static int amdgpu_atcs_verify_interface(acpi_handle handle, | |
| 482 | + struct amdgpu_atcs *atcs) | |
| 483 | +{ | |
| 484 | + union acpi_object *info; | |
| 485 | + struct atcs_verify_interface output; | |
| 486 | + size_t size; | |
| 487 | + int err = 0; | |
| 488 | + | |
| 489 | + info = amdgpu_atcs_call(handle, ATCS_FUNCTION_VERIFY_INTERFACE, NULL); | |
| 490 | + if (!info) | |
| 491 | + return -EIO; | |
| 492 | + | |
| 493 | + memset(&output, 0, sizeof(output)); | |
| 494 | + | |
| 495 | + size = *(u16 *) info->buffer.pointer; | |
| 496 | + if (size < 8) { | |
| 497 | + DRM_INFO("ATCS buffer is too small: %zu\n", size); | |
| 498 | + err = -EINVAL; | |
| 499 | + goto out; | |
| 500 | + } | |
| 501 | + size = min(sizeof(output), size); | |
| 502 | + | |
| 503 | + memcpy(&output, info->buffer.pointer, size); | |
| 504 | + | |
| 505 | + /* TODO: check version? */ | |
| 506 | + DRM_DEBUG_DRIVER("ATCS version %u\n", output.version); | |
| 507 | + | |
| 508 | + amdgpu_atcs_parse_functions(&atcs->functions, output.function_bits); | |
| 509 | + | |
| 510 | +out: | |
| 511 | + kfree(info); | |
| 512 | + return err; | |
| 513 | +} | |
| 514 | + | |
| 515 | +/** | |
| 516 | + * amdgpu_acpi_is_pcie_performance_request_supported | |
| 517 | + * | |
| 518 | + * @adev: amdgpu_device pointer | |
| 519 | + * | |
| 520 | + * Check if the ATCS pcie_perf_req and pcie_dev_rdy methods | |
| 521 | + * are supported (all asics). | |
| 522 | + * returns true if supported, false if not. | |
| 523 | + */ | |
| 524 | +bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev) | |
| 525 | +{ | |
| 526 | + struct amdgpu_atcs *atcs = &adev->atcs; | |
| 527 | + | |
| 528 | + if (atcs->functions.pcie_perf_req && atcs->functions.pcie_dev_rdy) | |
| 529 | + return true; | |
| 530 | + | |
| 531 | + return false; | |
| 532 | +} | |
| 533 | + | |
| 534 | +/** | |
| 535 | + * amdgpu_acpi_pcie_notify_device_ready | |
| 536 | + * | |
| 537 | + * @adev: amdgpu_device pointer | |
| 538 | + * | |
| 539 | + * Executes the PCIE_DEVICE_READY_NOTIFICATION method | |
| 540 | + * (all asics). | |
| 541 | + * returns 0 on success, error on failure. | |
| 542 | + */ | |
| 543 | +int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev) | |
| 544 | +{ | |
| 545 | + acpi_handle handle; | |
| 546 | + union acpi_object *info; | |
| 547 | + struct amdgpu_atcs *atcs = &adev->atcs; | |
| 548 | + | |
| 549 | + /* Get the device handle */ | |
| 550 | + handle = ACPI_HANDLE(&adev->pdev->dev); | |
| 551 | + if (!handle) | |
| 552 | + return -EINVAL; | |
| 553 | + | |
| 554 | + if (!atcs->functions.pcie_dev_rdy) | |
| 555 | + return -EINVAL; | |
| 556 | + | |
| 557 | + info = amdgpu_atcs_call(handle, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION, NULL); | |
| 558 | + if (!info) | |
| 559 | + return -EIO; | |
| 560 | + | |
| 561 | + kfree(info); | |
| 562 | + | |
| 563 | + return 0; | |
| 564 | +} | |
| 565 | + | |
| 566 | +/** | |
| 567 | + * amdgpu_acpi_pcie_performance_request | |
| 568 | + * | |
| 569 | + * @adev: amdgpu_device pointer | |
| 570 | + * @perf_req: requested perf level (pcie gen speed) | |
| 571 | + * @advertise: set advertise caps flag if set | |
| 572 | + * | |
| 573 | + * Executes the PCIE_PERFORMANCE_REQUEST method to | |
| 574 | + * change the pcie gen speed (all asics). | |
| 575 | + * returns 0 on success, error on failure. | |
| 576 | + */ | |
| 577 | +int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev, | |
| 578 | + u8 perf_req, bool advertise) | |
| 579 | +{ | |
| 580 | + acpi_handle handle; | |
| 581 | + union acpi_object *info; | |
| 582 | + struct amdgpu_atcs *atcs = &adev->atcs; | |
| 583 | + struct atcs_pref_req_input atcs_input; | |
| 584 | + struct atcs_pref_req_output atcs_output; | |
| 585 | + struct acpi_buffer params; | |
| 586 | + size_t size; | |
| 587 | + u32 retry = 3; | |
| 588 | + | |
| 589 | + /* Get the device handle */ | |
| 590 | + handle = ACPI_HANDLE(&adev->pdev->dev); | |
| 591 | + if (!handle) | |
| 592 | + return -EINVAL; | |
| 593 | + | |
| 594 | + if (!atcs->functions.pcie_perf_req) | |
| 595 | + return -EINVAL; | |
| 596 | + | |
| 597 | + atcs_input.size = sizeof(struct atcs_pref_req_input); | |
| 598 | + /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */ | |
| 599 | + atcs_input.client_id = adev->pdev->devfn | (adev->pdev->bus->number << 8); | |
| 600 | + atcs_input.valid_flags_mask = ATCS_VALID_FLAGS_MASK; | |
| 601 | + atcs_input.flags = ATCS_WAIT_FOR_COMPLETION; | |
| 602 | + if (advertise) | |
| 603 | + atcs_input.flags |= ATCS_ADVERTISE_CAPS; | |
| 604 | + atcs_input.req_type = ATCS_PCIE_LINK_SPEED; | |
| 605 | + atcs_input.perf_req = perf_req; | |
| 606 | + | |
| 607 | + params.length = sizeof(struct atcs_pref_req_input); | |
| 608 | + params.pointer = &atcs_input; | |
| 609 | + | |
| 610 | + while (retry--) { | |
| 611 | + info = amdgpu_atcs_call(handle, ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST, ¶ms); | |
| 612 | + if (!info) | |
| 613 | + return -EIO; | |
| 614 | + | |
| 615 | + memset(&atcs_output, 0, sizeof(atcs_output)); | |
| 616 | + | |
| 617 | + size = *(u16 *) info->buffer.pointer; | |
| 618 | + if (size < 3) { | |
| 619 | + DRM_INFO("ATCS buffer is too small: %zu\n", size); | |
| 620 | + kfree(info); | |
| 621 | + return -EINVAL; | |
| 622 | + } | |
| 623 | + size = min(sizeof(atcs_output), size); | |
| 624 | + | |
| 625 | + memcpy(&atcs_output, info->buffer.pointer, size); | |
| 626 | + | |
| 627 | + kfree(info); | |
| 628 | + | |
| 629 | + switch (atcs_output.ret_val) { | |
| 630 | + case ATCS_REQUEST_REFUSED: | |
| 631 | + default: | |
| 632 | + return -EINVAL; | |
| 633 | + case ATCS_REQUEST_COMPLETE: | |
| 634 | + return 0; | |
| 635 | + case ATCS_REQUEST_IN_PROGRESS: | |
| 636 | + udelay(10); | |
| 637 | + break; | |
| 638 | + } | |
| 639 | + } | |
| 640 | + | |
| 641 | + return 0; | |
| 642 | +} | |
| 643 | + | |
| 644 | +/** | |
| 645 | + * amdgpu_acpi_event - handle notify events | |
| 646 | + * | |
| 647 | + * @nb: notifier block | |
| 648 | + * @val: val | |
| 649 | + * @data: acpi event | |
| 650 | + * | |
| 651 | + * Calls relevant amdgpu functions in response to various | |
| 652 | + * acpi events. | |
| 653 | + * Returns NOTIFY code | |
| 654 | + */ | |
| 655 | +static int amdgpu_acpi_event(struct notifier_block *nb, | |
| 656 | + unsigned long val, | |
| 657 | + void *data) | |
| 658 | +{ | |
| 659 | + struct amdgpu_device *adev = container_of(nb, struct amdgpu_device, acpi_nb); | |
| 660 | + struct acpi_bus_event *entry = (struct acpi_bus_event *)data; | |
| 661 | + | |
| 662 | + if (strcmp(entry->device_class, ACPI_AC_CLASS) == 0) { | |
| 663 | + if (power_supply_is_system_supplied() > 0) | |
| 664 | + DRM_DEBUG_DRIVER("pm: AC\n"); | |
| 665 | + else | |
| 666 | + DRM_DEBUG_DRIVER("pm: DC\n"); | |
| 667 | + | |
| 668 | + amdgpu_pm_acpi_event_handler(adev); | |
| 669 | + } | |
| 670 | + | |
| 671 | + /* Check for pending SBIOS requests */ | |
| 672 | + return amdgpu_atif_handler(adev, entry); | |
| 673 | +} | |
| 674 | + | |
| 675 | +/* Call all ACPI methods here */ | |
| 676 | +/** | |
| 677 | + * amdgpu_acpi_init - init driver acpi support | |
| 678 | + * | |
| 679 | + * @adev: amdgpu_device pointer | |
| 680 | + * | |
| 681 | + * Verifies the AMD ACPI interfaces and registers with the acpi | |
| 682 | + * notifier chain (all asics). | |
| 683 | + * Returns 0 on success, error on failure. | |
| 684 | + */ | |
| 685 | +int amdgpu_acpi_init(struct amdgpu_device *adev) | |
| 686 | +{ | |
| 687 | + acpi_handle handle; | |
| 688 | + struct amdgpu_atif *atif = &adev->atif; | |
| 689 | + struct amdgpu_atcs *atcs = &adev->atcs; | |
| 690 | + int ret; | |
| 691 | + | |
| 692 | + /* Get the device handle */ | |
| 693 | + handle = ACPI_HANDLE(&adev->pdev->dev); | |
| 694 | + | |
| 695 | + if (!adev->bios || !handle) | |
| 696 | + return 0; | |
| 697 | + | |
| 698 | + /* Call the ATCS method */ | |
| 699 | + ret = amdgpu_atcs_verify_interface(handle, atcs); | |
| 700 | + if (ret) { | |
| 701 | + DRM_DEBUG_DRIVER("Call to ATCS verify_interface failed: %d\n", ret); | |
| 702 | + } | |
| 703 | + | |
| 704 | + /* Call the ATIF method */ | |
| 705 | + ret = amdgpu_atif_verify_interface(handle, atif); | |
| 706 | + if (ret) { | |
| 707 | + DRM_DEBUG_DRIVER("Call to ATIF verify_interface failed: %d\n", ret); | |
| 708 | + goto out; | |
| 709 | + } | |
| 710 | + | |
| 711 | + if (atif->notifications.brightness_change) { | |
| 712 | + struct drm_encoder *tmp; | |
| 713 | + | |
| 714 | + /* Find the encoder controlling the brightness */ | |
| 715 | + list_for_each_entry(tmp, &adev->ddev->mode_config.encoder_list, | |
| 716 | + head) { | |
| 717 | + struct amdgpu_encoder *enc = to_amdgpu_encoder(tmp); | |
| 718 | + | |
| 719 | + if ((enc->devices & (ATOM_DEVICE_LCD_SUPPORT)) && | |
| 720 | + enc->enc_priv) { | |
| 721 | + if (adev->is_atom_bios) { | |
| 722 | + struct amdgpu_encoder_atom_dig *dig = enc->enc_priv; | |
| 723 | + if (dig->bl_dev) { | |
| 724 | + atif->encoder_for_bl = enc; | |
| 725 | + break; | |
| 726 | + } | |
| 727 | + } | |
| 728 | + } | |
| 729 | + } | |
| 730 | + } | |
| 731 | + | |
| 732 | + if (atif->functions.sbios_requests && !atif->functions.system_params) { | |
| 733 | + /* XXX check this workraround, if sbios request function is | |
| 734 | + * present we have to see how it's configured in the system | |
| 735 | + * params | |
| 736 | + */ | |
| 737 | + atif->functions.system_params = true; | |
| 738 | + } | |
| 739 | + | |
| 740 | + if (atif->functions.system_params) { | |
| 741 | + ret = amdgpu_atif_get_notification_params(handle, | |
| 742 | + &atif->notification_cfg); | |
| 743 | + if (ret) { | |
| 744 | + DRM_DEBUG_DRIVER("Call to GET_SYSTEM_PARAMS failed: %d\n", | |
| 745 | + ret); | |
| 746 | + /* Disable notification */ | |
| 747 | + atif->notification_cfg.enabled = false; | |
| 748 | + } | |
| 749 | + } | |
| 750 | + | |
| 751 | +out: | |
| 752 | + adev->acpi_nb.notifier_call = amdgpu_acpi_event; | |
| 753 | + register_acpi_notifier(&adev->acpi_nb); | |
| 754 | + | |
| 755 | + return ret; | |
| 756 | +} | |
| 757 | + | |
| 758 | +/** | |
| 759 | + * amdgpu_acpi_fini - tear down driver acpi support | |
| 760 | + * | |
| 761 | + * @adev: amdgpu_device pointer | |
| 762 | + * | |
| 763 | + * Unregisters with the acpi notifier chain (all asics). | |
| 764 | + */ | |
| 765 | +void amdgpu_acpi_fini(struct amdgpu_device *adev) | |
| 766 | +{ | |
| 767 | + unregister_acpi_notifier(&adev->acpi_nb); | |
| 768 | +} | |
| @@ -0,0 +1,445 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2012 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#ifndef AMDGPU_ACPI_H | |
| 25 | +#define AMDGPU_ACPI_H | |
| 26 | + | |
| 27 | +struct amdgpu_device; | |
| 28 | +struct acpi_bus_event; | |
| 29 | + | |
| 30 | +int amdgpu_atif_handler(struct amdgpu_device *adev, | |
| 31 | + struct acpi_bus_event *event); | |
| 32 | + | |
| 33 | +/* AMD hw uses four ACPI control methods: | |
| 34 | + * 1. ATIF | |
| 35 | + * ARG0: (ACPI_INTEGER) function code | |
| 36 | + * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes | |
| 37 | + * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes | |
| 38 | + * ATIF provides an entry point for the gfx driver to interact with the sbios. | |
| 39 | + * The AMD ACPI notification mechanism uses Notify (VGA, 0x81) or a custom | |
| 40 | + * notification. Which notification is used as indicated by the ATIF Control | |
| 41 | + * Method GET_SYSTEM_PARAMETERS. When the driver receives Notify (VGA, 0x81) or | |
| 42 | + * a custom notification it invokes ATIF Control Method GET_SYSTEM_BIOS_REQUESTS | |
| 43 | + * to identify pending System BIOS requests and associated parameters. For | |
| 44 | + * example, if one of the pending requests is DISPLAY_SWITCH_REQUEST, the driver | |
| 45 | + * will perform display device detection and invoke ATIF Control Method | |
| 46 | + * SELECT_ACTIVE_DISPLAYS. | |
| 47 | + * | |
| 48 | + * 2. ATPX | |
| 49 | + * ARG0: (ACPI_INTEGER) function code | |
| 50 | + * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes | |
| 51 | + * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes | |
| 52 | + * ATPX methods are used on PowerXpress systems to handle mux switching and | |
| 53 | + * discrete GPU power control. | |
| 54 | + * | |
| 55 | + * 3. ATRM | |
| 56 | + * ARG0: (ACPI_INTEGER) offset of vbios rom data | |
| 57 | + * ARG1: (ACPI_BUFFER) size of the buffer to fill (up to 4K). | |
| 58 | + * OUTPUT: (ACPI_BUFFER) output buffer | |
| 59 | + * ATRM provides an interfacess to access the discrete GPU vbios image on | |
| 60 | + * PowerXpress systems with multiple GPUs. | |
| 61 | + * | |
| 62 | + * 4. ATCS | |
| 63 | + * ARG0: (ACPI_INTEGER) function code | |
| 64 | + * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes | |
| 65 | + * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes | |
| 66 | + * ATCS provides an interface to AMD chipset specific functionality. | |
| 67 | + * | |
| 68 | + */ | |
| 69 | +/* ATIF */ | |
| 70 | +#define ATIF_FUNCTION_VERIFY_INTERFACE 0x0 | |
| 71 | +/* ARG0: ATIF_FUNCTION_VERIFY_INTERFACE | |
| 72 | + * ARG1: none | |
| 73 | + * OUTPUT: | |
| 74 | + * WORD - structure size in bytes (includes size field) | |
| 75 | + * WORD - version | |
| 76 | + * DWORD - supported notifications mask | |
| 77 | + * DWORD - supported functions bit vector | |
| 78 | + */ | |
| 79 | +/* Notifications mask */ | |
| 80 | +# define ATIF_DISPLAY_SWITCH_REQUEST_SUPPORTED (1 << 0) | |
| 81 | +# define ATIF_EXPANSION_MODE_CHANGE_REQUEST_SUPPORTED (1 << 1) | |
| 82 | +# define ATIF_THERMAL_STATE_CHANGE_REQUEST_SUPPORTED (1 << 2) | |
| 83 | +# define ATIF_FORCED_POWER_STATE_CHANGE_REQUEST_SUPPORTED (1 << 3) | |
| 84 | +# define ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST_SUPPORTED (1 << 4) | |
| 85 | +# define ATIF_DISPLAY_CONF_CHANGE_REQUEST_SUPPORTED (1 << 5) | |
| 86 | +# define ATIF_PX_GFX_SWITCH_REQUEST_SUPPORTED (1 << 6) | |
| 87 | +# define ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED (1 << 7) | |
| 88 | +# define ATIF_DGPU_DISPLAY_EVENT_SUPPORTED (1 << 8) | |
| 89 | +/* supported functions vector */ | |
| 90 | +# define ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED (1 << 0) | |
| 91 | +# define ATIF_GET_SYSTEM_BIOS_REQUESTS_SUPPORTED (1 << 1) | |
| 92 | +# define ATIF_SELECT_ACTIVE_DISPLAYS_SUPPORTED (1 << 2) | |
| 93 | +# define ATIF_GET_LID_STATE_SUPPORTED (1 << 3) | |
| 94 | +# define ATIF_GET_TV_STANDARD_FROM_CMOS_SUPPORTED (1 << 4) | |
| 95 | +# define ATIF_SET_TV_STANDARD_IN_CMOS_SUPPORTED (1 << 5) | |
| 96 | +# define ATIF_GET_PANEL_EXPANSION_MODE_FROM_CMOS_SUPPORTED (1 << 6) | |
| 97 | +# define ATIF_SET_PANEL_EXPANSION_MODE_IN_CMOS_SUPPORTED (1 << 7) | |
| 98 | +# define ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED (1 << 12) | |
| 99 | +# define ATIF_GET_GRAPHICS_DEVICE_TYPES_SUPPORTED (1 << 14) | |
| 100 | +#define ATIF_FUNCTION_GET_SYSTEM_PARAMETERS 0x1 | |
| 101 | +/* ARG0: ATIF_FUNCTION_GET_SYSTEM_PARAMETERS | |
| 102 | + * ARG1: none | |
| 103 | + * OUTPUT: | |
| 104 | + * WORD - structure size in bytes (includes size field) | |
| 105 | + * DWORD - valid flags mask | |
| 106 | + * DWORD - flags | |
| 107 | + * | |
| 108 | + * OR | |
| 109 | + * | |
| 110 | + * WORD - structure size in bytes (includes size field) | |
| 111 | + * DWORD - valid flags mask | |
| 112 | + * DWORD - flags | |
| 113 | + * BYTE - notify command code | |
| 114 | + * | |
| 115 | + * flags | |
| 116 | + * bits 1:0: | |
| 117 | + * 0 - Notify(VGA, 0x81) is not used for notification | |
| 118 | + * 1 - Notify(VGA, 0x81) is used for notification | |
| 119 | + * 2 - Notify(VGA, n) is used for notification where | |
| 120 | + * n (0xd0-0xd9) is specified in notify command code. | |
| 121 | + * bit 2: | |
| 122 | + * 1 - lid changes not reported though int10 | |
| 123 | + */ | |
| 124 | +#define ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS 0x2 | |
| 125 | +/* ARG0: ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS | |
| 126 | + * ARG1: none | |
| 127 | + * OUTPUT: | |
| 128 | + * WORD - structure size in bytes (includes size field) | |
| 129 | + * DWORD - pending sbios requests | |
| 130 | + * BYTE - panel expansion mode | |
| 131 | + * BYTE - thermal state: target gfx controller | |
| 132 | + * BYTE - thermal state: state id (0: exit state, non-0: state) | |
| 133 | + * BYTE - forced power state: target gfx controller | |
| 134 | + * BYTE - forced power state: state id | |
| 135 | + * BYTE - system power source | |
| 136 | + * BYTE - panel backlight level (0-255) | |
| 137 | + */ | |
| 138 | +/* pending sbios requests */ | |
| 139 | +# define ATIF_DISPLAY_SWITCH_REQUEST (1 << 0) | |
| 140 | +# define ATIF_EXPANSION_MODE_CHANGE_REQUEST (1 << 1) | |
| 141 | +# define ATIF_THERMAL_STATE_CHANGE_REQUEST (1 << 2) | |
| 142 | +# define ATIF_FORCED_POWER_STATE_CHANGE_REQUEST (1 << 3) | |
| 143 | +# define ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST (1 << 4) | |
| 144 | +# define ATIF_DISPLAY_CONF_CHANGE_REQUEST (1 << 5) | |
| 145 | +# define ATIF_PX_GFX_SWITCH_REQUEST (1 << 6) | |
| 146 | +# define ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST (1 << 7) | |
| 147 | +# define ATIF_DGPU_DISPLAY_EVENT (1 << 8) | |
| 148 | +/* panel expansion mode */ | |
| 149 | +# define ATIF_PANEL_EXPANSION_DISABLE 0 | |
| 150 | +# define ATIF_PANEL_EXPANSION_FULL 1 | |
| 151 | +# define ATIF_PANEL_EXPANSION_ASPECT 2 | |
| 152 | +/* target gfx controller */ | |
| 153 | +# define ATIF_TARGET_GFX_SINGLE 0 | |
| 154 | +# define ATIF_TARGET_GFX_PX_IGPU 1 | |
| 155 | +# define ATIF_TARGET_GFX_PX_DGPU 2 | |
| 156 | +/* system power source */ | |
| 157 | +# define ATIF_POWER_SOURCE_AC 1 | |
| 158 | +# define ATIF_POWER_SOURCE_DC 2 | |
| 159 | +# define ATIF_POWER_SOURCE_RESTRICTED_AC_1 3 | |
| 160 | +# define ATIF_POWER_SOURCE_RESTRICTED_AC_2 4 | |
| 161 | +#define ATIF_FUNCTION_SELECT_ACTIVE_DISPLAYS 0x3 | |
| 162 | +/* ARG0: ATIF_FUNCTION_SELECT_ACTIVE_DISPLAYS | |
| 163 | + * ARG1: | |
| 164 | + * WORD - structure size in bytes (includes size field) | |
| 165 | + * WORD - selected displays | |
| 166 | + * WORD - connected displays | |
| 167 | + * OUTPUT: | |
| 168 | + * WORD - structure size in bytes (includes size field) | |
| 169 | + * WORD - selected displays | |
| 170 | + */ | |
| 171 | +# define ATIF_LCD1 (1 << 0) | |
| 172 | +# define ATIF_CRT1 (1 << 1) | |
| 173 | +# define ATIF_TV (1 << 2) | |
| 174 | +# define ATIF_DFP1 (1 << 3) | |
| 175 | +# define ATIF_CRT2 (1 << 4) | |
| 176 | +# define ATIF_LCD2 (1 << 5) | |
| 177 | +# define ATIF_DFP2 (1 << 7) | |
| 178 | +# define ATIF_CV (1 << 8) | |
| 179 | +# define ATIF_DFP3 (1 << 9) | |
| 180 | +# define ATIF_DFP4 (1 << 10) | |
| 181 | +# define ATIF_DFP5 (1 << 11) | |
| 182 | +# define ATIF_DFP6 (1 << 12) | |
| 183 | +#define ATIF_FUNCTION_GET_LID_STATE 0x4 | |
| 184 | +/* ARG0: ATIF_FUNCTION_GET_LID_STATE | |
| 185 | + * ARG1: none | |
| 186 | + * OUTPUT: | |
| 187 | + * WORD - structure size in bytes (includes size field) | |
| 188 | + * BYTE - lid state (0: open, 1: closed) | |
| 189 | + * | |
| 190 | + * GET_LID_STATE only works at boot and resume, for general lid | |
| 191 | + * status, use the kernel provided status | |
| 192 | + */ | |
| 193 | +#define ATIF_FUNCTION_GET_TV_STANDARD_FROM_CMOS 0x5 | |
| 194 | +/* ARG0: ATIF_FUNCTION_GET_TV_STANDARD_FROM_CMOS | |
| 195 | + * ARG1: none | |
| 196 | + * OUTPUT: | |
| 197 | + * WORD - structure size in bytes (includes size field) | |
| 198 | + * BYTE - 0 | |
| 199 | + * BYTE - TV standard | |
| 200 | + */ | |
| 201 | +# define ATIF_TV_STD_NTSC 0 | |
| 202 | +# define ATIF_TV_STD_PAL 1 | |
| 203 | +# define ATIF_TV_STD_PALM 2 | |
| 204 | +# define ATIF_TV_STD_PAL60 3 | |
| 205 | +# define ATIF_TV_STD_NTSCJ 4 | |
| 206 | +# define ATIF_TV_STD_PALCN 5 | |
| 207 | +# define ATIF_TV_STD_PALN 6 | |
| 208 | +# define ATIF_TV_STD_SCART_RGB 9 | |
| 209 | +#define ATIF_FUNCTION_SET_TV_STANDARD_IN_CMOS 0x6 | |
| 210 | +/* ARG0: ATIF_FUNCTION_SET_TV_STANDARD_IN_CMOS | |
| 211 | + * ARG1: | |
| 212 | + * WORD - structure size in bytes (includes size field) | |
| 213 | + * BYTE - 0 | |
| 214 | + * BYTE - TV standard | |
| 215 | + * OUTPUT: none | |
| 216 | + */ | |
| 217 | +#define ATIF_FUNCTION_GET_PANEL_EXPANSION_MODE_FROM_CMOS 0x7 | |
| 218 | +/* ARG0: ATIF_FUNCTION_GET_PANEL_EXPANSION_MODE_FROM_CMOS | |
| 219 | + * ARG1: none | |
| 220 | + * OUTPUT: | |
| 221 | + * WORD - structure size in bytes (includes size field) | |
| 222 | + * BYTE - panel expansion mode | |
| 223 | + */ | |
| 224 | +#define ATIF_FUNCTION_SET_PANEL_EXPANSION_MODE_IN_CMOS 0x8 | |
| 225 | +/* ARG0: ATIF_FUNCTION_SET_PANEL_EXPANSION_MODE_IN_CMOS | |
| 226 | + * ARG1: | |
| 227 | + * WORD - structure size in bytes (includes size field) | |
| 228 | + * BYTE - panel expansion mode | |
| 229 | + * OUTPUT: none | |
| 230 | + */ | |
| 231 | +#define ATIF_FUNCTION_TEMPERATURE_CHANGE_NOTIFICATION 0xD | |
| 232 | +/* ARG0: ATIF_FUNCTION_TEMPERATURE_CHANGE_NOTIFICATION | |
| 233 | + * ARG1: | |
| 234 | + * WORD - structure size in bytes (includes size field) | |
| 235 | + * WORD - gfx controller id | |
| 236 | + * BYTE - current temperature (degress Celsius) | |
| 237 | + * OUTPUT: none | |
| 238 | + */ | |
| 239 | +#define ATIF_FUNCTION_GET_GRAPHICS_DEVICE_TYPES 0xF | |
| 240 | +/* ARG0: ATIF_FUNCTION_GET_GRAPHICS_DEVICE_TYPES | |
| 241 | + * ARG1: none | |
| 242 | + * OUTPUT: | |
| 243 | + * WORD - number of gfx devices | |
| 244 | + * WORD - device structure size in bytes (excludes device size field) | |
| 245 | + * DWORD - flags \ | |
| 246 | + * WORD - bus number } repeated structure | |
| 247 | + * WORD - device number / | |
| 248 | + */ | |
| 249 | +/* flags */ | |
| 250 | +# define ATIF_PX_REMOVABLE_GRAPHICS_DEVICE (1 << 0) | |
| 251 | +# define ATIF_XGP_PORT (1 << 1) | |
| 252 | +# define ATIF_VGA_ENABLED_GRAPHICS_DEVICE (1 << 2) | |
| 253 | +# define ATIF_XGP_PORT_IN_DOCK (1 << 3) | |
| 254 | + | |
| 255 | +/* ATPX */ | |
| 256 | +#define ATPX_FUNCTION_VERIFY_INTERFACE 0x0 | |
| 257 | +/* ARG0: ATPX_FUNCTION_VERIFY_INTERFACE | |
| 258 | + * ARG1: none | |
| 259 | + * OUTPUT: | |
| 260 | + * WORD - structure size in bytes (includes size field) | |
| 261 | + * WORD - version | |
| 262 | + * DWORD - supported functions bit vector | |
| 263 | + */ | |
| 264 | +/* supported functions vector */ | |
| 265 | +# define ATPX_GET_PX_PARAMETERS_SUPPORTED (1 << 0) | |
| 266 | +# define ATPX_POWER_CONTROL_SUPPORTED (1 << 1) | |
| 267 | +# define ATPX_DISPLAY_MUX_CONTROL_SUPPORTED (1 << 2) | |
| 268 | +# define ATPX_I2C_MUX_CONTROL_SUPPORTED (1 << 3) | |
| 269 | +# define ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED (1 << 4) | |
| 270 | +# define ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED (1 << 5) | |
| 271 | +# define ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED (1 << 7) | |
| 272 | +# define ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED (1 << 8) | |
| 273 | +#define ATPX_FUNCTION_GET_PX_PARAMETERS 0x1 | |
| 274 | +/* ARG0: ATPX_FUNCTION_GET_PX_PARAMETERS | |
| 275 | + * ARG1: none | |
| 276 | + * OUTPUT: | |
| 277 | + * WORD - structure size in bytes (includes size field) | |
| 278 | + * DWORD - valid flags mask | |
| 279 | + * DWORD - flags | |
| 280 | + */ | |
| 281 | +/* flags */ | |
| 282 | +# define ATPX_LVDS_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 0) | |
| 283 | +# define ATPX_CRT1_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 1) | |
| 284 | +# define ATPX_DVI1_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 2) | |
| 285 | +# define ATPX_CRT1_RGB_SIGNAL_MUXED (1 << 3) | |
| 286 | +# define ATPX_TV_SIGNAL_MUXED (1 << 4) | |
| 287 | +# define ATPX_DFP_SIGNAL_MUXED (1 << 5) | |
| 288 | +# define ATPX_SEPARATE_MUX_FOR_I2C (1 << 6) | |
| 289 | +# define ATPX_DYNAMIC_PX_SUPPORTED (1 << 7) | |
| 290 | +# define ATPX_ACF_NOT_SUPPORTED (1 << 8) | |
| 291 | +# define ATPX_FIXED_NOT_SUPPORTED (1 << 9) | |
| 292 | +# define ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED (1 << 10) | |
| 293 | +# define ATPX_DGPU_REQ_POWER_FOR_DISPLAYS (1 << 11) | |
| 294 | +#define ATPX_FUNCTION_POWER_CONTROL 0x2 | |
| 295 | +/* ARG0: ATPX_FUNCTION_POWER_CONTROL | |
| 296 | + * ARG1: | |
| 297 | + * WORD - structure size in bytes (includes size field) | |
| 298 | + * BYTE - dGPU power state (0: power off, 1: power on) | |
| 299 | + * OUTPUT: none | |
| 300 | + */ | |
| 301 | +#define ATPX_FUNCTION_DISPLAY_MUX_CONTROL 0x3 | |
| 302 | +/* ARG0: ATPX_FUNCTION_DISPLAY_MUX_CONTROL | |
| 303 | + * ARG1: | |
| 304 | + * WORD - structure size in bytes (includes size field) | |
| 305 | + * WORD - display mux control (0: iGPU, 1: dGPU) | |
| 306 | + * OUTPUT: none | |
| 307 | + */ | |
| 308 | +# define ATPX_INTEGRATED_GPU 0 | |
| 309 | +# define ATPX_DISCRETE_GPU 1 | |
| 310 | +#define ATPX_FUNCTION_I2C_MUX_CONTROL 0x4 | |
| 311 | +/* ARG0: ATPX_FUNCTION_I2C_MUX_CONTROL | |
| 312 | + * ARG1: | |
| 313 | + * WORD - structure size in bytes (includes size field) | |
| 314 | + * WORD - i2c/aux/hpd mux control (0: iGPU, 1: dGPU) | |
| 315 | + * OUTPUT: none | |
| 316 | + */ | |
| 317 | +#define ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION 0x5 | |
| 318 | +/* ARG0: ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION | |
| 319 | + * ARG1: | |
| 320 | + * WORD - structure size in bytes (includes size field) | |
| 321 | + * WORD - target gpu (0: iGPU, 1: dGPU) | |
| 322 | + * OUTPUT: none | |
| 323 | + */ | |
| 324 | +#define ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION 0x6 | |
| 325 | +/* ARG0: ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION | |
| 326 | + * ARG1: | |
| 327 | + * WORD - structure size in bytes (includes size field) | |
| 328 | + * WORD - target gpu (0: iGPU, 1: dGPU) | |
| 329 | + * OUTPUT: none | |
| 330 | + */ | |
| 331 | +#define ATPX_FUNCTION_GET_DISPLAY_CONNECTORS_MAPPING 0x8 | |
| 332 | +/* ARG0: ATPX_FUNCTION_GET_DISPLAY_CONNECTORS_MAPPING | |
| 333 | + * ARG1: none | |
| 334 | + * OUTPUT: | |
| 335 | + * WORD - number of display connectors | |
| 336 | + * WORD - connector structure size in bytes (excludes connector size field) | |
| 337 | + * BYTE - flags \ | |
| 338 | + * BYTE - ATIF display vector bit position } repeated | |
| 339 | + * BYTE - adapter id (0: iGPU, 1-n: dGPU ordered by pcie bus number) } structure | |
| 340 | + * WORD - connector ACPI id / | |
| 341 | + */ | |
| 342 | +/* flags */ | |
| 343 | +# define ATPX_DISPLAY_OUTPUT_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 0) | |
| 344 | +# define ATPX_DISPLAY_HPD_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 1) | |
| 345 | +# define ATPX_DISPLAY_I2C_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 2) | |
| 346 | +#define ATPX_FUNCTION_GET_DISPLAY_DETECTION_PORTS 0x9 | |
| 347 | +/* ARG0: ATPX_FUNCTION_GET_DISPLAY_DETECTION_PORTS | |
| 348 | + * ARG1: none | |
| 349 | + * OUTPUT: | |
| 350 | + * WORD - number of HPD/DDC ports | |
| 351 | + * WORD - port structure size in bytes (excludes port size field) | |
| 352 | + * BYTE - ATIF display vector bit position \ | |
| 353 | + * BYTE - hpd id } reapeated structure | |
| 354 | + * BYTE - ddc id / | |
| 355 | + * | |
| 356 | + * available on A+A systems only | |
| 357 | + */ | |
| 358 | +/* hpd id */ | |
| 359 | +# define ATPX_HPD_NONE 0 | |
| 360 | +# define ATPX_HPD1 1 | |
| 361 | +# define ATPX_HPD2 2 | |
| 362 | +# define ATPX_HPD3 3 | |
| 363 | +# define ATPX_HPD4 4 | |
| 364 | +# define ATPX_HPD5 5 | |
| 365 | +# define ATPX_HPD6 6 | |
| 366 | +/* ddc id */ | |
| 367 | +# define ATPX_DDC_NONE 0 | |
| 368 | +# define ATPX_DDC1 1 | |
| 369 | +# define ATPX_DDC2 2 | |
| 370 | +# define ATPX_DDC3 3 | |
| 371 | +# define ATPX_DDC4 4 | |
| 372 | +# define ATPX_DDC5 5 | |
| 373 | +# define ATPX_DDC6 6 | |
| 374 | +# define ATPX_DDC7 7 | |
| 375 | +# define ATPX_DDC8 8 | |
| 376 | + | |
| 377 | +/* ATCS */ | |
| 378 | +#define ATCS_FUNCTION_VERIFY_INTERFACE 0x0 | |
| 379 | +/* ARG0: ATCS_FUNCTION_VERIFY_INTERFACE | |
| 380 | + * ARG1: none | |
| 381 | + * OUTPUT: | |
| 382 | + * WORD - structure size in bytes (includes size field) | |
| 383 | + * WORD - version | |
| 384 | + * DWORD - supported functions bit vector | |
| 385 | + */ | |
| 386 | +/* supported functions vector */ | |
| 387 | +# define ATCS_GET_EXTERNAL_STATE_SUPPORTED (1 << 0) | |
| 388 | +# define ATCS_PCIE_PERFORMANCE_REQUEST_SUPPORTED (1 << 1) | |
| 389 | +# define ATCS_PCIE_DEVICE_READY_NOTIFICATION_SUPPORTED (1 << 2) | |
| 390 | +# define ATCS_SET_PCIE_BUS_WIDTH_SUPPORTED (1 << 3) | |
| 391 | +#define ATCS_FUNCTION_GET_EXTERNAL_STATE 0x1 | |
| 392 | +/* ARG0: ATCS_FUNCTION_GET_EXTERNAL_STATE | |
| 393 | + * ARG1: none | |
| 394 | + * OUTPUT: | |
| 395 | + * WORD - structure size in bytes (includes size field) | |
| 396 | + * DWORD - valid flags mask | |
| 397 | + * DWORD - flags (0: undocked, 1: docked) | |
| 398 | + */ | |
| 399 | +/* flags */ | |
| 400 | +# define ATCS_DOCKED (1 << 0) | |
| 401 | +#define ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST 0x2 | |
| 402 | +/* ARG0: ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST | |
| 403 | + * ARG1: | |
| 404 | + * WORD - structure size in bytes (includes size field) | |
| 405 | + * WORD - client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) | |
| 406 | + * WORD - valid flags mask | |
| 407 | + * WORD - flags | |
| 408 | + * BYTE - request type | |
| 409 | + * BYTE - performance request | |
| 410 | + * OUTPUT: | |
| 411 | + * WORD - structure size in bytes (includes size field) | |
| 412 | + * BYTE - return value | |
| 413 | + */ | |
| 414 | +/* flags */ | |
| 415 | +# define ATCS_ADVERTISE_CAPS (1 << 0) | |
| 416 | +# define ATCS_WAIT_FOR_COMPLETION (1 << 1) | |
| 417 | +/* request type */ | |
| 418 | +# define ATCS_PCIE_LINK_SPEED 1 | |
| 419 | +/* performance request */ | |
| 420 | +# define ATCS_REMOVE 0 | |
| 421 | +# define ATCS_FORCE_LOW_POWER 1 | |
| 422 | +# define ATCS_PERF_LEVEL_1 2 /* PCIE Gen 1 */ | |
| 423 | +# define ATCS_PERF_LEVEL_2 3 /* PCIE Gen 2 */ | |
| 424 | +# define ATCS_PERF_LEVEL_3 4 /* PCIE Gen 3 */ | |
| 425 | +/* return value */ | |
| 426 | +# define ATCS_REQUEST_REFUSED 1 | |
| 427 | +# define ATCS_REQUEST_COMPLETE 2 | |
| 428 | +# define ATCS_REQUEST_IN_PROGRESS 3 | |
| 429 | +#define ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION 0x3 | |
| 430 | +/* ARG0: ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION | |
| 431 | + * ARG1: none | |
| 432 | + * OUTPUT: none | |
| 433 | + */ | |
| 434 | +#define ATCS_FUNCTION_SET_PCIE_BUS_WIDTH 0x4 | |
| 435 | +/* ARG0: ATCS_FUNCTION_SET_PCIE_BUS_WIDTH | |
| 436 | + * ARG1: | |
| 437 | + * WORD - structure size in bytes (includes size field) | |
| 438 | + * WORD - client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) | |
| 439 | + * BYTE - number of active lanes | |
| 440 | + * OUTPUT: | |
| 441 | + * WORD - structure size in bytes (includes size field) | |
| 442 | + * BYTE - number of active lanes | |
| 443 | + */ | |
| 444 | + | |
| 445 | +#endif | |
| @@ -0,0 +1,105 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Christian König. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Christian König | |
| 25 | + */ | |
| 26 | +#include <linux/hdmi.h> | |
| 27 | +#include <linux/gcd.h> | |
| 28 | +#include <drm/drmP.h> | |
| 29 | +#include <drm/amdgpu_drm.h> | |
| 30 | +#include "amdgpu.h" | |
| 31 | + | |
| 32 | +static const struct amdgpu_afmt_acr amdgpu_afmt_predefined_acr[] = { | |
| 33 | + /* 32kHz 44.1kHz 48kHz */ | |
| 34 | + /* Clock N CTS N CTS N CTS */ | |
| 35 | + { 25175, 4096, 25175, 28224, 125875, 6144, 25175 }, /* 25,20/1.001 MHz */ | |
| 36 | + { 25200, 4096, 25200, 6272, 28000, 6144, 25200 }, /* 25.20 MHz */ | |
| 37 | + { 27000, 4096, 27000, 6272, 30000, 6144, 27000 }, /* 27.00 MHz */ | |
| 38 | + { 27027, 4096, 27027, 6272, 30030, 6144, 27027 }, /* 27.00*1.001 MHz */ | |
| 39 | + { 54000, 4096, 54000, 6272, 60000, 6144, 54000 }, /* 54.00 MHz */ | |
| 40 | + { 54054, 4096, 54054, 6272, 60060, 6144, 54054 }, /* 54.00*1.001 MHz */ | |
| 41 | + { 74176, 4096, 74176, 5733, 75335, 6144, 74176 }, /* 74.25/1.001 MHz */ | |
| 42 | + { 74250, 4096, 74250, 6272, 82500, 6144, 74250 }, /* 74.25 MHz */ | |
| 43 | + { 148352, 4096, 148352, 5733, 150670, 6144, 148352 }, /* 148.50/1.001 MHz */ | |
| 44 | + { 148500, 4096, 148500, 6272, 165000, 6144, 148500 }, /* 148.50 MHz */ | |
| 45 | +}; | |
| 46 | + | |
| 47 | + | |
| 48 | +/* | |
| 49 | + * calculate CTS and N values if they are not found in the table | |
| 50 | + */ | |
| 51 | +static void amdgpu_afmt_calc_cts(uint32_t clock, int *CTS, int *N, int freq) | |
| 52 | +{ | |
| 53 | + int n, cts; | |
| 54 | + unsigned long div, mul; | |
| 55 | + | |
| 56 | + /* Safe, but overly large values */ | |
| 57 | + n = 128 * freq; | |
| 58 | + cts = clock * 1000; | |
| 59 | + | |
| 60 | + /* Smallest valid fraction */ | |
| 61 | + div = gcd(n, cts); | |
| 62 | + | |
| 63 | + n /= div; | |
| 64 | + cts /= div; | |
| 65 | + | |
| 66 | + /* | |
| 67 | + * The optimal N is 128*freq/1000. Calculate the closest larger | |
| 68 | + * value that doesn't truncate any bits. | |
| 69 | + */ | |
| 70 | + mul = ((128*freq/1000) + (n-1))/n; | |
| 71 | + | |
| 72 | + n *= mul; | |
| 73 | + cts *= mul; | |
| 74 | + | |
| 75 | + /* Check that we are in spec (not always possible) */ | |
| 76 | + if (n < (128*freq/1500)) | |
| 77 | + printk(KERN_WARNING "Calculated ACR N value is too small. You may experience audio problems.\n"); | |
| 78 | + if (n > (128*freq/300)) | |
| 79 | + printk(KERN_WARNING "Calculated ACR N value is too large. You may experience audio problems.\n"); | |
| 80 | + | |
| 81 | + *N = n; | |
| 82 | + *CTS = cts; | |
| 83 | + | |
| 84 | + DRM_DEBUG("Calculated ACR timing N=%d CTS=%d for frequency %d\n", | |
| 85 | + *N, *CTS, freq); | |
| 86 | +} | |
| 87 | + | |
| 88 | +struct amdgpu_afmt_acr amdgpu_afmt_acr(uint32_t clock) | |
| 89 | +{ | |
| 90 | + struct amdgpu_afmt_acr res; | |
| 91 | + u8 i; | |
| 92 | + | |
| 93 | + /* Precalculated values for common clocks */ | |
| 94 | + for (i = 0; i < ARRAY_SIZE(amdgpu_afmt_predefined_acr); i++) { | |
| 95 | + if (amdgpu_afmt_predefined_acr[i].clock == clock) | |
| 96 | + return amdgpu_afmt_predefined_acr[i]; | |
| 97 | + } | |
| 98 | + | |
| 99 | + /* And odd clocks get manually calculated */ | |
| 100 | + amdgpu_afmt_calc_cts(clock, &res.cts_32khz, &res.n_32khz, 32000); | |
| 101 | + amdgpu_afmt_calc_cts(clock, &res.cts_44_1khz, &res.n_44_1khz, 44100); | |
| 102 | + amdgpu_afmt_calc_cts(clock, &res.cts_48khz, &res.n_48khz, 48000); | |
| 103 | + | |
| 104 | + return res; | |
| 105 | +} | |
| @@ -0,0 +1,1598 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2007-8 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the "Software"), | |
| 7 | + * to deal in the Software without restriction, including without limitation | |
| 8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 10 | + * Software is furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in | |
| 13 | + * all copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 18 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 19 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 20 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 21 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 22 | + * | |
| 23 | + * Authors: Dave Airlie | |
| 24 | + * Alex Deucher | |
| 25 | + */ | |
| 26 | +#include <drm/drmP.h> | |
| 27 | +#include <drm/amdgpu_drm.h> | |
| 28 | +#include "amdgpu.h" | |
| 29 | +#include "amdgpu_atombios.h" | |
| 30 | +#include "amdgpu_i2c.h" | |
| 31 | + | |
| 32 | +#include "atom.h" | |
| 33 | +#include "atom-bits.h" | |
| 34 | +#include "atombios_encoders.h" | |
| 35 | +#include "bif/bif_4_1_d.h" | |
| 36 | + | |
| 37 | +static void amdgpu_atombios_lookup_i2c_gpio_quirks(struct amdgpu_device *adev, | |
| 38 | + ATOM_GPIO_I2C_ASSIGMENT *gpio, | |
| 39 | + u8 index) | |
| 40 | +{ | |
| 41 | + | |
| 42 | +} | |
| 43 | + | |
| 44 | +static struct amdgpu_i2c_bus_rec amdgpu_atombios_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio) | |
| 45 | +{ | |
| 46 | + struct amdgpu_i2c_bus_rec i2c; | |
| 47 | + | |
| 48 | + memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); | |
| 49 | + | |
| 50 | + i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex); | |
| 51 | + i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex); | |
| 52 | + i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex); | |
| 53 | + i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex); | |
| 54 | + i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex); | |
| 55 | + i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex); | |
| 56 | + i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex); | |
| 57 | + i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex); | |
| 58 | + i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); | |
| 59 | + i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); | |
| 60 | + i2c.en_clk_mask = (1 << gpio->ucClkEnShift); | |
| 61 | + i2c.en_data_mask = (1 << gpio->ucDataEnShift); | |
| 62 | + i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); | |
| 63 | + i2c.y_data_mask = (1 << gpio->ucDataY_Shift); | |
| 64 | + i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); | |
| 65 | + i2c.a_data_mask = (1 << gpio->ucDataA_Shift); | |
| 66 | + | |
| 67 | + if (gpio->sucI2cId.sbfAccess.bfHW_Capable) | |
| 68 | + i2c.hw_capable = true; | |
| 69 | + else | |
| 70 | + i2c.hw_capable = false; | |
| 71 | + | |
| 72 | + if (gpio->sucI2cId.ucAccess == 0xa0) | |
| 73 | + i2c.mm_i2c = true; | |
| 74 | + else | |
| 75 | + i2c.mm_i2c = false; | |
| 76 | + | |
| 77 | + i2c.i2c_id = gpio->sucI2cId.ucAccess; | |
| 78 | + | |
| 79 | + if (i2c.mask_clk_reg) | |
| 80 | + i2c.valid = true; | |
| 81 | + else | |
| 82 | + i2c.valid = false; | |
| 83 | + | |
| 84 | + return i2c; | |
| 85 | +} | |
| 86 | + | |
| 87 | +struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev, | |
| 88 | + uint8_t id) | |
| 89 | +{ | |
| 90 | + struct atom_context *ctx = adev->mode_info.atom_context; | |
| 91 | + ATOM_GPIO_I2C_ASSIGMENT *gpio; | |
| 92 | + struct amdgpu_i2c_bus_rec i2c; | |
| 93 | + int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); | |
| 94 | + struct _ATOM_GPIO_I2C_INFO *i2c_info; | |
| 95 | + uint16_t data_offset, size; | |
| 96 | + int i, num_indices; | |
| 97 | + | |
| 98 | + memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); | |
| 99 | + i2c.valid = false; | |
| 100 | + | |
| 101 | + if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { | |
| 102 | + i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); | |
| 103 | + | |
| 104 | + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / | |
| 105 | + sizeof(ATOM_GPIO_I2C_ASSIGMENT); | |
| 106 | + | |
| 107 | + gpio = &i2c_info->asGPIO_Info[0]; | |
| 108 | + for (i = 0; i < num_indices; i++) { | |
| 109 | + | |
| 110 | + amdgpu_atombios_lookup_i2c_gpio_quirks(adev, gpio, i); | |
| 111 | + | |
| 112 | + if (gpio->sucI2cId.ucAccess == id) { | |
| 113 | + i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); | |
| 114 | + break; | |
| 115 | + } | |
| 116 | + gpio = (ATOM_GPIO_I2C_ASSIGMENT *) | |
| 117 | + ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); | |
| 118 | + } | |
| 119 | + } | |
| 120 | + | |
| 121 | + return i2c; | |
| 122 | +} | |
| 123 | + | |
| 124 | +void amdgpu_atombios_i2c_init(struct amdgpu_device *adev) | |
| 125 | +{ | |
| 126 | + struct atom_context *ctx = adev->mode_info.atom_context; | |
| 127 | + ATOM_GPIO_I2C_ASSIGMENT *gpio; | |
| 128 | + struct amdgpu_i2c_bus_rec i2c; | |
| 129 | + int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); | |
| 130 | + struct _ATOM_GPIO_I2C_INFO *i2c_info; | |
| 131 | + uint16_t data_offset, size; | |
| 132 | + int i, num_indices; | |
| 133 | + char stmp[32]; | |
| 134 | + | |
| 135 | + if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { | |
| 136 | + i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); | |
| 137 | + | |
| 138 | + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / | |
| 139 | + sizeof(ATOM_GPIO_I2C_ASSIGMENT); | |
| 140 | + | |
| 141 | + gpio = &i2c_info->asGPIO_Info[0]; | |
| 142 | + for (i = 0; i < num_indices; i++) { | |
| 143 | + amdgpu_atombios_lookup_i2c_gpio_quirks(adev, gpio, i); | |
| 144 | + | |
| 145 | + i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); | |
| 146 | + | |
| 147 | + if (i2c.valid) { | |
| 148 | + sprintf(stmp, "0x%x", i2c.i2c_id); | |
| 149 | + adev->i2c_bus[i] = amdgpu_i2c_create(adev->ddev, &i2c, stmp); | |
| 150 | + } | |
| 151 | + gpio = (ATOM_GPIO_I2C_ASSIGMENT *) | |
| 152 | + ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); | |
| 153 | + } | |
| 154 | + } | |
| 155 | +} | |
| 156 | + | |
| 157 | +struct amdgpu_gpio_rec | |
| 158 | +amdgpu_atombios_lookup_gpio(struct amdgpu_device *adev, | |
| 159 | + u8 id) | |
| 160 | +{ | |
| 161 | + struct atom_context *ctx = adev->mode_info.atom_context; | |
| 162 | + struct amdgpu_gpio_rec gpio; | |
| 163 | + int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); | |
| 164 | + struct _ATOM_GPIO_PIN_LUT *gpio_info; | |
| 165 | + ATOM_GPIO_PIN_ASSIGNMENT *pin; | |
| 166 | + u16 data_offset, size; | |
| 167 | + int i, num_indices; | |
| 168 | + | |
| 169 | + memset(&gpio, 0, sizeof(struct amdgpu_gpio_rec)); | |
| 170 | + gpio.valid = false; | |
| 171 | + | |
| 172 | + if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { | |
| 173 | + gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset); | |
| 174 | + | |
| 175 | + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / | |
| 176 | + sizeof(ATOM_GPIO_PIN_ASSIGNMENT); | |
| 177 | + | |
| 178 | + pin = gpio_info->asGPIO_Pin; | |
| 179 | + for (i = 0; i < num_indices; i++) { | |
| 180 | + if (id == pin->ucGPIO_ID) { | |
| 181 | + gpio.id = pin->ucGPIO_ID; | |
| 182 | + gpio.reg = le16_to_cpu(pin->usGpioPin_AIndex); | |
| 183 | + gpio.shift = pin->ucGpioPinBitShift; | |
| 184 | + gpio.mask = (1 << pin->ucGpioPinBitShift); | |
| 185 | + gpio.valid = true; | |
| 186 | + break; | |
| 187 | + } | |
| 188 | + pin = (ATOM_GPIO_PIN_ASSIGNMENT *) | |
| 189 | + ((u8 *)pin + sizeof(ATOM_GPIO_PIN_ASSIGNMENT)); | |
| 190 | + } | |
| 191 | + } | |
| 192 | + | |
| 193 | + return gpio; | |
| 194 | +} | |
| 195 | + | |
| 196 | +static struct amdgpu_hpd | |
| 197 | +amdgpu_atombios_get_hpd_info_from_gpio(struct amdgpu_device *adev, | |
| 198 | + struct amdgpu_gpio_rec *gpio) | |
| 199 | +{ | |
| 200 | + struct amdgpu_hpd hpd; | |
| 201 | + u32 reg; | |
| 202 | + | |
| 203 | + memset(&hpd, 0, sizeof(struct amdgpu_hpd)); | |
| 204 | + | |
| 205 | + reg = amdgpu_display_hpd_get_gpio_reg(adev); | |
| 206 | + | |
| 207 | + hpd.gpio = *gpio; | |
| 208 | + if (gpio->reg == reg) { | |
| 209 | + switch(gpio->mask) { | |
| 210 | + case (1 << 0): | |
| 211 | + hpd.hpd = AMDGPU_HPD_1; | |
| 212 | + break; | |
| 213 | + case (1 << 8): | |
| 214 | + hpd.hpd = AMDGPU_HPD_2; | |
| 215 | + break; | |
| 216 | + case (1 << 16): | |
| 217 | + hpd.hpd = AMDGPU_HPD_3; | |
| 218 | + break; | |
| 219 | + case (1 << 24): | |
| 220 | + hpd.hpd = AMDGPU_HPD_4; | |
| 221 | + break; | |
| 222 | + case (1 << 26): | |
| 223 | + hpd.hpd = AMDGPU_HPD_5; | |
| 224 | + break; | |
| 225 | + case (1 << 28): | |
| 226 | + hpd.hpd = AMDGPU_HPD_6; | |
| 227 | + break; | |
| 228 | + default: | |
| 229 | + hpd.hpd = AMDGPU_HPD_NONE; | |
| 230 | + break; | |
| 231 | + } | |
| 232 | + } else | |
| 233 | + hpd.hpd = AMDGPU_HPD_NONE; | |
| 234 | + return hpd; | |
| 235 | +} | |
| 236 | + | |
| 237 | +static bool amdgpu_atombios_apply_quirks(struct amdgpu_device *adev, | |
| 238 | + uint32_t supported_device, | |
| 239 | + int *connector_type, | |
| 240 | + struct amdgpu_i2c_bus_rec *i2c_bus, | |
| 241 | + uint16_t *line_mux, | |
| 242 | + struct amdgpu_hpd *hpd) | |
| 243 | +{ | |
| 244 | + return true; | |
| 245 | +} | |
| 246 | + | |
| 247 | +static const int object_connector_convert[] = { | |
| 248 | + DRM_MODE_CONNECTOR_Unknown, | |
| 249 | + DRM_MODE_CONNECTOR_DVII, | |
| 250 | + DRM_MODE_CONNECTOR_DVII, | |
| 251 | + DRM_MODE_CONNECTOR_DVID, | |
| 252 | + DRM_MODE_CONNECTOR_DVID, | |
| 253 | + DRM_MODE_CONNECTOR_VGA, | |
| 254 | + DRM_MODE_CONNECTOR_Composite, | |
| 255 | + DRM_MODE_CONNECTOR_SVIDEO, | |
| 256 | + DRM_MODE_CONNECTOR_Unknown, | |
| 257 | + DRM_MODE_CONNECTOR_Unknown, | |
| 258 | + DRM_MODE_CONNECTOR_9PinDIN, | |
| 259 | + DRM_MODE_CONNECTOR_Unknown, | |
| 260 | + DRM_MODE_CONNECTOR_HDMIA, | |
| 261 | + DRM_MODE_CONNECTOR_HDMIB, | |
| 262 | + DRM_MODE_CONNECTOR_LVDS, | |
| 263 | + DRM_MODE_CONNECTOR_9PinDIN, | |
| 264 | + DRM_MODE_CONNECTOR_Unknown, | |
| 265 | + DRM_MODE_CONNECTOR_Unknown, | |
| 266 | + DRM_MODE_CONNECTOR_Unknown, | |
| 267 | + DRM_MODE_CONNECTOR_DisplayPort, | |
| 268 | + DRM_MODE_CONNECTOR_eDP, | |
| 269 | + DRM_MODE_CONNECTOR_Unknown | |
| 270 | +}; | |
| 271 | + | |
| 272 | +bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev) | |
| 273 | +{ | |
| 274 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 275 | + struct atom_context *ctx = mode_info->atom_context; | |
| 276 | + int index = GetIndexIntoMasterTable(DATA, Object_Header); | |
| 277 | + u16 size, data_offset; | |
| 278 | + u8 frev, crev; | |
| 279 | + ATOM_CONNECTOR_OBJECT_TABLE *con_obj; | |
| 280 | + ATOM_ENCODER_OBJECT_TABLE *enc_obj; | |
| 281 | + ATOM_OBJECT_TABLE *router_obj; | |
| 282 | + ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; | |
| 283 | + ATOM_OBJECT_HEADER *obj_header; | |
| 284 | + int i, j, k, path_size, device_support; | |
| 285 | + int connector_type; | |
| 286 | + u16 conn_id, connector_object_id; | |
| 287 | + struct amdgpu_i2c_bus_rec ddc_bus; | |
| 288 | + struct amdgpu_router router; | |
| 289 | + struct amdgpu_gpio_rec gpio; | |
| 290 | + struct amdgpu_hpd hpd; | |
| 291 | + | |
| 292 | + if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) | |
| 293 | + return false; | |
| 294 | + | |
| 295 | + if (crev < 2) | |
| 296 | + return false; | |
| 297 | + | |
| 298 | + obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); | |
| 299 | + path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) | |
| 300 | + (ctx->bios + data_offset + | |
| 301 | + le16_to_cpu(obj_header->usDisplayPathTableOffset)); | |
| 302 | + con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) | |
| 303 | + (ctx->bios + data_offset + | |
| 304 | + le16_to_cpu(obj_header->usConnectorObjectTableOffset)); | |
| 305 | + enc_obj = (ATOM_ENCODER_OBJECT_TABLE *) | |
| 306 | + (ctx->bios + data_offset + | |
| 307 | + le16_to_cpu(obj_header->usEncoderObjectTableOffset)); | |
| 308 | + router_obj = (ATOM_OBJECT_TABLE *) | |
| 309 | + (ctx->bios + data_offset + | |
| 310 | + le16_to_cpu(obj_header->usRouterObjectTableOffset)); | |
| 311 | + device_support = le16_to_cpu(obj_header->usDeviceSupport); | |
| 312 | + | |
| 313 | + path_size = 0; | |
| 314 | + for (i = 0; i < path_obj->ucNumOfDispPath; i++) { | |
| 315 | + uint8_t *addr = (uint8_t *) path_obj->asDispPath; | |
| 316 | + ATOM_DISPLAY_OBJECT_PATH *path; | |
| 317 | + addr += path_size; | |
| 318 | + path = (ATOM_DISPLAY_OBJECT_PATH *) addr; | |
| 319 | + path_size += le16_to_cpu(path->usSize); | |
| 320 | + | |
| 321 | + if (device_support & le16_to_cpu(path->usDeviceTag)) { | |
| 322 | + uint8_t con_obj_id, con_obj_num, con_obj_type; | |
| 323 | + | |
| 324 | + con_obj_id = | |
| 325 | + (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK) | |
| 326 | + >> OBJECT_ID_SHIFT; | |
| 327 | + con_obj_num = | |
| 328 | + (le16_to_cpu(path->usConnObjectId) & ENUM_ID_MASK) | |
| 329 | + >> ENUM_ID_SHIFT; | |
| 330 | + con_obj_type = | |
| 331 | + (le16_to_cpu(path->usConnObjectId) & | |
| 332 | + OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; | |
| 333 | + | |
| 334 | + connector_type = | |
| 335 | + object_connector_convert[con_obj_id]; | |
| 336 | + connector_object_id = con_obj_id; | |
| 337 | + | |
| 338 | + if (connector_type == DRM_MODE_CONNECTOR_Unknown) | |
| 339 | + continue; | |
| 340 | + | |
| 341 | + router.ddc_valid = false; | |
| 342 | + router.cd_valid = false; | |
| 343 | + for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { | |
| 344 | + uint8_t grph_obj_id, grph_obj_num, grph_obj_type; | |
| 345 | + | |
| 346 | + grph_obj_id = | |
| 347 | + (le16_to_cpu(path->usGraphicObjIds[j]) & | |
| 348 | + OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; | |
| 349 | + grph_obj_num = | |
| 350 | + (le16_to_cpu(path->usGraphicObjIds[j]) & | |
| 351 | + ENUM_ID_MASK) >> ENUM_ID_SHIFT; | |
| 352 | + grph_obj_type = | |
| 353 | + (le16_to_cpu(path->usGraphicObjIds[j]) & | |
| 354 | + OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; | |
| 355 | + | |
| 356 | + if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { | |
| 357 | + for (k = 0; k < enc_obj->ucNumberOfObjects; k++) { | |
| 358 | + u16 encoder_obj = le16_to_cpu(enc_obj->asObjects[k].usObjectID); | |
| 359 | + if (le16_to_cpu(path->usGraphicObjIds[j]) == encoder_obj) { | |
| 360 | + ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) | |
| 361 | + (ctx->bios + data_offset + | |
| 362 | + le16_to_cpu(enc_obj->asObjects[k].usRecordOffset)); | |
| 363 | + ATOM_ENCODER_CAP_RECORD *cap_record; | |
| 364 | + u16 caps = 0; | |
| 365 | + | |
| 366 | + while (record->ucRecordSize > 0 && | |
| 367 | + record->ucRecordType > 0 && | |
| 368 | + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { | |
| 369 | + switch (record->ucRecordType) { | |
| 370 | + case ATOM_ENCODER_CAP_RECORD_TYPE: | |
| 371 | + cap_record =(ATOM_ENCODER_CAP_RECORD *) | |
| 372 | + record; | |
| 373 | + caps = le16_to_cpu(cap_record->usEncoderCap); | |
| 374 | + break; | |
| 375 | + } | |
| 376 | + record = (ATOM_COMMON_RECORD_HEADER *) | |
| 377 | + ((char *)record + record->ucRecordSize); | |
| 378 | + } | |
| 379 | + amdgpu_display_add_encoder(adev, encoder_obj, | |
| 380 | + le16_to_cpu(path->usDeviceTag), | |
| 381 | + caps); | |
| 382 | + } | |
| 383 | + } | |
| 384 | + } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { | |
| 385 | + for (k = 0; k < router_obj->ucNumberOfObjects; k++) { | |
| 386 | + u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID); | |
| 387 | + if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { | |
| 388 | + ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) | |
| 389 | + (ctx->bios + data_offset + | |
| 390 | + le16_to_cpu(router_obj->asObjects[k].usRecordOffset)); | |
| 391 | + ATOM_I2C_RECORD *i2c_record; | |
| 392 | + ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; | |
| 393 | + ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; | |
| 394 | + ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path; | |
| 395 | + ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = | |
| 396 | + (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) | |
| 397 | + (ctx->bios + data_offset + | |
| 398 | + le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); | |
| 399 | + u8 *num_dst_objs = (u8 *) | |
| 400 | + ((u8 *)router_src_dst_table + 1 + | |
| 401 | + (router_src_dst_table->ucNumberOfSrc * 2)); | |
| 402 | + u16 *dst_objs = (u16 *)(num_dst_objs + 1); | |
| 403 | + int enum_id; | |
| 404 | + | |
| 405 | + router.router_id = router_obj_id; | |
| 406 | + for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) { | |
| 407 | + if (le16_to_cpu(path->usConnObjectId) == | |
| 408 | + le16_to_cpu(dst_objs[enum_id])) | |
| 409 | + break; | |
| 410 | + } | |
| 411 | + | |
| 412 | + while (record->ucRecordSize > 0 && | |
| 413 | + record->ucRecordType > 0 && | |
| 414 | + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { | |
| 415 | + switch (record->ucRecordType) { | |
| 416 | + case ATOM_I2C_RECORD_TYPE: | |
| 417 | + i2c_record = | |
| 418 | + (ATOM_I2C_RECORD *) | |
| 419 | + record; | |
| 420 | + i2c_config = | |
| 421 | + (ATOM_I2C_ID_CONFIG_ACCESS *) | |
| 422 | + &i2c_record->sucI2cId; | |
| 423 | + router.i2c_info = | |
| 424 | + amdgpu_atombios_lookup_i2c_gpio(adev, | |
| 425 | + i2c_config-> | |
| 426 | + ucAccess); | |
| 427 | + router.i2c_addr = i2c_record->ucI2CAddr >> 1; | |
| 428 | + break; | |
| 429 | + case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: | |
| 430 | + ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) | |
| 431 | + record; | |
| 432 | + router.ddc_valid = true; | |
| 433 | + router.ddc_mux_type = ddc_path->ucMuxType; | |
| 434 | + router.ddc_mux_control_pin = ddc_path->ucMuxControlPin; | |
| 435 | + router.ddc_mux_state = ddc_path->ucMuxState[enum_id]; | |
| 436 | + break; | |
| 437 | + case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: | |
| 438 | + cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *) | |
| 439 | + record; | |
| 440 | + router.cd_valid = true; | |
| 441 | + router.cd_mux_type = cd_path->ucMuxType; | |
| 442 | + router.cd_mux_control_pin = cd_path->ucMuxControlPin; | |
| 443 | + router.cd_mux_state = cd_path->ucMuxState[enum_id]; | |
| 444 | + break; | |
| 445 | + } | |
| 446 | + record = (ATOM_COMMON_RECORD_HEADER *) | |
| 447 | + ((char *)record + record->ucRecordSize); | |
| 448 | + } | |
| 449 | + } | |
| 450 | + } | |
| 451 | + } | |
| 452 | + } | |
| 453 | + | |
| 454 | + /* look up gpio for ddc, hpd */ | |
| 455 | + ddc_bus.valid = false; | |
| 456 | + hpd.hpd = AMDGPU_HPD_NONE; | |
| 457 | + if ((le16_to_cpu(path->usDeviceTag) & | |
| 458 | + (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) == 0) { | |
| 459 | + for (j = 0; j < con_obj->ucNumberOfObjects; j++) { | |
| 460 | + if (le16_to_cpu(path->usConnObjectId) == | |
| 461 | + le16_to_cpu(con_obj->asObjects[j]. | |
| 462 | + usObjectID)) { | |
| 463 | + ATOM_COMMON_RECORD_HEADER | |
| 464 | + *record = | |
| 465 | + (ATOM_COMMON_RECORD_HEADER | |
| 466 | + *) | |
| 467 | + (ctx->bios + data_offset + | |
| 468 | + le16_to_cpu(con_obj-> | |
| 469 | + asObjects[j]. | |
| 470 | + usRecordOffset)); | |
| 471 | + ATOM_I2C_RECORD *i2c_record; | |
| 472 | + ATOM_HPD_INT_RECORD *hpd_record; | |
| 473 | + ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; | |
| 474 | + | |
| 475 | + while (record->ucRecordSize > 0 && | |
| 476 | + record->ucRecordType > 0 && | |
| 477 | + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { | |
| 478 | + switch (record->ucRecordType) { | |
| 479 | + case ATOM_I2C_RECORD_TYPE: | |
| 480 | + i2c_record = | |
| 481 | + (ATOM_I2C_RECORD *) | |
| 482 | + record; | |
| 483 | + i2c_config = | |
| 484 | + (ATOM_I2C_ID_CONFIG_ACCESS *) | |
| 485 | + &i2c_record->sucI2cId; | |
| 486 | + ddc_bus = amdgpu_atombios_lookup_i2c_gpio(adev, | |
| 487 | + i2c_config-> | |
| 488 | + ucAccess); | |
| 489 | + break; | |
| 490 | + case ATOM_HPD_INT_RECORD_TYPE: | |
| 491 | + hpd_record = | |
| 492 | + (ATOM_HPD_INT_RECORD *) | |
| 493 | + record; | |
| 494 | + gpio = amdgpu_atombios_lookup_gpio(adev, | |
| 495 | + hpd_record->ucHPDIntGPIOID); | |
| 496 | + hpd = amdgpu_atombios_get_hpd_info_from_gpio(adev, &gpio); | |
| 497 | + hpd.plugged_state = hpd_record->ucPlugged_PinState; | |
| 498 | + break; | |
| 499 | + } | |
| 500 | + record = | |
| 501 | + (ATOM_COMMON_RECORD_HEADER | |
| 502 | + *) ((char *)record | |
| 503 | + + | |
| 504 | + record-> | |
| 505 | + ucRecordSize); | |
| 506 | + } | |
| 507 | + break; | |
| 508 | + } | |
| 509 | + } | |
| 510 | + } | |
| 511 | + | |
| 512 | + /* needed for aux chan transactions */ | |
| 513 | + ddc_bus.hpd = hpd.hpd; | |
| 514 | + | |
| 515 | + conn_id = le16_to_cpu(path->usConnObjectId); | |
| 516 | + | |
| 517 | + if (!amdgpu_atombios_apply_quirks | |
| 518 | + (adev, le16_to_cpu(path->usDeviceTag), &connector_type, | |
| 519 | + &ddc_bus, &conn_id, &hpd)) | |
| 520 | + continue; | |
| 521 | + | |
| 522 | + amdgpu_display_add_connector(adev, | |
| 523 | + conn_id, | |
| 524 | + le16_to_cpu(path->usDeviceTag), | |
| 525 | + connector_type, &ddc_bus, | |
| 526 | + connector_object_id, | |
| 527 | + &hpd, | |
| 528 | + &router); | |
| 529 | + | |
| 530 | + } | |
| 531 | + } | |
| 532 | + | |
| 533 | + amdgpu_link_encoder_connector(adev->ddev); | |
| 534 | + | |
| 535 | + return true; | |
| 536 | +} | |
| 537 | + | |
| 538 | +union firmware_info { | |
| 539 | + ATOM_FIRMWARE_INFO info; | |
| 540 | + ATOM_FIRMWARE_INFO_V1_2 info_12; | |
| 541 | + ATOM_FIRMWARE_INFO_V1_3 info_13; | |
| 542 | + ATOM_FIRMWARE_INFO_V1_4 info_14; | |
| 543 | + ATOM_FIRMWARE_INFO_V2_1 info_21; | |
| 544 | + ATOM_FIRMWARE_INFO_V2_2 info_22; | |
| 545 | +}; | |
| 546 | + | |
| 547 | +int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev) | |
| 548 | +{ | |
| 549 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 550 | + int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); | |
| 551 | + uint8_t frev, crev; | |
| 552 | + uint16_t data_offset; | |
| 553 | + int ret = -EINVAL; | |
| 554 | + | |
| 555 | + if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, | |
| 556 | + &frev, &crev, &data_offset)) { | |
| 557 | + int i; | |
| 558 | + struct amdgpu_pll *ppll = &adev->clock.ppll[0]; | |
| 559 | + struct amdgpu_pll *spll = &adev->clock.spll; | |
| 560 | + struct amdgpu_pll *mpll = &adev->clock.mpll; | |
| 561 | + union firmware_info *firmware_info = | |
| 562 | + (union firmware_info *)(mode_info->atom_context->bios + | |
| 563 | + data_offset); | |
| 564 | + /* pixel clocks */ | |
| 565 | + ppll->reference_freq = | |
| 566 | + le16_to_cpu(firmware_info->info.usReferenceClock); | |
| 567 | + ppll->reference_div = 0; | |
| 568 | + | |
| 569 | + if (crev < 2) | |
| 570 | + ppll->pll_out_min = | |
| 571 | + le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); | |
| 572 | + else | |
| 573 | + ppll->pll_out_min = | |
| 574 | + le32_to_cpu(firmware_info->info_12.ulMinPixelClockPLL_Output); | |
| 575 | + ppll->pll_out_max = | |
| 576 | + le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output); | |
| 577 | + | |
| 578 | + if (crev >= 4) { | |
| 579 | + ppll->lcd_pll_out_min = | |
| 580 | + le16_to_cpu(firmware_info->info_14.usLcdMinPixelClockPLL_Output) * 100; | |
| 581 | + if (ppll->lcd_pll_out_min == 0) | |
| 582 | + ppll->lcd_pll_out_min = ppll->pll_out_min; | |
| 583 | + ppll->lcd_pll_out_max = | |
| 584 | + le16_to_cpu(firmware_info->info_14.usLcdMaxPixelClockPLL_Output) * 100; | |
| 585 | + if (ppll->lcd_pll_out_max == 0) | |
| 586 | + ppll->lcd_pll_out_max = ppll->pll_out_max; | |
| 587 | + } else { | |
| 588 | + ppll->lcd_pll_out_min = ppll->pll_out_min; | |
| 589 | + ppll->lcd_pll_out_max = ppll->pll_out_max; | |
| 590 | + } | |
| 591 | + | |
| 592 | + if (ppll->pll_out_min == 0) | |
| 593 | + ppll->pll_out_min = 64800; | |
| 594 | + | |
| 595 | + ppll->pll_in_min = | |
| 596 | + le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Input); | |
| 597 | + ppll->pll_in_max = | |
| 598 | + le16_to_cpu(firmware_info->info.usMaxPixelClockPLL_Input); | |
| 599 | + | |
| 600 | + ppll->min_post_div = 2; | |
| 601 | + ppll->max_post_div = 0x7f; | |
| 602 | + ppll->min_frac_feedback_div = 0; | |
| 603 | + ppll->max_frac_feedback_div = 9; | |
| 604 | + ppll->min_ref_div = 2; | |
| 605 | + ppll->max_ref_div = 0x3ff; | |
| 606 | + ppll->min_feedback_div = 4; | |
| 607 | + ppll->max_feedback_div = 0xfff; | |
| 608 | + ppll->best_vco = 0; | |
| 609 | + | |
| 610 | + for (i = 1; i < AMDGPU_MAX_PPLL; i++) | |
| 611 | + adev->clock.ppll[i] = *ppll; | |
| 612 | + | |
| 613 | + /* system clock */ | |
| 614 | + spll->reference_freq = | |
| 615 | + le16_to_cpu(firmware_info->info_21.usCoreReferenceClock); | |
| 616 | + spll->reference_div = 0; | |
| 617 | + | |
| 618 | + spll->pll_out_min = | |
| 619 | + le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Output); | |
| 620 | + spll->pll_out_max = | |
| 621 | + le32_to_cpu(firmware_info->info.ulMaxEngineClockPLL_Output); | |
| 622 | + | |
| 623 | + /* ??? */ | |
| 624 | + if (spll->pll_out_min == 0) | |
| 625 | + spll->pll_out_min = 64800; | |
| 626 | + | |
| 627 | + spll->pll_in_min = | |
| 628 | + le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Input); | |
| 629 | + spll->pll_in_max = | |
| 630 | + le16_to_cpu(firmware_info->info.usMaxEngineClockPLL_Input); | |
| 631 | + | |
| 632 | + spll->min_post_div = 1; | |
| 633 | + spll->max_post_div = 1; | |
| 634 | + spll->min_ref_div = 2; | |
| 635 | + spll->max_ref_div = 0xff; | |
| 636 | + spll->min_feedback_div = 4; | |
| 637 | + spll->max_feedback_div = 0xff; | |
| 638 | + spll->best_vco = 0; | |
| 639 | + | |
| 640 | + /* memory clock */ | |
| 641 | + mpll->reference_freq = | |
| 642 | + le16_to_cpu(firmware_info->info_21.usMemoryReferenceClock); | |
| 643 | + mpll->reference_div = 0; | |
| 644 | + | |
| 645 | + mpll->pll_out_min = | |
| 646 | + le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Output); | |
| 647 | + mpll->pll_out_max = | |
| 648 | + le32_to_cpu(firmware_info->info.ulMaxMemoryClockPLL_Output); | |
| 649 | + | |
| 650 | + /* ??? */ | |
| 651 | + if (mpll->pll_out_min == 0) | |
| 652 | + mpll->pll_out_min = 64800; | |
| 653 | + | |
| 654 | + mpll->pll_in_min = | |
| 655 | + le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Input); | |
| 656 | + mpll->pll_in_max = | |
| 657 | + le16_to_cpu(firmware_info->info.usMaxMemoryClockPLL_Input); | |
| 658 | + | |
| 659 | + adev->clock.default_sclk = | |
| 660 | + le32_to_cpu(firmware_info->info.ulDefaultEngineClock); | |
| 661 | + adev->clock.default_mclk = | |
| 662 | + le32_to_cpu(firmware_info->info.ulDefaultMemoryClock); | |
| 663 | + | |
| 664 | + mpll->min_post_div = 1; | |
| 665 | + mpll->max_post_div = 1; | |
| 666 | + mpll->min_ref_div = 2; | |
| 667 | + mpll->max_ref_div = 0xff; | |
| 668 | + mpll->min_feedback_div = 4; | |
| 669 | + mpll->max_feedback_div = 0xff; | |
| 670 | + mpll->best_vco = 0; | |
| 671 | + | |
| 672 | + /* disp clock */ | |
| 673 | + adev->clock.default_dispclk = | |
| 674 | + le32_to_cpu(firmware_info->info_21.ulDefaultDispEngineClkFreq); | |
| 675 | + if (adev->clock.default_dispclk == 0) | |
| 676 | + adev->clock.default_dispclk = 54000; /* 540 Mhz */ | |
| 677 | + adev->clock.dp_extclk = | |
| 678 | + le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq); | |
| 679 | + adev->clock.current_dispclk = adev->clock.default_dispclk; | |
| 680 | + | |
| 681 | + adev->clock.max_pixel_clock = le16_to_cpu(firmware_info->info.usMaxPixelClock); | |
| 682 | + if (adev->clock.max_pixel_clock == 0) | |
| 683 | + adev->clock.max_pixel_clock = 40000; | |
| 684 | + | |
| 685 | + /* not technically a clock, but... */ | |
| 686 | + adev->mode_info.firmware_flags = | |
| 687 | + le16_to_cpu(firmware_info->info.usFirmwareCapability.susAccess); | |
| 688 | + | |
| 689 | + ret = 0; | |
| 690 | + } | |
| 691 | + | |
| 692 | + adev->pm.current_sclk = adev->clock.default_sclk; | |
| 693 | + adev->pm.current_mclk = adev->clock.default_mclk; | |
| 694 | + | |
| 695 | + return ret; | |
| 696 | +} | |
| 697 | + | |
| 698 | +union igp_info { | |
| 699 | + struct _ATOM_INTEGRATED_SYSTEM_INFO info; | |
| 700 | + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; | |
| 701 | + struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6; | |
| 702 | + struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; | |
| 703 | + struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8; | |
| 704 | + struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_9 info_9; | |
| 705 | +}; | |
| 706 | + | |
| 707 | +static void amdgpu_atombios_get_igp_ss_overrides(struct amdgpu_device *adev, | |
| 708 | + struct amdgpu_atom_ss *ss, | |
| 709 | + int id) | |
| 710 | +{ | |
| 711 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 712 | + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); | |
| 713 | + u16 data_offset, size; | |
| 714 | + union igp_info *igp_info; | |
| 715 | + u8 frev, crev; | |
| 716 | + u16 percentage = 0, rate = 0; | |
| 717 | + | |
| 718 | + /* get any igp specific overrides */ | |
| 719 | + if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, &size, | |
| 720 | + &frev, &crev, &data_offset)) { | |
| 721 | + igp_info = (union igp_info *) | |
| 722 | + (mode_info->atom_context->bios + data_offset); | |
| 723 | + switch (crev) { | |
| 724 | + case 6: | |
| 725 | + switch (id) { | |
| 726 | + case ASIC_INTERNAL_SS_ON_TMDS: | |
| 727 | + percentage = le16_to_cpu(igp_info->info_6.usDVISSPercentage); | |
| 728 | + rate = le16_to_cpu(igp_info->info_6.usDVISSpreadRateIn10Hz); | |
| 729 | + break; | |
| 730 | + case ASIC_INTERNAL_SS_ON_HDMI: | |
| 731 | + percentage = le16_to_cpu(igp_info->info_6.usHDMISSPercentage); | |
| 732 | + rate = le16_to_cpu(igp_info->info_6.usHDMISSpreadRateIn10Hz); | |
| 733 | + break; | |
| 734 | + case ASIC_INTERNAL_SS_ON_LVDS: | |
| 735 | + percentage = le16_to_cpu(igp_info->info_6.usLvdsSSPercentage); | |
| 736 | + rate = le16_to_cpu(igp_info->info_6.usLvdsSSpreadRateIn10Hz); | |
| 737 | + break; | |
| 738 | + } | |
| 739 | + break; | |
| 740 | + case 7: | |
| 741 | + switch (id) { | |
| 742 | + case ASIC_INTERNAL_SS_ON_TMDS: | |
| 743 | + percentage = le16_to_cpu(igp_info->info_7.usDVISSPercentage); | |
| 744 | + rate = le16_to_cpu(igp_info->info_7.usDVISSpreadRateIn10Hz); | |
| 745 | + break; | |
| 746 | + case ASIC_INTERNAL_SS_ON_HDMI: | |
| 747 | + percentage = le16_to_cpu(igp_info->info_7.usHDMISSPercentage); | |
| 748 | + rate = le16_to_cpu(igp_info->info_7.usHDMISSpreadRateIn10Hz); | |
| 749 | + break; | |
| 750 | + case ASIC_INTERNAL_SS_ON_LVDS: | |
| 751 | + percentage = le16_to_cpu(igp_info->info_7.usLvdsSSPercentage); | |
| 752 | + rate = le16_to_cpu(igp_info->info_7.usLvdsSSpreadRateIn10Hz); | |
| 753 | + break; | |
| 754 | + } | |
| 755 | + break; | |
| 756 | + case 8: | |
| 757 | + switch (id) { | |
| 758 | + case ASIC_INTERNAL_SS_ON_TMDS: | |
| 759 | + percentage = le16_to_cpu(igp_info->info_8.usDVISSPercentage); | |
| 760 | + rate = le16_to_cpu(igp_info->info_8.usDVISSpreadRateIn10Hz); | |
| 761 | + break; | |
| 762 | + case ASIC_INTERNAL_SS_ON_HDMI: | |
| 763 | + percentage = le16_to_cpu(igp_info->info_8.usHDMISSPercentage); | |
| 764 | + rate = le16_to_cpu(igp_info->info_8.usHDMISSpreadRateIn10Hz); | |
| 765 | + break; | |
| 766 | + case ASIC_INTERNAL_SS_ON_LVDS: | |
| 767 | + percentage = le16_to_cpu(igp_info->info_8.usLvdsSSPercentage); | |
| 768 | + rate = le16_to_cpu(igp_info->info_8.usLvdsSSpreadRateIn10Hz); | |
| 769 | + break; | |
| 770 | + } | |
| 771 | + break; | |
| 772 | + case 9: | |
| 773 | + switch (id) { | |
| 774 | + case ASIC_INTERNAL_SS_ON_TMDS: | |
| 775 | + percentage = le16_to_cpu(igp_info->info_9.usDVISSPercentage); | |
| 776 | + rate = le16_to_cpu(igp_info->info_9.usDVISSpreadRateIn10Hz); | |
| 777 | + break; | |
| 778 | + case ASIC_INTERNAL_SS_ON_HDMI: | |
| 779 | + percentage = le16_to_cpu(igp_info->info_9.usHDMISSPercentage); | |
| 780 | + rate = le16_to_cpu(igp_info->info_9.usHDMISSpreadRateIn10Hz); | |
| 781 | + break; | |
| 782 | + case ASIC_INTERNAL_SS_ON_LVDS: | |
| 783 | + percentage = le16_to_cpu(igp_info->info_9.usLvdsSSPercentage); | |
| 784 | + rate = le16_to_cpu(igp_info->info_9.usLvdsSSpreadRateIn10Hz); | |
| 785 | + break; | |
| 786 | + } | |
| 787 | + break; | |
| 788 | + default: | |
| 789 | + DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); | |
| 790 | + break; | |
| 791 | + } | |
| 792 | + if (percentage) | |
| 793 | + ss->percentage = percentage; | |
| 794 | + if (rate) | |
| 795 | + ss->rate = rate; | |
| 796 | + } | |
| 797 | +} | |
| 798 | + | |
| 799 | +union asic_ss_info { | |
| 800 | + struct _ATOM_ASIC_INTERNAL_SS_INFO info; | |
| 801 | + struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2; | |
| 802 | + struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3; | |
| 803 | +}; | |
| 804 | + | |
| 805 | +union asic_ss_assignment { | |
| 806 | + struct _ATOM_ASIC_SS_ASSIGNMENT v1; | |
| 807 | + struct _ATOM_ASIC_SS_ASSIGNMENT_V2 v2; | |
| 808 | + struct _ATOM_ASIC_SS_ASSIGNMENT_V3 v3; | |
| 809 | +}; | |
| 810 | + | |
| 811 | +bool amdgpu_atombios_get_asic_ss_info(struct amdgpu_device *adev, | |
| 812 | + struct amdgpu_atom_ss *ss, | |
| 813 | + int id, u32 clock) | |
| 814 | +{ | |
| 815 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 816 | + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); | |
| 817 | + uint16_t data_offset, size; | |
| 818 | + union asic_ss_info *ss_info; | |
| 819 | + union asic_ss_assignment *ss_assign; | |
| 820 | + uint8_t frev, crev; | |
| 821 | + int i, num_indices; | |
| 822 | + | |
| 823 | + if (id == ASIC_INTERNAL_MEMORY_SS) { | |
| 824 | + if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT)) | |
| 825 | + return false; | |
| 826 | + } | |
| 827 | + if (id == ASIC_INTERNAL_ENGINE_SS) { | |
| 828 | + if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT)) | |
| 829 | + return false; | |
| 830 | + } | |
| 831 | + | |
| 832 | + memset(ss, 0, sizeof(struct amdgpu_atom_ss)); | |
| 833 | + if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, &size, | |
| 834 | + &frev, &crev, &data_offset)) { | |
| 835 | + | |
| 836 | + ss_info = | |
| 837 | + (union asic_ss_info *)(mode_info->atom_context->bios + data_offset); | |
| 838 | + | |
| 839 | + switch (frev) { | |
| 840 | + case 1: | |
| 841 | + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / | |
| 842 | + sizeof(ATOM_ASIC_SS_ASSIGNMENT); | |
| 843 | + | |
| 844 | + ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info.asSpreadSpectrum[0]); | |
| 845 | + for (i = 0; i < num_indices; i++) { | |
| 846 | + if ((ss_assign->v1.ucClockIndication == id) && | |
| 847 | + (clock <= le32_to_cpu(ss_assign->v1.ulTargetClockRange))) { | |
| 848 | + ss->percentage = | |
| 849 | + le16_to_cpu(ss_assign->v1.usSpreadSpectrumPercentage); | |
| 850 | + ss->type = ss_assign->v1.ucSpreadSpectrumMode; | |
| 851 | + ss->rate = le16_to_cpu(ss_assign->v1.usSpreadRateInKhz); | |
| 852 | + ss->percentage_divider = 100; | |
| 853 | + return true; | |
| 854 | + } | |
| 855 | + ss_assign = (union asic_ss_assignment *) | |
| 856 | + ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT)); | |
| 857 | + } | |
| 858 | + break; | |
| 859 | + case 2: | |
| 860 | + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / | |
| 861 | + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); | |
| 862 | + ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info_2.asSpreadSpectrum[0]); | |
| 863 | + for (i = 0; i < num_indices; i++) { | |
| 864 | + if ((ss_assign->v2.ucClockIndication == id) && | |
| 865 | + (clock <= le32_to_cpu(ss_assign->v2.ulTargetClockRange))) { | |
| 866 | + ss->percentage = | |
| 867 | + le16_to_cpu(ss_assign->v2.usSpreadSpectrumPercentage); | |
| 868 | + ss->type = ss_assign->v2.ucSpreadSpectrumMode; | |
| 869 | + ss->rate = le16_to_cpu(ss_assign->v2.usSpreadRateIn10Hz); | |
| 870 | + ss->percentage_divider = 100; | |
| 871 | + if ((crev == 2) && | |
| 872 | + ((id == ASIC_INTERNAL_ENGINE_SS) || | |
| 873 | + (id == ASIC_INTERNAL_MEMORY_SS))) | |
| 874 | + ss->rate /= 100; | |
| 875 | + return true; | |
| 876 | + } | |
| 877 | + ss_assign = (union asic_ss_assignment *) | |
| 878 | + ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2)); | |
| 879 | + } | |
| 880 | + break; | |
| 881 | + case 3: | |
| 882 | + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / | |
| 883 | + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); | |
| 884 | + ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info_3.asSpreadSpectrum[0]); | |
| 885 | + for (i = 0; i < num_indices; i++) { | |
| 886 | + if ((ss_assign->v3.ucClockIndication == id) && | |
| 887 | + (clock <= le32_to_cpu(ss_assign->v3.ulTargetClockRange))) { | |
| 888 | + ss->percentage = | |
| 889 | + le16_to_cpu(ss_assign->v3.usSpreadSpectrumPercentage); | |
| 890 | + ss->type = ss_assign->v3.ucSpreadSpectrumMode; | |
| 891 | + ss->rate = le16_to_cpu(ss_assign->v3.usSpreadRateIn10Hz); | |
| 892 | + if (ss_assign->v3.ucSpreadSpectrumMode & | |
| 893 | + SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK) | |
| 894 | + ss->percentage_divider = 1000; | |
| 895 | + else | |
| 896 | + ss->percentage_divider = 100; | |
| 897 | + if ((id == ASIC_INTERNAL_ENGINE_SS) || | |
| 898 | + (id == ASIC_INTERNAL_MEMORY_SS)) | |
| 899 | + ss->rate /= 100; | |
| 900 | + if (adev->flags & AMDGPU_IS_APU) | |
| 901 | + amdgpu_atombios_get_igp_ss_overrides(adev, ss, id); | |
| 902 | + return true; | |
| 903 | + } | |
| 904 | + ss_assign = (union asic_ss_assignment *) | |
| 905 | + ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3)); | |
| 906 | + } | |
| 907 | + break; | |
| 908 | + default: | |
| 909 | + DRM_ERROR("Unsupported ASIC_InternalSS_Info table: %d %d\n", frev, crev); | |
| 910 | + break; | |
| 911 | + } | |
| 912 | + | |
| 913 | + } | |
| 914 | + return false; | |
| 915 | +} | |
| 916 | + | |
| 917 | +union get_clock_dividers { | |
| 918 | + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS v1; | |
| 919 | + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 v2; | |
| 920 | + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 v3; | |
| 921 | + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 v4; | |
| 922 | + struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 v5; | |
| 923 | + struct _COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6 v6_in; | |
| 924 | + struct _COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 v6_out; | |
| 925 | +}; | |
| 926 | + | |
| 927 | +int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev, | |
| 928 | + u8 clock_type, | |
| 929 | + u32 clock, | |
| 930 | + bool strobe_mode, | |
| 931 | + struct atom_clock_dividers *dividers) | |
| 932 | +{ | |
| 933 | + union get_clock_dividers args; | |
| 934 | + int index = GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL); | |
| 935 | + u8 frev, crev; | |
| 936 | + | |
| 937 | + memset(&args, 0, sizeof(args)); | |
| 938 | + memset(dividers, 0, sizeof(struct atom_clock_dividers)); | |
| 939 | + | |
| 940 | + if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) | |
| 941 | + return -EINVAL; | |
| 942 | + | |
| 943 | + switch (crev) { | |
| 944 | + case 4: | |
| 945 | + /* fusion */ | |
| 946 | + args.v4.ulClock = cpu_to_le32(clock); /* 10 khz */ | |
| 947 | + | |
| 948 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 949 | + | |
| 950 | + dividers->post_divider = dividers->post_div = args.v4.ucPostDiv; | |
| 951 | + dividers->real_clock = le32_to_cpu(args.v4.ulClock); | |
| 952 | + break; | |
| 953 | + case 6: | |
| 954 | + /* CI */ | |
| 955 | + /* COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, COMPUTE_GPUCLK_INPUT_FLAG_SCLK */ | |
| 956 | + args.v6_in.ulClock.ulComputeClockFlag = clock_type; | |
| 957 | + args.v6_in.ulClock.ulClockFreq = cpu_to_le32(clock); /* 10 khz */ | |
| 958 | + | |
| 959 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 960 | + | |
| 961 | + dividers->whole_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDiv); | |
| 962 | + dividers->frac_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDivFrac); | |
| 963 | + dividers->ref_div = args.v6_out.ucPllRefDiv; | |
| 964 | + dividers->post_div = args.v6_out.ucPllPostDiv; | |
| 965 | + dividers->flags = args.v6_out.ucPllCntlFlag; | |
| 966 | + dividers->real_clock = le32_to_cpu(args.v6_out.ulClock.ulClock); | |
| 967 | + dividers->post_divider = args.v6_out.ulClock.ucPostDiv; | |
| 968 | + break; | |
| 969 | + default: | |
| 970 | + return -EINVAL; | |
| 971 | + } | |
| 972 | + return 0; | |
| 973 | +} | |
| 974 | + | |
| 975 | +int amdgpu_atombios_get_memory_pll_dividers(struct amdgpu_device *adev, | |
| 976 | + u32 clock, | |
| 977 | + bool strobe_mode, | |
| 978 | + struct atom_mpll_param *mpll_param) | |
| 979 | +{ | |
| 980 | + COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1 args; | |
| 981 | + int index = GetIndexIntoMasterTable(COMMAND, ComputeMemoryClockParam); | |
| 982 | + u8 frev, crev; | |
| 983 | + | |
| 984 | + memset(&args, 0, sizeof(args)); | |
| 985 | + memset(mpll_param, 0, sizeof(struct atom_mpll_param)); | |
| 986 | + | |
| 987 | + if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) | |
| 988 | + return -EINVAL; | |
| 989 | + | |
| 990 | + switch (frev) { | |
| 991 | + case 2: | |
| 992 | + switch (crev) { | |
| 993 | + case 1: | |
| 994 | + /* SI */ | |
| 995 | + args.ulClock = cpu_to_le32(clock); /* 10 khz */ | |
| 996 | + args.ucInputFlag = 0; | |
| 997 | + if (strobe_mode) | |
| 998 | + args.ucInputFlag |= MPLL_INPUT_FLAG_STROBE_MODE_EN; | |
| 999 | + | |
| 1000 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1001 | + | |
| 1002 | + mpll_param->clkfrac = le16_to_cpu(args.ulFbDiv.usFbDivFrac); | |
| 1003 | + mpll_param->clkf = le16_to_cpu(args.ulFbDiv.usFbDiv); | |
| 1004 | + mpll_param->post_div = args.ucPostDiv; | |
| 1005 | + mpll_param->dll_speed = args.ucDllSpeed; | |
| 1006 | + mpll_param->bwcntl = args.ucBWCntl; | |
| 1007 | + mpll_param->vco_mode = | |
| 1008 | + (args.ucPllCntlFlag & MPLL_CNTL_FLAG_VCO_MODE_MASK); | |
| 1009 | + mpll_param->yclk_sel = | |
| 1010 | + (args.ucPllCntlFlag & MPLL_CNTL_FLAG_BYPASS_DQ_PLL) ? 1 : 0; | |
| 1011 | + mpll_param->qdr = | |
| 1012 | + (args.ucPllCntlFlag & MPLL_CNTL_FLAG_QDR_ENABLE) ? 1 : 0; | |
| 1013 | + mpll_param->half_rate = | |
| 1014 | + (args.ucPllCntlFlag & MPLL_CNTL_FLAG_AD_HALF_RATE) ? 1 : 0; | |
| 1015 | + break; | |
| 1016 | + default: | |
| 1017 | + return -EINVAL; | |
| 1018 | + } | |
| 1019 | + break; | |
| 1020 | + default: | |
| 1021 | + return -EINVAL; | |
| 1022 | + } | |
| 1023 | + return 0; | |
| 1024 | +} | |
| 1025 | + | |
| 1026 | +uint32_t amdgpu_atombios_get_engine_clock(struct amdgpu_device *adev) | |
| 1027 | +{ | |
| 1028 | + GET_ENGINE_CLOCK_PS_ALLOCATION args; | |
| 1029 | + int index = GetIndexIntoMasterTable(COMMAND, GetEngineClock); | |
| 1030 | + | |
| 1031 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1032 | + return le32_to_cpu(args.ulReturnEngineClock); | |
| 1033 | +} | |
| 1034 | + | |
| 1035 | +uint32_t amdgpu_atombios_get_memory_clock(struct amdgpu_device *adev) | |
| 1036 | +{ | |
| 1037 | + GET_MEMORY_CLOCK_PS_ALLOCATION args; | |
| 1038 | + int index = GetIndexIntoMasterTable(COMMAND, GetMemoryClock); | |
| 1039 | + | |
| 1040 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1041 | + return le32_to_cpu(args.ulReturnMemoryClock); | |
| 1042 | +} | |
| 1043 | + | |
| 1044 | +void amdgpu_atombios_set_engine_clock(struct amdgpu_device *adev, | |
| 1045 | + uint32_t eng_clock) | |
| 1046 | +{ | |
| 1047 | + SET_ENGINE_CLOCK_PS_ALLOCATION args; | |
| 1048 | + int index = GetIndexIntoMasterTable(COMMAND, SetEngineClock); | |
| 1049 | + | |
| 1050 | + args.ulTargetEngineClock = cpu_to_le32(eng_clock); /* 10 khz */ | |
| 1051 | + | |
| 1052 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1053 | +} | |
| 1054 | + | |
| 1055 | +void amdgpu_atombios_set_memory_clock(struct amdgpu_device *adev, | |
| 1056 | + uint32_t mem_clock) | |
| 1057 | +{ | |
| 1058 | + SET_MEMORY_CLOCK_PS_ALLOCATION args; | |
| 1059 | + int index = GetIndexIntoMasterTable(COMMAND, SetMemoryClock); | |
| 1060 | + | |
| 1061 | + if (adev->flags & AMDGPU_IS_APU) | |
| 1062 | + return; | |
| 1063 | + | |
| 1064 | + args.ulTargetMemoryClock = cpu_to_le32(mem_clock); /* 10 khz */ | |
| 1065 | + | |
| 1066 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1067 | +} | |
| 1068 | + | |
| 1069 | +void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev, | |
| 1070 | + u32 eng_clock, u32 mem_clock) | |
| 1071 | +{ | |
| 1072 | + SET_ENGINE_CLOCK_PS_ALLOCATION args; | |
| 1073 | + int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings); | |
| 1074 | + u32 tmp; | |
| 1075 | + | |
| 1076 | + memset(&args, 0, sizeof(args)); | |
| 1077 | + | |
| 1078 | + tmp = eng_clock & SET_CLOCK_FREQ_MASK; | |
| 1079 | + tmp |= (COMPUTE_ENGINE_PLL_PARAM << 24); | |
| 1080 | + | |
| 1081 | + args.ulTargetEngineClock = cpu_to_le32(tmp); | |
| 1082 | + if (mem_clock) | |
| 1083 | + args.sReserved.ulClock = cpu_to_le32(mem_clock & SET_CLOCK_FREQ_MASK); | |
| 1084 | + | |
| 1085 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1086 | +} | |
| 1087 | + | |
| 1088 | +union set_voltage { | |
| 1089 | + struct _SET_VOLTAGE_PS_ALLOCATION alloc; | |
| 1090 | + struct _SET_VOLTAGE_PARAMETERS v1; | |
| 1091 | + struct _SET_VOLTAGE_PARAMETERS_V2 v2; | |
| 1092 | + struct _SET_VOLTAGE_PARAMETERS_V1_3 v3; | |
| 1093 | +}; | |
| 1094 | + | |
| 1095 | +void amdgpu_atombios_set_voltage(struct amdgpu_device *adev, | |
| 1096 | + u16 voltage_level, | |
| 1097 | + u8 voltage_type) | |
| 1098 | +{ | |
| 1099 | + union set_voltage args; | |
| 1100 | + int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); | |
| 1101 | + u8 frev, crev, volt_index = voltage_level; | |
| 1102 | + | |
| 1103 | + if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) | |
| 1104 | + return; | |
| 1105 | + | |
| 1106 | + /* 0xff01 is a flag rather then an actual voltage */ | |
| 1107 | + if (voltage_level == 0xff01) | |
| 1108 | + return; | |
| 1109 | + | |
| 1110 | + switch (crev) { | |
| 1111 | + case 1: | |
| 1112 | + args.v1.ucVoltageType = voltage_type; | |
| 1113 | + args.v1.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_ALL_SOURCE; | |
| 1114 | + args.v1.ucVoltageIndex = volt_index; | |
| 1115 | + break; | |
| 1116 | + case 2: | |
| 1117 | + args.v2.ucVoltageType = voltage_type; | |
| 1118 | + args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE; | |
| 1119 | + args.v2.usVoltageLevel = cpu_to_le16(voltage_level); | |
| 1120 | + break; | |
| 1121 | + case 3: | |
| 1122 | + args.v3.ucVoltageType = voltage_type; | |
| 1123 | + args.v3.ucVoltageMode = ATOM_SET_VOLTAGE; | |
| 1124 | + args.v3.usVoltageLevel = cpu_to_le16(voltage_level); | |
| 1125 | + break; | |
| 1126 | + default: | |
| 1127 | + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); | |
| 1128 | + return; | |
| 1129 | + } | |
| 1130 | + | |
| 1131 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1132 | +} | |
| 1133 | + | |
| 1134 | +int amdgpu_atombios_get_leakage_id_from_vbios(struct amdgpu_device *adev, | |
| 1135 | + u16 *leakage_id) | |
| 1136 | +{ | |
| 1137 | + union set_voltage args; | |
| 1138 | + int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); | |
| 1139 | + u8 frev, crev; | |
| 1140 | + | |
| 1141 | + if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) | |
| 1142 | + return -EINVAL; | |
| 1143 | + | |
| 1144 | + switch (crev) { | |
| 1145 | + case 3: | |
| 1146 | + case 4: | |
| 1147 | + args.v3.ucVoltageType = 0; | |
| 1148 | + args.v3.ucVoltageMode = ATOM_GET_LEAKAGE_ID; | |
| 1149 | + args.v3.usVoltageLevel = 0; | |
| 1150 | + | |
| 1151 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1152 | + | |
| 1153 | + *leakage_id = le16_to_cpu(args.v3.usVoltageLevel); | |
| 1154 | + break; | |
| 1155 | + default: | |
| 1156 | + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); | |
| 1157 | + return -EINVAL; | |
| 1158 | + } | |
| 1159 | + | |
| 1160 | + return 0; | |
| 1161 | +} | |
| 1162 | + | |
| 1163 | +int amdgpu_atombios_get_leakage_vddc_based_on_leakage_params(struct amdgpu_device *adev, | |
| 1164 | + u16 *vddc, u16 *vddci, | |
| 1165 | + u16 virtual_voltage_id, | |
| 1166 | + u16 vbios_voltage_id) | |
| 1167 | +{ | |
| 1168 | + int index = GetIndexIntoMasterTable(DATA, ASIC_ProfilingInfo); | |
| 1169 | + u8 frev, crev; | |
| 1170 | + u16 data_offset, size; | |
| 1171 | + int i, j; | |
| 1172 | + ATOM_ASIC_PROFILING_INFO_V2_1 *profile; | |
| 1173 | + u16 *leakage_bin, *vddc_id_buf, *vddc_buf, *vddci_id_buf, *vddci_buf; | |
| 1174 | + | |
| 1175 | + *vddc = 0; | |
| 1176 | + *vddci = 0; | |
| 1177 | + | |
| 1178 | + if (!amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, | |
| 1179 | + &frev, &crev, &data_offset)) | |
| 1180 | + return -EINVAL; | |
| 1181 | + | |
| 1182 | + profile = (ATOM_ASIC_PROFILING_INFO_V2_1 *) | |
| 1183 | + (adev->mode_info.atom_context->bios + data_offset); | |
| 1184 | + | |
| 1185 | + switch (frev) { | |
| 1186 | + case 1: | |
| 1187 | + return -EINVAL; | |
| 1188 | + case 2: | |
| 1189 | + switch (crev) { | |
| 1190 | + case 1: | |
| 1191 | + if (size < sizeof(ATOM_ASIC_PROFILING_INFO_V2_1)) | |
| 1192 | + return -EINVAL; | |
| 1193 | + leakage_bin = (u16 *) | |
| 1194 | + (adev->mode_info.atom_context->bios + data_offset + | |
| 1195 | + le16_to_cpu(profile->usLeakageBinArrayOffset)); | |
| 1196 | + vddc_id_buf = (u16 *) | |
| 1197 | + (adev->mode_info.atom_context->bios + data_offset + | |
| 1198 | + le16_to_cpu(profile->usElbVDDC_IdArrayOffset)); | |
| 1199 | + vddc_buf = (u16 *) | |
| 1200 | + (adev->mode_info.atom_context->bios + data_offset + | |
| 1201 | + le16_to_cpu(profile->usElbVDDC_LevelArrayOffset)); | |
| 1202 | + vddci_id_buf = (u16 *) | |
| 1203 | + (adev->mode_info.atom_context->bios + data_offset + | |
| 1204 | + le16_to_cpu(profile->usElbVDDCI_IdArrayOffset)); | |
| 1205 | + vddci_buf = (u16 *) | |
| 1206 | + (adev->mode_info.atom_context->bios + data_offset + | |
| 1207 | + le16_to_cpu(profile->usElbVDDCI_LevelArrayOffset)); | |
| 1208 | + | |
| 1209 | + if (profile->ucElbVDDC_Num > 0) { | |
| 1210 | + for (i = 0; i < profile->ucElbVDDC_Num; i++) { | |
| 1211 | + if (vddc_id_buf[i] == virtual_voltage_id) { | |
| 1212 | + for (j = 0; j < profile->ucLeakageBinNum; j++) { | |
| 1213 | + if (vbios_voltage_id <= leakage_bin[j]) { | |
| 1214 | + *vddc = vddc_buf[j * profile->ucElbVDDC_Num + i]; | |
| 1215 | + break; | |
| 1216 | + } | |
| 1217 | + } | |
| 1218 | + break; | |
| 1219 | + } | |
| 1220 | + } | |
| 1221 | + } | |
| 1222 | + if (profile->ucElbVDDCI_Num > 0) { | |
| 1223 | + for (i = 0; i < profile->ucElbVDDCI_Num; i++) { | |
| 1224 | + if (vddci_id_buf[i] == virtual_voltage_id) { | |
| 1225 | + for (j = 0; j < profile->ucLeakageBinNum; j++) { | |
| 1226 | + if (vbios_voltage_id <= leakage_bin[j]) { | |
| 1227 | + *vddci = vddci_buf[j * profile->ucElbVDDCI_Num + i]; | |
| 1228 | + break; | |
| 1229 | + } | |
| 1230 | + } | |
| 1231 | + break; | |
| 1232 | + } | |
| 1233 | + } | |
| 1234 | + } | |
| 1235 | + break; | |
| 1236 | + default: | |
| 1237 | + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); | |
| 1238 | + return -EINVAL; | |
| 1239 | + } | |
| 1240 | + break; | |
| 1241 | + default: | |
| 1242 | + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); | |
| 1243 | + return -EINVAL; | |
| 1244 | + } | |
| 1245 | + | |
| 1246 | + return 0; | |
| 1247 | +} | |
| 1248 | + | |
| 1249 | +union get_voltage_info { | |
| 1250 | + struct _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2 in; | |
| 1251 | + struct _GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2 evv_out; | |
| 1252 | +}; | |
| 1253 | + | |
| 1254 | +int amdgpu_atombios_get_voltage_evv(struct amdgpu_device *adev, | |
| 1255 | + u16 virtual_voltage_id, | |
| 1256 | + u16 *voltage) | |
| 1257 | +{ | |
| 1258 | + int index = GetIndexIntoMasterTable(COMMAND, GetVoltageInfo); | |
| 1259 | + u32 entry_id; | |
| 1260 | + u32 count = adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; | |
| 1261 | + union get_voltage_info args; | |
| 1262 | + | |
| 1263 | + for (entry_id = 0; entry_id < count; entry_id++) { | |
| 1264 | + if (adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].v == | |
| 1265 | + virtual_voltage_id) | |
| 1266 | + break; | |
| 1267 | + } | |
| 1268 | + | |
| 1269 | + if (entry_id >= count) | |
| 1270 | + return -EINVAL; | |
| 1271 | + | |
| 1272 | + args.in.ucVoltageType = VOLTAGE_TYPE_VDDC; | |
| 1273 | + args.in.ucVoltageMode = ATOM_GET_VOLTAGE_EVV_VOLTAGE; | |
| 1274 | + args.in.usVoltageLevel = cpu_to_le16(virtual_voltage_id); | |
| 1275 | + args.in.ulSCLKFreq = | |
| 1276 | + cpu_to_le32(adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].clk); | |
| 1277 | + | |
| 1278 | + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); | |
| 1279 | + | |
| 1280 | + *voltage = le16_to_cpu(args.evv_out.usVoltageLevel); | |
| 1281 | + | |
| 1282 | + return 0; | |
| 1283 | +} | |
| 1284 | + | |
| 1285 | +union voltage_object_info { | |
| 1286 | + struct _ATOM_VOLTAGE_OBJECT_INFO v1; | |
| 1287 | + struct _ATOM_VOLTAGE_OBJECT_INFO_V2 v2; | |
| 1288 | + struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 v3; | |
| 1289 | +}; | |
| 1290 | + | |
| 1291 | +union voltage_object { | |
| 1292 | + struct _ATOM_VOLTAGE_OBJECT v1; | |
| 1293 | + struct _ATOM_VOLTAGE_OBJECT_V2 v2; | |
| 1294 | + union _ATOM_VOLTAGE_OBJECT_V3 v3; | |
| 1295 | +}; | |
| 1296 | + | |
| 1297 | + | |
| 1298 | +static ATOM_VOLTAGE_OBJECT_V3 *amdgpu_atombios_lookup_voltage_object_v3(ATOM_VOLTAGE_OBJECT_INFO_V3_1 *v3, | |
| 1299 | + u8 voltage_type, u8 voltage_mode) | |
| 1300 | +{ | |
| 1301 | + u32 size = le16_to_cpu(v3->sHeader.usStructureSize); | |
| 1302 | + u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]); | |
| 1303 | + u8 *start = (u8*)v3; | |
| 1304 | + | |
| 1305 | + while (offset < size) { | |
| 1306 | + ATOM_VOLTAGE_OBJECT_V3 *vo = (ATOM_VOLTAGE_OBJECT_V3 *)(start + offset); | |
| 1307 | + if ((vo->asGpioVoltageObj.sHeader.ucVoltageType == voltage_type) && | |
| 1308 | + (vo->asGpioVoltageObj.sHeader.ucVoltageMode == voltage_mode)) | |
| 1309 | + return vo; | |
| 1310 | + offset += le16_to_cpu(vo->asGpioVoltageObj.sHeader.usSize); | |
| 1311 | + } | |
| 1312 | + return NULL; | |
| 1313 | +} | |
| 1314 | + | |
| 1315 | +bool | |
| 1316 | +amdgpu_atombios_is_voltage_gpio(struct amdgpu_device *adev, | |
| 1317 | + u8 voltage_type, u8 voltage_mode) | |
| 1318 | +{ | |
| 1319 | + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); | |
| 1320 | + u8 frev, crev; | |
| 1321 | + u16 data_offset, size; | |
| 1322 | + union voltage_object_info *voltage_info; | |
| 1323 | + | |
| 1324 | + if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, | |
| 1325 | + &frev, &crev, &data_offset)) { | |
| 1326 | + voltage_info = (union voltage_object_info *) | |
| 1327 | + (adev->mode_info.atom_context->bios + data_offset); | |
| 1328 | + | |
| 1329 | + switch (frev) { | |
| 1330 | + case 3: | |
| 1331 | + switch (crev) { | |
| 1332 | + case 1: | |
| 1333 | + if (amdgpu_atombios_lookup_voltage_object_v3(&voltage_info->v3, | |
| 1334 | + voltage_type, voltage_mode)) | |
| 1335 | + return true; | |
| 1336 | + break; | |
| 1337 | + default: | |
| 1338 | + DRM_ERROR("unknown voltage object table\n"); | |
| 1339 | + return false; | |
| 1340 | + } | |
| 1341 | + break; | |
| 1342 | + default: | |
| 1343 | + DRM_ERROR("unknown voltage object table\n"); | |
| 1344 | + return false; | |
| 1345 | + } | |
| 1346 | + | |
| 1347 | + } | |
| 1348 | + return false; | |
| 1349 | +} | |
| 1350 | + | |
| 1351 | +int amdgpu_atombios_get_voltage_table(struct amdgpu_device *adev, | |
| 1352 | + u8 voltage_type, u8 voltage_mode, | |
| 1353 | + struct atom_voltage_table *voltage_table) | |
| 1354 | +{ | |
| 1355 | + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); | |
| 1356 | + u8 frev, crev; | |
| 1357 | + u16 data_offset, size; | |
| 1358 | + int i; | |
| 1359 | + union voltage_object_info *voltage_info; | |
| 1360 | + union voltage_object *voltage_object = NULL; | |
| 1361 | + | |
| 1362 | + if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, | |
| 1363 | + &frev, &crev, &data_offset)) { | |
| 1364 | + voltage_info = (union voltage_object_info *) | |
| 1365 | + (adev->mode_info.atom_context->bios + data_offset); | |
| 1366 | + | |
| 1367 | + switch (frev) { | |
| 1368 | + case 3: | |
| 1369 | + switch (crev) { | |
| 1370 | + case 1: | |
| 1371 | + voltage_object = (union voltage_object *) | |
| 1372 | + amdgpu_atombios_lookup_voltage_object_v3(&voltage_info->v3, | |
| 1373 | + voltage_type, voltage_mode); | |
| 1374 | + if (voltage_object) { | |
| 1375 | + ATOM_GPIO_VOLTAGE_OBJECT_V3 *gpio = | |
| 1376 | + &voltage_object->v3.asGpioVoltageObj; | |
| 1377 | + VOLTAGE_LUT_ENTRY_V2 *lut; | |
| 1378 | + if (gpio->ucGpioEntryNum > MAX_VOLTAGE_ENTRIES) | |
| 1379 | + return -EINVAL; | |
| 1380 | + lut = &gpio->asVolGpioLut[0]; | |
| 1381 | + for (i = 0; i < gpio->ucGpioEntryNum; i++) { | |
| 1382 | + voltage_table->entries[i].value = | |
| 1383 | + le16_to_cpu(lut->usVoltageValue); | |
| 1384 | + voltage_table->entries[i].smio_low = | |
| 1385 | + le32_to_cpu(lut->ulVoltageId); | |
| 1386 | + lut = (VOLTAGE_LUT_ENTRY_V2 *) | |
| 1387 | + ((u8 *)lut + sizeof(VOLTAGE_LUT_ENTRY_V2)); | |
| 1388 | + } | |
| 1389 | + voltage_table->mask_low = le32_to_cpu(gpio->ulGpioMaskVal); | |
| 1390 | + voltage_table->count = gpio->ucGpioEntryNum; | |
| 1391 | + voltage_table->phase_delay = gpio->ucPhaseDelay; | |
| 1392 | + return 0; | |
| 1393 | + } | |
| 1394 | + break; | |
| 1395 | + default: | |
| 1396 | + DRM_ERROR("unknown voltage object table\n"); | |
| 1397 | + return -EINVAL; | |
| 1398 | + } | |
| 1399 | + break; | |
| 1400 | + default: | |
| 1401 | + DRM_ERROR("unknown voltage object table\n"); | |
| 1402 | + return -EINVAL; | |
| 1403 | + } | |
| 1404 | + } | |
| 1405 | + return -EINVAL; | |
| 1406 | +} | |
| 1407 | + | |
| 1408 | +union vram_info { | |
| 1409 | + struct _ATOM_VRAM_INFO_V3 v1_3; | |
| 1410 | + struct _ATOM_VRAM_INFO_V4 v1_4; | |
| 1411 | + struct _ATOM_VRAM_INFO_HEADER_V2_1 v2_1; | |
| 1412 | +}; | |
| 1413 | + | |
| 1414 | +#define MEM_ID_MASK 0xff000000 | |
| 1415 | +#define MEM_ID_SHIFT 24 | |
| 1416 | +#define CLOCK_RANGE_MASK 0x00ffffff | |
| 1417 | +#define CLOCK_RANGE_SHIFT 0 | |
| 1418 | +#define LOW_NIBBLE_MASK 0xf | |
| 1419 | +#define DATA_EQU_PREV 0 | |
| 1420 | +#define DATA_FROM_TABLE 4 | |
| 1421 | + | |
| 1422 | +int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, | |
| 1423 | + u8 module_index, | |
| 1424 | + struct atom_mc_reg_table *reg_table) | |
| 1425 | +{ | |
| 1426 | + int index = GetIndexIntoMasterTable(DATA, VRAM_Info); | |
| 1427 | + u8 frev, crev, num_entries, t_mem_id, num_ranges = 0; | |
| 1428 | + u32 i = 0, j; | |
| 1429 | + u16 data_offset, size; | |
| 1430 | + union vram_info *vram_info; | |
| 1431 | + | |
| 1432 | + memset(reg_table, 0, sizeof(struct atom_mc_reg_table)); | |
| 1433 | + | |
| 1434 | + if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, | |
| 1435 | + &frev, &crev, &data_offset)) { | |
| 1436 | + vram_info = (union vram_info *) | |
| 1437 | + (adev->mode_info.atom_context->bios + data_offset); | |
| 1438 | + switch (frev) { | |
| 1439 | + case 1: | |
| 1440 | + DRM_ERROR("old table version %d, %d\n", frev, crev); | |
| 1441 | + return -EINVAL; | |
| 1442 | + case 2: | |
| 1443 | + switch (crev) { | |
| 1444 | + case 1: | |
| 1445 | + if (module_index < vram_info->v2_1.ucNumOfVRAMModule) { | |
| 1446 | + ATOM_INIT_REG_BLOCK *reg_block = | |
| 1447 | + (ATOM_INIT_REG_BLOCK *) | |
| 1448 | + ((u8 *)vram_info + le16_to_cpu(vram_info->v2_1.usMemClkPatchTblOffset)); | |
| 1449 | + ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = | |
| 1450 | + (ATOM_MEMORY_SETTING_DATA_BLOCK *) | |
| 1451 | + ((u8 *)reg_block + (2 * sizeof(u16)) + | |
| 1452 | + le16_to_cpu(reg_block->usRegIndexTblSize)); | |
| 1453 | + ATOM_INIT_REG_INDEX_FORMAT *format = ®_block->asRegIndexBuf[0]; | |
| 1454 | + num_entries = (u8)((le16_to_cpu(reg_block->usRegIndexTblSize)) / | |
| 1455 | + sizeof(ATOM_INIT_REG_INDEX_FORMAT)) - 1; | |
| 1456 | + if (num_entries > VBIOS_MC_REGISTER_ARRAY_SIZE) | |
| 1457 | + return -EINVAL; | |
| 1458 | + while (i < num_entries) { | |
| 1459 | + if (format->ucPreRegDataLength & ACCESS_PLACEHOLDER) | |
| 1460 | + break; | |
| 1461 | + reg_table->mc_reg_address[i].s1 = | |
| 1462 | + (u16)(le16_to_cpu(format->usRegIndex)); | |
| 1463 | + reg_table->mc_reg_address[i].pre_reg_data = | |
| 1464 | + (u8)(format->ucPreRegDataLength); | |
| 1465 | + i++; | |
| 1466 | + format = (ATOM_INIT_REG_INDEX_FORMAT *) | |
| 1467 | + ((u8 *)format + sizeof(ATOM_INIT_REG_INDEX_FORMAT)); | |
| 1468 | + } | |
| 1469 | + reg_table->last = i; | |
| 1470 | + while ((le32_to_cpu(*(u32 *)reg_data) != END_OF_REG_DATA_BLOCK) && | |
| 1471 | + (num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES)) { | |
| 1472 | + t_mem_id = (u8)((le32_to_cpu(*(u32 *)reg_data) & MEM_ID_MASK) | |
| 1473 | + >> MEM_ID_SHIFT); | |
| 1474 | + if (module_index == t_mem_id) { | |
| 1475 | + reg_table->mc_reg_table_entry[num_ranges].mclk_max = | |
| 1476 | + (u32)((le32_to_cpu(*(u32 *)reg_data) & CLOCK_RANGE_MASK) | |
| 1477 | + >> CLOCK_RANGE_SHIFT); | |
| 1478 | + for (i = 0, j = 1; i < reg_table->last; i++) { | |
| 1479 | + if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) { | |
| 1480 | + reg_table->mc_reg_table_entry[num_ranges].mc_data[i] = | |
| 1481 | + (u32)le32_to_cpu(*((u32 *)reg_data + j)); | |
| 1482 | + j++; | |
| 1483 | + } else if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_EQU_PREV) { | |
| 1484 | + reg_table->mc_reg_table_entry[num_ranges].mc_data[i] = | |
| 1485 | + reg_table->mc_reg_table_entry[num_ranges].mc_data[i - 1]; | |
| 1486 | + } | |
| 1487 | + } | |
| 1488 | + num_ranges++; | |
| 1489 | + } | |
| 1490 | + reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) | |
| 1491 | + ((u8 *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)); | |
| 1492 | + } | |
| 1493 | + if (le32_to_cpu(*(u32 *)reg_data) != END_OF_REG_DATA_BLOCK) | |
| 1494 | + return -EINVAL; | |
| 1495 | + reg_table->num_entries = num_ranges; | |
| 1496 | + } else | |
| 1497 | + return -EINVAL; | |
| 1498 | + break; | |
| 1499 | + default: | |
| 1500 | + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); | |
| 1501 | + return -EINVAL; | |
| 1502 | + } | |
| 1503 | + break; | |
| 1504 | + default: | |
| 1505 | + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); | |
| 1506 | + return -EINVAL; | |
| 1507 | + } | |
| 1508 | + return 0; | |
| 1509 | + } | |
| 1510 | + return -EINVAL; | |
| 1511 | +} | |
| 1512 | + | |
| 1513 | +void amdgpu_atombios_scratch_regs_lock(struct amdgpu_device *adev, bool lock) | |
| 1514 | +{ | |
| 1515 | + uint32_t bios_6_scratch; | |
| 1516 | + | |
| 1517 | + bios_6_scratch = RREG32(mmBIOS_SCRATCH_6); | |
| 1518 | + | |
| 1519 | + if (lock) { | |
| 1520 | + bios_6_scratch |= ATOM_S6_CRITICAL_STATE; | |
| 1521 | + bios_6_scratch &= ~ATOM_S6_ACC_MODE; | |
| 1522 | + } else { | |
| 1523 | + bios_6_scratch &= ~ATOM_S6_CRITICAL_STATE; | |
| 1524 | + bios_6_scratch |= ATOM_S6_ACC_MODE; | |
| 1525 | + } | |
| 1526 | + | |
| 1527 | + WREG32(mmBIOS_SCRATCH_6, bios_6_scratch); | |
| 1528 | +} | |
| 1529 | + | |
| 1530 | +void amdgpu_atombios_scratch_regs_init(struct amdgpu_device *adev) | |
| 1531 | +{ | |
| 1532 | + uint32_t bios_2_scratch, bios_6_scratch; | |
| 1533 | + | |
| 1534 | + bios_2_scratch = RREG32(mmBIOS_SCRATCH_2); | |
| 1535 | + bios_6_scratch = RREG32(mmBIOS_SCRATCH_6); | |
| 1536 | + | |
| 1537 | + /* let the bios control the backlight */ | |
| 1538 | + bios_2_scratch &= ~ATOM_S2_VRI_BRIGHT_ENABLE; | |
| 1539 | + | |
| 1540 | + /* tell the bios not to handle mode switching */ | |
| 1541 | + bios_6_scratch |= ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH; | |
| 1542 | + | |
| 1543 | + /* clear the vbios dpms state */ | |
| 1544 | + bios_2_scratch &= ~ATOM_S2_DEVICE_DPMS_STATE; | |
| 1545 | + | |
| 1546 | + WREG32(mmBIOS_SCRATCH_2, bios_2_scratch); | |
| 1547 | + WREG32(mmBIOS_SCRATCH_6, bios_6_scratch); | |
| 1548 | +} | |
| 1549 | + | |
| 1550 | +void amdgpu_atombios_scratch_regs_save(struct amdgpu_device *adev) | |
| 1551 | +{ | |
| 1552 | + int i; | |
| 1553 | + | |
| 1554 | + for (i = 0; i < AMDGPU_BIOS_NUM_SCRATCH; i++) | |
| 1555 | + adev->bios_scratch[i] = RREG32(mmBIOS_SCRATCH_0 + i); | |
| 1556 | +} | |
| 1557 | + | |
| 1558 | +void amdgpu_atombios_scratch_regs_restore(struct amdgpu_device *adev) | |
| 1559 | +{ | |
| 1560 | + int i; | |
| 1561 | + | |
| 1562 | + for (i = 0; i < AMDGPU_BIOS_NUM_SCRATCH; i++) | |
| 1563 | + WREG32(mmBIOS_SCRATCH_0 + i, adev->bios_scratch[i]); | |
| 1564 | +} | |
| 1565 | + | |
| 1566 | +/* Atom needs data in little endian format | |
| 1567 | + * so swap as appropriate when copying data to | |
| 1568 | + * or from atom. Note that atom operates on | |
| 1569 | + * dw units. | |
| 1570 | + */ | |
| 1571 | +void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) | |
| 1572 | +{ | |
| 1573 | +#ifdef __BIG_ENDIAN | |
| 1574 | + u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */ | |
| 1575 | + u32 *dst32, *src32; | |
| 1576 | + int i; | |
| 1577 | + | |
| 1578 | + memcpy(src_tmp, src, num_bytes); | |
| 1579 | + src32 = (u32 *)src_tmp; | |
| 1580 | + dst32 = (u32 *)dst_tmp; | |
| 1581 | + if (to_le) { | |
| 1582 | + for (i = 0; i < ((num_bytes + 3) / 4); i++) | |
| 1583 | + dst32[i] = cpu_to_le32(src32[i]); | |
| 1584 | + memcpy(dst, dst_tmp, num_bytes); | |
| 1585 | + } else { | |
| 1586 | + u8 dws = num_bytes & ~3; | |
| 1587 | + for (i = 0; i < ((num_bytes + 3) / 4); i++) | |
| 1588 | + dst32[i] = le32_to_cpu(src32[i]); | |
| 1589 | + memcpy(dst, dst_tmp, dws); | |
| 1590 | + if (num_bytes % 4) { | |
| 1591 | + for (i = 0; i < (num_bytes % 4); i++) | |
| 1592 | + dst[dws+i] = dst_tmp[dws+i]; | |
| 1593 | + } | |
| 1594 | + } | |
| 1595 | +#else | |
| 1596 | + memcpy(dst, src, num_bytes); | |
| 1597 | +#endif | |
| 1598 | +} | |
| @@ -0,0 +1,206 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#ifndef __AMDGPU_ATOMBIOS_H__ | |
| 25 | +#define __AMDGPU_ATOMBIOS_H__ | |
| 26 | + | |
| 27 | +struct atom_clock_dividers { | |
| 28 | + u32 post_div; | |
| 29 | + union { | |
| 30 | + struct { | |
| 31 | +#ifdef __BIG_ENDIAN | |
| 32 | + u32 reserved : 6; | |
| 33 | + u32 whole_fb_div : 12; | |
| 34 | + u32 frac_fb_div : 14; | |
| 35 | +#else | |
| 36 | + u32 frac_fb_div : 14; | |
| 37 | + u32 whole_fb_div : 12; | |
| 38 | + u32 reserved : 6; | |
| 39 | +#endif | |
| 40 | + }; | |
| 41 | + u32 fb_div; | |
| 42 | + }; | |
| 43 | + u32 ref_div; | |
| 44 | + bool enable_post_div; | |
| 45 | + bool enable_dithen; | |
| 46 | + u32 vco_mode; | |
| 47 | + u32 real_clock; | |
| 48 | + /* added for CI */ | |
| 49 | + u32 post_divider; | |
| 50 | + u32 flags; | |
| 51 | +}; | |
| 52 | + | |
| 53 | +struct atom_mpll_param { | |
| 54 | + union { | |
| 55 | + struct { | |
| 56 | +#ifdef __BIG_ENDIAN | |
| 57 | + u32 reserved : 8; | |
| 58 | + u32 clkfrac : 12; | |
| 59 | + u32 clkf : 12; | |
| 60 | +#else | |
| 61 | + u32 clkf : 12; | |
| 62 | + u32 clkfrac : 12; | |
| 63 | + u32 reserved : 8; | |
| 64 | +#endif | |
| 65 | + }; | |
| 66 | + u32 fb_div; | |
| 67 | + }; | |
| 68 | + u32 post_div; | |
| 69 | + u32 bwcntl; | |
| 70 | + u32 dll_speed; | |
| 71 | + u32 vco_mode; | |
| 72 | + u32 yclk_sel; | |
| 73 | + u32 qdr; | |
| 74 | + u32 half_rate; | |
| 75 | +}; | |
| 76 | + | |
| 77 | +#define MEM_TYPE_GDDR5 0x50 | |
| 78 | +#define MEM_TYPE_GDDR4 0x40 | |
| 79 | +#define MEM_TYPE_GDDR3 0x30 | |
| 80 | +#define MEM_TYPE_DDR2 0x20 | |
| 81 | +#define MEM_TYPE_GDDR1 0x10 | |
| 82 | +#define MEM_TYPE_DDR3 0xb0 | |
| 83 | +#define MEM_TYPE_MASK 0xf0 | |
| 84 | + | |
| 85 | +struct atom_memory_info { | |
| 86 | + u8 mem_vendor; | |
| 87 | + u8 mem_type; | |
| 88 | +}; | |
| 89 | + | |
| 90 | +#define MAX_AC_TIMING_ENTRIES 16 | |
| 91 | + | |
| 92 | +struct atom_memory_clock_range_table | |
| 93 | +{ | |
| 94 | + u8 num_entries; | |
| 95 | + u8 rsv[3]; | |
| 96 | + u32 mclk[MAX_AC_TIMING_ENTRIES]; | |
| 97 | +}; | |
| 98 | + | |
| 99 | +#define VBIOS_MC_REGISTER_ARRAY_SIZE 32 | |
| 100 | +#define VBIOS_MAX_AC_TIMING_ENTRIES 20 | |
| 101 | + | |
| 102 | +struct atom_mc_reg_entry { | |
| 103 | + u32 mclk_max; | |
| 104 | + u32 mc_data[VBIOS_MC_REGISTER_ARRAY_SIZE]; | |
| 105 | +}; | |
| 106 | + | |
| 107 | +struct atom_mc_register_address { | |
| 108 | + u16 s1; | |
| 109 | + u8 pre_reg_data; | |
| 110 | +}; | |
| 111 | + | |
| 112 | +struct atom_mc_reg_table { | |
| 113 | + u8 last; | |
| 114 | + u8 num_entries; | |
| 115 | + struct atom_mc_reg_entry mc_reg_table_entry[VBIOS_MAX_AC_TIMING_ENTRIES]; | |
| 116 | + struct atom_mc_register_address mc_reg_address[VBIOS_MC_REGISTER_ARRAY_SIZE]; | |
| 117 | +}; | |
| 118 | + | |
| 119 | +#define MAX_VOLTAGE_ENTRIES 32 | |
| 120 | + | |
| 121 | +struct atom_voltage_table_entry | |
| 122 | +{ | |
| 123 | + u16 value; | |
| 124 | + u32 smio_low; | |
| 125 | +}; | |
| 126 | + | |
| 127 | +struct atom_voltage_table | |
| 128 | +{ | |
| 129 | + u32 count; | |
| 130 | + u32 mask_low; | |
| 131 | + u32 phase_delay; | |
| 132 | + struct atom_voltage_table_entry entries[MAX_VOLTAGE_ENTRIES]; | |
| 133 | +}; | |
| 134 | + | |
| 135 | +struct amdgpu_gpio_rec | |
| 136 | +amdgpu_atombios_lookup_gpio(struct amdgpu_device *adev, | |
| 137 | + u8 id); | |
| 138 | + | |
| 139 | +struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev, | |
| 140 | + uint8_t id); | |
| 141 | +void amdgpu_atombios_i2c_init(struct amdgpu_device *adev); | |
| 142 | + | |
| 143 | +bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev); | |
| 144 | + | |
| 145 | +int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev); | |
| 146 | + | |
| 147 | +bool amdgpu_atombios_get_asic_ss_info(struct amdgpu_device *adev, | |
| 148 | + struct amdgpu_atom_ss *ss, | |
| 149 | + int id, u32 clock); | |
| 150 | + | |
| 151 | +int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev, | |
| 152 | + u8 clock_type, | |
| 153 | + u32 clock, | |
| 154 | + bool strobe_mode, | |
| 155 | + struct atom_clock_dividers *dividers); | |
| 156 | + | |
| 157 | +int amdgpu_atombios_get_memory_pll_dividers(struct amdgpu_device *adev, | |
| 158 | + u32 clock, | |
| 159 | + bool strobe_mode, | |
| 160 | + struct atom_mpll_param *mpll_param); | |
| 161 | + | |
| 162 | +uint32_t amdgpu_atombios_get_engine_clock(struct amdgpu_device *adev); | |
| 163 | +uint32_t amdgpu_atombios_get_memory_clock(struct amdgpu_device *adev); | |
| 164 | +void amdgpu_atombios_set_engine_clock(struct amdgpu_device *adev, | |
| 165 | + uint32_t eng_clock); | |
| 166 | +void amdgpu_atombios_set_memory_clock(struct amdgpu_device *adev, | |
| 167 | + uint32_t mem_clock); | |
| 168 | +void amdgpu_atombios_set_voltage(struct amdgpu_device *adev, | |
| 169 | + u16 voltage_level, | |
| 170 | + u8 voltage_type); | |
| 171 | + | |
| 172 | +void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev, | |
| 173 | + u32 eng_clock, u32 mem_clock); | |
| 174 | + | |
| 175 | +int amdgpu_atombios_get_leakage_id_from_vbios(struct amdgpu_device *adev, | |
| 176 | + u16 *leakage_id); | |
| 177 | + | |
| 178 | +int amdgpu_atombios_get_leakage_vddc_based_on_leakage_params(struct amdgpu_device *adev, | |
| 179 | + u16 *vddc, u16 *vddci, | |
| 180 | + u16 virtual_voltage_id, | |
| 181 | + u16 vbios_voltage_id); | |
| 182 | + | |
| 183 | +int amdgpu_atombios_get_voltage_evv(struct amdgpu_device *adev, | |
| 184 | + u16 virtual_voltage_id, | |
| 185 | + u16 *voltage); | |
| 186 | + | |
| 187 | +bool | |
| 188 | +amdgpu_atombios_is_voltage_gpio(struct amdgpu_device *adev, | |
| 189 | + u8 voltage_type, u8 voltage_mode); | |
| 190 | + | |
| 191 | +int amdgpu_atombios_get_voltage_table(struct amdgpu_device *adev, | |
| 192 | + u8 voltage_type, u8 voltage_mode, | |
| 193 | + struct atom_voltage_table *voltage_table); | |
| 194 | + | |
| 195 | +int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, | |
| 196 | + u8 module_index, | |
| 197 | + struct atom_mc_reg_table *reg_table); | |
| 198 | + | |
| 199 | +void amdgpu_atombios_scratch_regs_lock(struct amdgpu_device *adev, bool lock); | |
| 200 | +void amdgpu_atombios_scratch_regs_init(struct amdgpu_device *adev); | |
| 201 | +void amdgpu_atombios_scratch_regs_save(struct amdgpu_device *adev); | |
| 202 | +void amdgpu_atombios_scratch_regs_restore(struct amdgpu_device *adev); | |
| 203 | + | |
| 204 | +void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le); | |
| 205 | + | |
| 206 | +#endif | |
| @@ -0,0 +1,572 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2010 Red Hat Inc. | |
| 3 | + * Author : Dave Airlie <airlied@redhat.com> | |
| 4 | + * | |
| 5 | + * Licensed under GPLv2 | |
| 6 | + * | |
| 7 | + * ATPX support for both Intel/ATI | |
| 8 | + */ | |
| 9 | +#include <linux/vga_switcheroo.h> | |
| 10 | +#include <linux/slab.h> | |
| 11 | +#include <linux/acpi.h> | |
| 12 | +#include <linux/pci.h> | |
| 13 | + | |
| 14 | +#include "amdgpu_acpi.h" | |
| 15 | + | |
| 16 | +struct amdgpu_atpx_functions { | |
| 17 | + bool px_params; | |
| 18 | + bool power_cntl; | |
| 19 | + bool disp_mux_cntl; | |
| 20 | + bool i2c_mux_cntl; | |
| 21 | + bool switch_start; | |
| 22 | + bool switch_end; | |
| 23 | + bool disp_connectors_mapping; | |
| 24 | + bool disp_detetion_ports; | |
| 25 | +}; | |
| 26 | + | |
| 27 | +struct amdgpu_atpx { | |
| 28 | + acpi_handle handle; | |
| 29 | + struct amdgpu_atpx_functions functions; | |
| 30 | +}; | |
| 31 | + | |
| 32 | +static struct amdgpu_atpx_priv { | |
| 33 | + bool atpx_detected; | |
| 34 | + /* handle for device - and atpx */ | |
| 35 | + acpi_handle dhandle; | |
| 36 | + acpi_handle other_handle; | |
| 37 | + struct amdgpu_atpx atpx; | |
| 38 | +} amdgpu_atpx_priv; | |
| 39 | + | |
| 40 | +struct atpx_verify_interface { | |
| 41 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 42 | + u16 version; /* version */ | |
| 43 | + u32 function_bits; /* supported functions bit vector */ | |
| 44 | +} __packed; | |
| 45 | + | |
| 46 | +struct atpx_px_params { | |
| 47 | + u16 size; /* structure size in bytes (includes size field) */ | |
| 48 | + u32 valid_flags; /* which flags are valid */ | |
| 49 | + u32 flags; /* flags */ | |
| 50 | +} __packed; | |
| 51 | + | |
| 52 | +struct atpx_power_control { | |
| 53 | + u16 size; | |
| 54 | + u8 dgpu_state; | |
| 55 | +} __packed; | |
| 56 | + | |
| 57 | +struct atpx_mux { | |
| 58 | + u16 size; | |
| 59 | + u16 mux; | |
| 60 | +} __packed; | |
| 61 | + | |
| 62 | +bool amdgpu_has_atpx(void) { | |
| 63 | + return amdgpu_atpx_priv.atpx_detected; | |
| 64 | +} | |
| 65 | + | |
| 66 | +/** | |
| 67 | + * amdgpu_atpx_call - call an ATPX method | |
| 68 | + * | |
| 69 | + * @handle: acpi handle | |
| 70 | + * @function: the ATPX function to execute | |
| 71 | + * @params: ATPX function params | |
| 72 | + * | |
| 73 | + * Executes the requested ATPX function (all asics). | |
| 74 | + * Returns a pointer to the acpi output buffer. | |
| 75 | + */ | |
| 76 | +static union acpi_object *amdgpu_atpx_call(acpi_handle handle, int function, | |
| 77 | + struct acpi_buffer *params) | |
| 78 | +{ | |
| 79 | + acpi_status status; | |
| 80 | + union acpi_object atpx_arg_elements[2]; | |
| 81 | + struct acpi_object_list atpx_arg; | |
| 82 | + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | |
| 83 | + | |
| 84 | + atpx_arg.count = 2; | |
| 85 | + atpx_arg.pointer = &atpx_arg_elements[0]; | |
| 86 | + | |
| 87 | + atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; | |
| 88 | + atpx_arg_elements[0].integer.value = function; | |
| 89 | + | |
| 90 | + if (params) { | |
| 91 | + atpx_arg_elements[1].type = ACPI_TYPE_BUFFER; | |
| 92 | + atpx_arg_elements[1].buffer.length = params->length; | |
| 93 | + atpx_arg_elements[1].buffer.pointer = params->pointer; | |
| 94 | + } else { | |
| 95 | + /* We need a second fake parameter */ | |
| 96 | + atpx_arg_elements[1].type = ACPI_TYPE_INTEGER; | |
| 97 | + atpx_arg_elements[1].integer.value = 0; | |
| 98 | + } | |
| 99 | + | |
| 100 | + status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); | |
| 101 | + | |
| 102 | + /* Fail only if calling the method fails and ATPX is supported */ | |
| 103 | + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { | |
| 104 | + printk("failed to evaluate ATPX got %s\n", | |
| 105 | + acpi_format_exception(status)); | |
| 106 | + kfree(buffer.pointer); | |
| 107 | + return NULL; | |
| 108 | + } | |
| 109 | + | |
| 110 | + return buffer.pointer; | |
| 111 | +} | |
| 112 | + | |
| 113 | +/** | |
| 114 | + * amdgpu_atpx_parse_functions - parse supported functions | |
| 115 | + * | |
| 116 | + * @f: supported functions struct | |
| 117 | + * @mask: supported functions mask from ATPX | |
| 118 | + * | |
| 119 | + * Use the supported functions mask from ATPX function | |
| 120 | + * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions | |
| 121 | + * are supported (all asics). | |
| 122 | + */ | |
| 123 | +static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mask) | |
| 124 | +{ | |
| 125 | + f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED; | |
| 126 | + f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED; | |
| 127 | + f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED; | |
| 128 | + f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED; | |
| 129 | + f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED; | |
| 130 | + f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED; | |
| 131 | + f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED; | |
| 132 | + f->disp_detetion_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED; | |
| 133 | +} | |
| 134 | + | |
| 135 | +/** | |
| 136 | + * amdgpu_atpx_validate_functions - validate ATPX functions | |
| 137 | + * | |
| 138 | + * @atpx: amdgpu atpx struct | |
| 139 | + * | |
| 140 | + * Validate that required functions are enabled (all asics). | |
| 141 | + * returns 0 on success, error on failure. | |
| 142 | + */ | |
| 143 | +static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) | |
| 144 | +{ | |
| 145 | + /* make sure required functions are enabled */ | |
| 146 | + /* dGPU power control is required */ | |
| 147 | + atpx->functions.power_cntl = true; | |
| 148 | + | |
| 149 | + if (atpx->functions.px_params) { | |
| 150 | + union acpi_object *info; | |
| 151 | + struct atpx_px_params output; | |
| 152 | + size_t size; | |
| 153 | + u32 valid_bits; | |
| 154 | + | |
| 155 | + info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL); | |
| 156 | + if (!info) | |
| 157 | + return -EIO; | |
| 158 | + | |
| 159 | + memset(&output, 0, sizeof(output)); | |
| 160 | + | |
| 161 | + size = *(u16 *) info->buffer.pointer; | |
| 162 | + if (size < 10) { | |
| 163 | + printk("ATPX buffer is too small: %zu\n", size); | |
| 164 | + kfree(info); | |
| 165 | + return -EINVAL; | |
| 166 | + } | |
| 167 | + size = min(sizeof(output), size); | |
| 168 | + | |
| 169 | + memcpy(&output, info->buffer.pointer, size); | |
| 170 | + | |
| 171 | + valid_bits = output.flags & output.valid_flags; | |
| 172 | + /* if separate mux flag is set, mux controls are required */ | |
| 173 | + if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) { | |
| 174 | + atpx->functions.i2c_mux_cntl = true; | |
| 175 | + atpx->functions.disp_mux_cntl = true; | |
| 176 | + } | |
| 177 | + /* if any outputs are muxed, mux controls are required */ | |
| 178 | + if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED | | |
| 179 | + ATPX_TV_SIGNAL_MUXED | | |
| 180 | + ATPX_DFP_SIGNAL_MUXED)) | |
| 181 | + atpx->functions.disp_mux_cntl = true; | |
| 182 | + | |
| 183 | + kfree(info); | |
| 184 | + } | |
| 185 | + return 0; | |
| 186 | +} | |
| 187 | + | |
| 188 | +/** | |
| 189 | + * amdgpu_atpx_verify_interface - verify ATPX | |
| 190 | + * | |
| 191 | + * @atpx: amdgpu atpx struct | |
| 192 | + * | |
| 193 | + * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function | |
| 194 | + * to initialize ATPX and determine what features are supported | |
| 195 | + * (all asics). | |
| 196 | + * returns 0 on success, error on failure. | |
| 197 | + */ | |
| 198 | +static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx) | |
| 199 | +{ | |
| 200 | + union acpi_object *info; | |
| 201 | + struct atpx_verify_interface output; | |
| 202 | + size_t size; | |
| 203 | + int err = 0; | |
| 204 | + | |
| 205 | + info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL); | |
| 206 | + if (!info) | |
| 207 | + return -EIO; | |
| 208 | + | |
| 209 | + memset(&output, 0, sizeof(output)); | |
| 210 | + | |
| 211 | + size = *(u16 *) info->buffer.pointer; | |
| 212 | + if (size < 8) { | |
| 213 | + printk("ATPX buffer is too small: %zu\n", size); | |
| 214 | + err = -EINVAL; | |
| 215 | + goto out; | |
| 216 | + } | |
| 217 | + size = min(sizeof(output), size); | |
| 218 | + | |
| 219 | + memcpy(&output, info->buffer.pointer, size); | |
| 220 | + | |
| 221 | + /* TODO: check version? */ | |
| 222 | + printk("ATPX version %u, functions 0x%08x\n", | |
| 223 | + output.version, output.function_bits); | |
| 224 | + | |
| 225 | + amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits); | |
| 226 | + | |
| 227 | +out: | |
| 228 | + kfree(info); | |
| 229 | + return err; | |
| 230 | +} | |
| 231 | + | |
| 232 | +/** | |
| 233 | + * amdgpu_atpx_set_discrete_state - power up/down discrete GPU | |
| 234 | + * | |
| 235 | + * @atpx: atpx info struct | |
| 236 | + * @state: discrete GPU state (0 = power down, 1 = power up) | |
| 237 | + * | |
| 238 | + * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to | |
| 239 | + * power down/up the discrete GPU (all asics). | |
| 240 | + * Returns 0 on success, error on failure. | |
| 241 | + */ | |
| 242 | +static int amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state) | |
| 243 | +{ | |
| 244 | + struct acpi_buffer params; | |
| 245 | + union acpi_object *info; | |
| 246 | + struct atpx_power_control input; | |
| 247 | + | |
| 248 | + if (atpx->functions.power_cntl) { | |
| 249 | + input.size = 3; | |
| 250 | + input.dgpu_state = state; | |
| 251 | + params.length = input.size; | |
| 252 | + params.pointer = &input; | |
| 253 | + info = amdgpu_atpx_call(atpx->handle, | |
| 254 | + ATPX_FUNCTION_POWER_CONTROL, | |
| 255 | + ¶ms); | |
| 256 | + if (!info) | |
| 257 | + return -EIO; | |
| 258 | + kfree(info); | |
| 259 | + } | |
| 260 | + return 0; | |
| 261 | +} | |
| 262 | + | |
| 263 | +/** | |
| 264 | + * amdgpu_atpx_switch_disp_mux - switch display mux | |
| 265 | + * | |
| 266 | + * @atpx: atpx info struct | |
| 267 | + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) | |
| 268 | + * | |
| 269 | + * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to | |
| 270 | + * switch the display mux between the discrete GPU and integrated GPU | |
| 271 | + * (all asics). | |
| 272 | + * Returns 0 on success, error on failure. | |
| 273 | + */ | |
| 274 | +static int amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id) | |
| 275 | +{ | |
| 276 | + struct acpi_buffer params; | |
| 277 | + union acpi_object *info; | |
| 278 | + struct atpx_mux input; | |
| 279 | + | |
| 280 | + if (atpx->functions.disp_mux_cntl) { | |
| 281 | + input.size = 4; | |
| 282 | + input.mux = mux_id; | |
| 283 | + params.length = input.size; | |
| 284 | + params.pointer = &input; | |
| 285 | + info = amdgpu_atpx_call(atpx->handle, | |
| 286 | + ATPX_FUNCTION_DISPLAY_MUX_CONTROL, | |
| 287 | + ¶ms); | |
| 288 | + if (!info) | |
| 289 | + return -EIO; | |
| 290 | + kfree(info); | |
| 291 | + } | |
| 292 | + return 0; | |
| 293 | +} | |
| 294 | + | |
| 295 | +/** | |
| 296 | + * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux | |
| 297 | + * | |
| 298 | + * @atpx: atpx info struct | |
| 299 | + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) | |
| 300 | + * | |
| 301 | + * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to | |
| 302 | + * switch the i2c/hpd mux between the discrete GPU and integrated GPU | |
| 303 | + * (all asics). | |
| 304 | + * Returns 0 on success, error on failure. | |
| 305 | + */ | |
| 306 | +static int amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id) | |
| 307 | +{ | |
| 308 | + struct acpi_buffer params; | |
| 309 | + union acpi_object *info; | |
| 310 | + struct atpx_mux input; | |
| 311 | + | |
| 312 | + if (atpx->functions.i2c_mux_cntl) { | |
| 313 | + input.size = 4; | |
| 314 | + input.mux = mux_id; | |
| 315 | + params.length = input.size; | |
| 316 | + params.pointer = &input; | |
| 317 | + info = amdgpu_atpx_call(atpx->handle, | |
| 318 | + ATPX_FUNCTION_I2C_MUX_CONTROL, | |
| 319 | + ¶ms); | |
| 320 | + if (!info) | |
| 321 | + return -EIO; | |
| 322 | + kfree(info); | |
| 323 | + } | |
| 324 | + return 0; | |
| 325 | +} | |
| 326 | + | |
| 327 | +/** | |
| 328 | + * amdgpu_atpx_switch_start - notify the sbios of a GPU switch | |
| 329 | + * | |
| 330 | + * @atpx: atpx info struct | |
| 331 | + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) | |
| 332 | + * | |
| 333 | + * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX | |
| 334 | + * function to notify the sbios that a switch between the discrete GPU and | |
| 335 | + * integrated GPU has begun (all asics). | |
| 336 | + * Returns 0 on success, error on failure. | |
| 337 | + */ | |
| 338 | +static int amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id) | |
| 339 | +{ | |
| 340 | + struct acpi_buffer params; | |
| 341 | + union acpi_object *info; | |
| 342 | + struct atpx_mux input; | |
| 343 | + | |
| 344 | + if (atpx->functions.switch_start) { | |
| 345 | + input.size = 4; | |
| 346 | + input.mux = mux_id; | |
| 347 | + params.length = input.size; | |
| 348 | + params.pointer = &input; | |
| 349 | + info = amdgpu_atpx_call(atpx->handle, | |
| 350 | + ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION, | |
| 351 | + ¶ms); | |
| 352 | + if (!info) | |
| 353 | + return -EIO; | |
| 354 | + kfree(info); | |
| 355 | + } | |
| 356 | + return 0; | |
| 357 | +} | |
| 358 | + | |
| 359 | +/** | |
| 360 | + * amdgpu_atpx_switch_end - notify the sbios of a GPU switch | |
| 361 | + * | |
| 362 | + * @atpx: atpx info struct | |
| 363 | + * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) | |
| 364 | + * | |
| 365 | + * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX | |
| 366 | + * function to notify the sbios that a switch between the discrete GPU and | |
| 367 | + * integrated GPU has ended (all asics). | |
| 368 | + * Returns 0 on success, error on failure. | |
| 369 | + */ | |
| 370 | +static int amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id) | |
| 371 | +{ | |
| 372 | + struct acpi_buffer params; | |
| 373 | + union acpi_object *info; | |
| 374 | + struct atpx_mux input; | |
| 375 | + | |
| 376 | + if (atpx->functions.switch_end) { | |
| 377 | + input.size = 4; | |
| 378 | + input.mux = mux_id; | |
| 379 | + params.length = input.size; | |
| 380 | + params.pointer = &input; | |
| 381 | + info = amdgpu_atpx_call(atpx->handle, | |
| 382 | + ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION, | |
| 383 | + ¶ms); | |
| 384 | + if (!info) | |
| 385 | + return -EIO; | |
| 386 | + kfree(info); | |
| 387 | + } | |
| 388 | + return 0; | |
| 389 | +} | |
| 390 | + | |
| 391 | +/** | |
| 392 | + * amdgpu_atpx_switchto - switch to the requested GPU | |
| 393 | + * | |
| 394 | + * @id: GPU to switch to | |
| 395 | + * | |
| 396 | + * Execute the necessary ATPX functions to switch between the discrete GPU and | |
| 397 | + * integrated GPU (all asics). | |
| 398 | + * Returns 0 on success, error on failure. | |
| 399 | + */ | |
| 400 | +static int amdgpu_atpx_switchto(enum vga_switcheroo_client_id id) | |
| 401 | +{ | |
| 402 | + u16 gpu_id; | |
| 403 | + | |
| 404 | + if (id == VGA_SWITCHEROO_IGD) | |
| 405 | + gpu_id = ATPX_INTEGRATED_GPU; | |
| 406 | + else | |
| 407 | + gpu_id = ATPX_DISCRETE_GPU; | |
| 408 | + | |
| 409 | + amdgpu_atpx_switch_start(&amdgpu_atpx_priv.atpx, gpu_id); | |
| 410 | + amdgpu_atpx_switch_disp_mux(&amdgpu_atpx_priv.atpx, gpu_id); | |
| 411 | + amdgpu_atpx_switch_i2c_mux(&amdgpu_atpx_priv.atpx, gpu_id); | |
| 412 | + amdgpu_atpx_switch_end(&amdgpu_atpx_priv.atpx, gpu_id); | |
| 413 | + | |
| 414 | + return 0; | |
| 415 | +} | |
| 416 | + | |
| 417 | +/** | |
| 418 | + * amdgpu_atpx_power_state - power down/up the requested GPU | |
| 419 | + * | |
| 420 | + * @id: GPU to power down/up | |
| 421 | + * @state: requested power state (0 = off, 1 = on) | |
| 422 | + * | |
| 423 | + * Execute the necessary ATPX function to power down/up the discrete GPU | |
| 424 | + * (all asics). | |
| 425 | + * Returns 0 on success, error on failure. | |
| 426 | + */ | |
| 427 | +static int amdgpu_atpx_power_state(enum vga_switcheroo_client_id id, | |
| 428 | + enum vga_switcheroo_state state) | |
| 429 | +{ | |
| 430 | + /* on w500 ACPI can't change intel gpu state */ | |
| 431 | + if (id == VGA_SWITCHEROO_IGD) | |
| 432 | + return 0; | |
| 433 | + | |
| 434 | + amdgpu_atpx_set_discrete_state(&amdgpu_atpx_priv.atpx, state); | |
| 435 | + return 0; | |
| 436 | +} | |
| 437 | + | |
| 438 | +/** | |
| 439 | + * amdgpu_atpx_pci_probe_handle - look up the ATPX handle | |
| 440 | + * | |
| 441 | + * @pdev: pci device | |
| 442 | + * | |
| 443 | + * Look up the ATPX handles (all asics). | |
| 444 | + * Returns true if the handles are found, false if not. | |
| 445 | + */ | |
| 446 | +static bool amdgpu_atpx_pci_probe_handle(struct pci_dev *pdev) | |
| 447 | +{ | |
| 448 | + acpi_handle dhandle, atpx_handle; | |
| 449 | + acpi_status status; | |
| 450 | + | |
| 451 | + dhandle = ACPI_HANDLE(&pdev->dev); | |
| 452 | + if (!dhandle) | |
| 453 | + return false; | |
| 454 | + | |
| 455 | + status = acpi_get_handle(dhandle, "ATPX", &atpx_handle); | |
| 456 | + if (ACPI_FAILURE(status)) { | |
| 457 | + amdgpu_atpx_priv.other_handle = dhandle; | |
| 458 | + return false; | |
| 459 | + } | |
| 460 | + amdgpu_atpx_priv.dhandle = dhandle; | |
| 461 | + amdgpu_atpx_priv.atpx.handle = atpx_handle; | |
| 462 | + return true; | |
| 463 | +} | |
| 464 | + | |
| 465 | +/** | |
| 466 | + * amdgpu_atpx_init - verify the ATPX interface | |
| 467 | + * | |
| 468 | + * Verify the ATPX interface (all asics). | |
| 469 | + * Returns 0 on success, error on failure. | |
| 470 | + */ | |
| 471 | +static int amdgpu_atpx_init(void) | |
| 472 | +{ | |
| 473 | + int r; | |
| 474 | + | |
| 475 | + /* set up the ATPX handle */ | |
| 476 | + r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx); | |
| 477 | + if (r) | |
| 478 | + return r; | |
| 479 | + | |
| 480 | + /* validate the atpx setup */ | |
| 481 | + r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx); | |
| 482 | + if (r) | |
| 483 | + return r; | |
| 484 | + | |
| 485 | + return 0; | |
| 486 | +} | |
| 487 | + | |
| 488 | +/** | |
| 489 | + * amdgpu_atpx_get_client_id - get the client id | |
| 490 | + * | |
| 491 | + * @pdev: pci device | |
| 492 | + * | |
| 493 | + * look up whether we are the integrated or discrete GPU (all asics). | |
| 494 | + * Returns the client id. | |
| 495 | + */ | |
| 496 | +static int amdgpu_atpx_get_client_id(struct pci_dev *pdev) | |
| 497 | +{ | |
| 498 | + if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev)) | |
| 499 | + return VGA_SWITCHEROO_IGD; | |
| 500 | + else | |
| 501 | + return VGA_SWITCHEROO_DIS; | |
| 502 | +} | |
| 503 | + | |
| 504 | +static struct vga_switcheroo_handler amdgpu_atpx_handler = { | |
| 505 | + .switchto = amdgpu_atpx_switchto, | |
| 506 | + .power_state = amdgpu_atpx_power_state, | |
| 507 | + .init = amdgpu_atpx_init, | |
| 508 | + .get_client_id = amdgpu_atpx_get_client_id, | |
| 509 | +}; | |
| 510 | + | |
| 511 | +/** | |
| 512 | + * amdgpu_atpx_detect - detect whether we have PX | |
| 513 | + * | |
| 514 | + * Check if we have a PX system (all asics). | |
| 515 | + * Returns true if we have a PX system, false if not. | |
| 516 | + */ | |
| 517 | +static bool amdgpu_atpx_detect(void) | |
| 518 | +{ | |
| 519 | + char acpi_method_name[255] = { 0 }; | |
| 520 | + struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; | |
| 521 | + struct pci_dev *pdev = NULL; | |
| 522 | + bool has_atpx = false; | |
| 523 | + int vga_count = 0; | |
| 524 | + | |
| 525 | + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | |
| 526 | + vga_count++; | |
| 527 | + | |
| 528 | + has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true); | |
| 529 | + } | |
| 530 | + | |
| 531 | + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { | |
| 532 | + vga_count++; | |
| 533 | + | |
| 534 | + has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true); | |
| 535 | + } | |
| 536 | + | |
| 537 | + if (has_atpx && vga_count == 2) { | |
| 538 | + acpi_get_name(amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer); | |
| 539 | + printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n", | |
| 540 | + acpi_method_name); | |
| 541 | + amdgpu_atpx_priv.atpx_detected = true; | |
| 542 | + return true; | |
| 543 | + } | |
| 544 | + return false; | |
| 545 | +} | |
| 546 | + | |
| 547 | +/** | |
| 548 | + * amdgpu_register_atpx_handler - register with vga_switcheroo | |
| 549 | + * | |
| 550 | + * Register the PX callbacks with vga_switcheroo (all asics). | |
| 551 | + */ | |
| 552 | +void amdgpu_register_atpx_handler(void) | |
| 553 | +{ | |
| 554 | + bool r; | |
| 555 | + | |
| 556 | + /* detect if we have any ATPX + 2 VGA in the system */ | |
| 557 | + r = amdgpu_atpx_detect(); | |
| 558 | + if (!r) | |
| 559 | + return; | |
| 560 | + | |
| 561 | + vga_switcheroo_register_handler(&amdgpu_atpx_handler); | |
| 562 | +} | |
| 563 | + | |
| 564 | +/** | |
| 565 | + * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo | |
| 566 | + * | |
| 567 | + * Unregister the PX callbacks with vga_switcheroo (all asics). | |
| 568 | + */ | |
| 569 | +void amdgpu_unregister_atpx_handler(void) | |
| 570 | +{ | |
| 571 | + vga_switcheroo_unregister_handler(); | |
| 572 | +} | |
| @@ -0,0 +1,221 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2009 Jerome Glisse. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + * Authors: Jerome Glisse | |
| 23 | + */ | |
| 24 | +#include <drm/drmP.h> | |
| 25 | +#include <drm/amdgpu_drm.h> | |
| 26 | +#include "amdgpu.h" | |
| 27 | + | |
| 28 | +#define AMDGPU_BENCHMARK_ITERATIONS 1024 | |
| 29 | +#define AMDGPU_BENCHMARK_COMMON_MODES_N 17 | |
| 30 | + | |
| 31 | +static int amdgpu_benchmark_do_move(struct amdgpu_device *adev, unsigned size, | |
| 32 | + uint64_t saddr, uint64_t daddr, int n) | |
| 33 | +{ | |
| 34 | + unsigned long start_jiffies; | |
| 35 | + unsigned long end_jiffies; | |
| 36 | + struct amdgpu_fence *fence = NULL; | |
| 37 | + int i, r; | |
| 38 | + | |
| 39 | + start_jiffies = jiffies; | |
| 40 | + for (i = 0; i < n; i++) { | |
| 41 | + struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring; | |
| 42 | + r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence); | |
| 43 | + if (r) | |
| 44 | + goto exit_do_move; | |
| 45 | + r = amdgpu_fence_wait(fence, false); | |
| 46 | + if (r) | |
| 47 | + goto exit_do_move; | |
| 48 | + amdgpu_fence_unref(&fence); | |
| 49 | + } | |
| 50 | + end_jiffies = jiffies; | |
| 51 | + r = jiffies_to_msecs(end_jiffies - start_jiffies); | |
| 52 | + | |
| 53 | +exit_do_move: | |
| 54 | + if (fence) | |
| 55 | + amdgpu_fence_unref(&fence); | |
| 56 | + return r; | |
| 57 | +} | |
| 58 | + | |
| 59 | + | |
| 60 | +static void amdgpu_benchmark_log_results(int n, unsigned size, | |
| 61 | + unsigned int time, | |
| 62 | + unsigned sdomain, unsigned ddomain, | |
| 63 | + char *kind) | |
| 64 | +{ | |
| 65 | + unsigned int throughput = (n * (size >> 10)) / time; | |
| 66 | + DRM_INFO("amdgpu: %s %u bo moves of %u kB from" | |
| 67 | + " %d to %d in %u ms, throughput: %u Mb/s or %u MB/s\n", | |
| 68 | + kind, n, size >> 10, sdomain, ddomain, time, | |
| 69 | + throughput * 8, throughput); | |
| 70 | +} | |
| 71 | + | |
| 72 | +static void amdgpu_benchmark_move(struct amdgpu_device *adev, unsigned size, | |
| 73 | + unsigned sdomain, unsigned ddomain) | |
| 74 | +{ | |
| 75 | + struct amdgpu_bo *dobj = NULL; | |
| 76 | + struct amdgpu_bo *sobj = NULL; | |
| 77 | + uint64_t saddr, daddr; | |
| 78 | + int r, n; | |
| 79 | + int time; | |
| 80 | + | |
| 81 | + n = AMDGPU_BENCHMARK_ITERATIONS; | |
| 82 | + r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, sdomain, 0, NULL, &sobj); | |
| 83 | + if (r) { | |
| 84 | + goto out_cleanup; | |
| 85 | + } | |
| 86 | + r = amdgpu_bo_reserve(sobj, false); | |
| 87 | + if (unlikely(r != 0)) | |
| 88 | + goto out_cleanup; | |
| 89 | + r = amdgpu_bo_pin(sobj, sdomain, &saddr); | |
| 90 | + amdgpu_bo_unreserve(sobj); | |
| 91 | + if (r) { | |
| 92 | + goto out_cleanup; | |
| 93 | + } | |
| 94 | + r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, ddomain, 0, NULL, &dobj); | |
| 95 | + if (r) { | |
| 96 | + goto out_cleanup; | |
| 97 | + } | |
| 98 | + r = amdgpu_bo_reserve(dobj, false); | |
| 99 | + if (unlikely(r != 0)) | |
| 100 | + goto out_cleanup; | |
| 101 | + r = amdgpu_bo_pin(dobj, ddomain, &daddr); | |
| 102 | + amdgpu_bo_unreserve(dobj); | |
| 103 | + if (r) { | |
| 104 | + goto out_cleanup; | |
| 105 | + } | |
| 106 | + | |
| 107 | + if (adev->mman.buffer_funcs) { | |
| 108 | + time = amdgpu_benchmark_do_move(adev, size, saddr, daddr, n); | |
| 109 | + if (time < 0) | |
| 110 | + goto out_cleanup; | |
| 111 | + if (time > 0) | |
| 112 | + amdgpu_benchmark_log_results(n, size, time, | |
| 113 | + sdomain, ddomain, "dma"); | |
| 114 | + } | |
| 115 | + | |
| 116 | +out_cleanup: | |
| 117 | + if (sobj) { | |
| 118 | + r = amdgpu_bo_reserve(sobj, false); | |
| 119 | + if (likely(r == 0)) { | |
| 120 | + amdgpu_bo_unpin(sobj); | |
| 121 | + amdgpu_bo_unreserve(sobj); | |
| 122 | + } | |
| 123 | + amdgpu_bo_unref(&sobj); | |
| 124 | + } | |
| 125 | + if (dobj) { | |
| 126 | + r = amdgpu_bo_reserve(dobj, false); | |
| 127 | + if (likely(r == 0)) { | |
| 128 | + amdgpu_bo_unpin(dobj); | |
| 129 | + amdgpu_bo_unreserve(dobj); | |
| 130 | + } | |
| 131 | + amdgpu_bo_unref(&dobj); | |
| 132 | + } | |
| 133 | + | |
| 134 | + if (r) { | |
| 135 | + DRM_ERROR("Error while benchmarking BO move.\n"); | |
| 136 | + } | |
| 137 | +} | |
| 138 | + | |
| 139 | +void amdgpu_benchmark(struct amdgpu_device *adev, int test_number) | |
| 140 | +{ | |
| 141 | + int i; | |
| 142 | + int common_modes[AMDGPU_BENCHMARK_COMMON_MODES_N] = { | |
| 143 | + 640 * 480 * 4, | |
| 144 | + 720 * 480 * 4, | |
| 145 | + 800 * 600 * 4, | |
| 146 | + 848 * 480 * 4, | |
| 147 | + 1024 * 768 * 4, | |
| 148 | + 1152 * 768 * 4, | |
| 149 | + 1280 * 720 * 4, | |
| 150 | + 1280 * 800 * 4, | |
| 151 | + 1280 * 854 * 4, | |
| 152 | + 1280 * 960 * 4, | |
| 153 | + 1280 * 1024 * 4, | |
| 154 | + 1440 * 900 * 4, | |
| 155 | + 1400 * 1050 * 4, | |
| 156 | + 1680 * 1050 * 4, | |
| 157 | + 1600 * 1200 * 4, | |
| 158 | + 1920 * 1080 * 4, | |
| 159 | + 1920 * 1200 * 4 | |
| 160 | + }; | |
| 161 | + | |
| 162 | + switch (test_number) { | |
| 163 | + case 1: | |
| 164 | + /* simple test, VRAM to GTT and GTT to VRAM */ | |
| 165 | + amdgpu_benchmark_move(adev, 1024*1024, AMDGPU_GEM_DOMAIN_GTT, | |
| 166 | + AMDGPU_GEM_DOMAIN_VRAM); | |
| 167 | + amdgpu_benchmark_move(adev, 1024*1024, AMDGPU_GEM_DOMAIN_VRAM, | |
| 168 | + AMDGPU_GEM_DOMAIN_GTT); | |
| 169 | + break; | |
| 170 | + case 2: | |
| 171 | + /* simple test, VRAM to VRAM */ | |
| 172 | + amdgpu_benchmark_move(adev, 1024*1024, AMDGPU_GEM_DOMAIN_VRAM, | |
| 173 | + AMDGPU_GEM_DOMAIN_VRAM); | |
| 174 | + break; | |
| 175 | + case 3: | |
| 176 | + /* GTT to VRAM, buffer size sweep, powers of 2 */ | |
| 177 | + for (i = 1; i <= 16384; i <<= 1) | |
| 178 | + amdgpu_benchmark_move(adev, i * AMDGPU_GPU_PAGE_SIZE, | |
| 179 | + AMDGPU_GEM_DOMAIN_GTT, | |
| 180 | + AMDGPU_GEM_DOMAIN_VRAM); | |
| 181 | + break; | |
| 182 | + case 4: | |
| 183 | + /* VRAM to GTT, buffer size sweep, powers of 2 */ | |
| 184 | + for (i = 1; i <= 16384; i <<= 1) | |
| 185 | + amdgpu_benchmark_move(adev, i * AMDGPU_GPU_PAGE_SIZE, | |
| 186 | + AMDGPU_GEM_DOMAIN_VRAM, | |
| 187 | + AMDGPU_GEM_DOMAIN_GTT); | |
| 188 | + break; | |
| 189 | + case 5: | |
| 190 | + /* VRAM to VRAM, buffer size sweep, powers of 2 */ | |
| 191 | + for (i = 1; i <= 16384; i <<= 1) | |
| 192 | + amdgpu_benchmark_move(adev, i * AMDGPU_GPU_PAGE_SIZE, | |
| 193 | + AMDGPU_GEM_DOMAIN_VRAM, | |
| 194 | + AMDGPU_GEM_DOMAIN_VRAM); | |
| 195 | + break; | |
| 196 | + case 6: | |
| 197 | + /* GTT to VRAM, buffer size sweep, common modes */ | |
| 198 | + for (i = 0; i < AMDGPU_BENCHMARK_COMMON_MODES_N; i++) | |
| 199 | + amdgpu_benchmark_move(adev, common_modes[i], | |
| 200 | + AMDGPU_GEM_DOMAIN_GTT, | |
| 201 | + AMDGPU_GEM_DOMAIN_VRAM); | |
| 202 | + break; | |
| 203 | + case 7: | |
| 204 | + /* VRAM to GTT, buffer size sweep, common modes */ | |
| 205 | + for (i = 0; i < AMDGPU_BENCHMARK_COMMON_MODES_N; i++) | |
| 206 | + amdgpu_benchmark_move(adev, common_modes[i], | |
| 207 | + AMDGPU_GEM_DOMAIN_VRAM, | |
| 208 | + AMDGPU_GEM_DOMAIN_GTT); | |
| 209 | + break; | |
| 210 | + case 8: | |
| 211 | + /* VRAM to VRAM, buffer size sweep, common modes */ | |
| 212 | + for (i = 0; i < AMDGPU_BENCHMARK_COMMON_MODES_N; i++) | |
| 213 | + amdgpu_benchmark_move(adev, common_modes[i], | |
| 214 | + AMDGPU_GEM_DOMAIN_VRAM, | |
| 215 | + AMDGPU_GEM_DOMAIN_VRAM); | |
| 216 | + break; | |
| 217 | + | |
| 218 | + default: | |
| 219 | + DRM_ERROR("Unknown benchmark\n"); | |
| 220 | + } | |
| 221 | +} | |
| @@ -0,0 +1,363 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + */ | |
| 28 | +#include <drm/drmP.h> | |
| 29 | +#include "amdgpu.h" | |
| 30 | +#include "atom.h" | |
| 31 | + | |
| 32 | +#include <linux/vga_switcheroo.h> | |
| 33 | +#include <linux/slab.h> | |
| 34 | +#include <linux/acpi.h> | |
| 35 | +/* | |
| 36 | + * BIOS. | |
| 37 | + */ | |
| 38 | + | |
| 39 | +/* If you boot an IGP board with a discrete card as the primary, | |
| 40 | + * the IGP rom is not accessible via the rom bar as the IGP rom is | |
| 41 | + * part of the system bios. On boot, the system bios puts a | |
| 42 | + * copy of the igp rom at the start of vram if a discrete card is | |
| 43 | + * present. | |
| 44 | + */ | |
| 45 | +static bool igp_read_bios_from_vram(struct amdgpu_device *adev) | |
| 46 | +{ | |
| 47 | + uint8_t __iomem *bios; | |
| 48 | + resource_size_t vram_base; | |
| 49 | + resource_size_t size = 256 * 1024; /* ??? */ | |
| 50 | + | |
| 51 | + if (!(adev->flags & AMDGPU_IS_APU)) | |
| 52 | + if (!amdgpu_card_posted(adev)) | |
| 53 | + return false; | |
| 54 | + | |
| 55 | + adev->bios = NULL; | |
| 56 | + vram_base = pci_resource_start(adev->pdev, 0); | |
| 57 | + bios = ioremap(vram_base, size); | |
| 58 | + if (!bios) { | |
| 59 | + return false; | |
| 60 | + } | |
| 61 | + | |
| 62 | + if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { | |
| 63 | + iounmap(bios); | |
| 64 | + return false; | |
| 65 | + } | |
| 66 | + adev->bios = kmalloc(size, GFP_KERNEL); | |
| 67 | + if (adev->bios == NULL) { | |
| 68 | + iounmap(bios); | |
| 69 | + return false; | |
| 70 | + } | |
| 71 | + memcpy_fromio(adev->bios, bios, size); | |
| 72 | + iounmap(bios); | |
| 73 | + return true; | |
| 74 | +} | |
| 75 | + | |
| 76 | +bool amdgpu_read_bios(struct amdgpu_device *adev) | |
| 77 | +{ | |
| 78 | + uint8_t __iomem *bios, val1, val2; | |
| 79 | + size_t size; | |
| 80 | + | |
| 81 | + adev->bios = NULL; | |
| 82 | + /* XXX: some cards may return 0 for rom size? ddx has a workaround */ | |
| 83 | + bios = pci_map_rom(adev->pdev, &size); | |
| 84 | + if (!bios) { | |
| 85 | + return false; | |
| 86 | + } | |
| 87 | + | |
| 88 | + val1 = readb(&bios[0]); | |
| 89 | + val2 = readb(&bios[1]); | |
| 90 | + | |
| 91 | + if (size == 0 || val1 != 0x55 || val2 != 0xaa) { | |
| 92 | + pci_unmap_rom(adev->pdev, bios); | |
| 93 | + return false; | |
| 94 | + } | |
| 95 | + adev->bios = kzalloc(size, GFP_KERNEL); | |
| 96 | + if (adev->bios == NULL) { | |
| 97 | + pci_unmap_rom(adev->pdev, bios); | |
| 98 | + return false; | |
| 99 | + } | |
| 100 | + memcpy_fromio(adev->bios, bios, size); | |
| 101 | + pci_unmap_rom(adev->pdev, bios); | |
| 102 | + return true; | |
| 103 | +} | |
| 104 | + | |
| 105 | +static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) | |
| 106 | +{ | |
| 107 | + uint8_t __iomem *bios; | |
| 108 | + size_t size; | |
| 109 | + | |
| 110 | + adev->bios = NULL; | |
| 111 | + | |
| 112 | + bios = pci_platform_rom(adev->pdev, &size); | |
| 113 | + if (!bios) { | |
| 114 | + return false; | |
| 115 | + } | |
| 116 | + | |
| 117 | + if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { | |
| 118 | + return false; | |
| 119 | + } | |
| 120 | + adev->bios = kmemdup(bios, size, GFP_KERNEL); | |
| 121 | + if (adev->bios == NULL) { | |
| 122 | + return false; | |
| 123 | + } | |
| 124 | + | |
| 125 | + return true; | |
| 126 | +} | |
| 127 | + | |
| 128 | +#ifdef CONFIG_ACPI | |
| 129 | +/* ATRM is used to get the BIOS on the discrete cards in | |
| 130 | + * dual-gpu systems. | |
| 131 | + */ | |
| 132 | +/* retrieve the ROM in 4k blocks */ | |
| 133 | +#define ATRM_BIOS_PAGE 4096 | |
| 134 | +/** | |
| 135 | + * amdgpu_atrm_call - fetch a chunk of the vbios | |
| 136 | + * | |
| 137 | + * @atrm_handle: acpi ATRM handle | |
| 138 | + * @bios: vbios image pointer | |
| 139 | + * @offset: offset of vbios image data to fetch | |
| 140 | + * @len: length of vbios image data to fetch | |
| 141 | + * | |
| 142 | + * Executes ATRM to fetch a chunk of the discrete | |
| 143 | + * vbios image on PX systems (all asics). | |
| 144 | + * Returns the length of the buffer fetched. | |
| 145 | + */ | |
| 146 | +static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios, | |
| 147 | + int offset, int len) | |
| 148 | +{ | |
| 149 | + acpi_status status; | |
| 150 | + union acpi_object atrm_arg_elements[2], *obj; | |
| 151 | + struct acpi_object_list atrm_arg; | |
| 152 | + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; | |
| 153 | + | |
| 154 | + atrm_arg.count = 2; | |
| 155 | + atrm_arg.pointer = &atrm_arg_elements[0]; | |
| 156 | + | |
| 157 | + atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; | |
| 158 | + atrm_arg_elements[0].integer.value = offset; | |
| 159 | + | |
| 160 | + atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; | |
| 161 | + atrm_arg_elements[1].integer.value = len; | |
| 162 | + | |
| 163 | + status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); | |
| 164 | + if (ACPI_FAILURE(status)) { | |
| 165 | + printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); | |
| 166 | + return -ENODEV; | |
| 167 | + } | |
| 168 | + | |
| 169 | + obj = (union acpi_object *)buffer.pointer; | |
| 170 | + memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); | |
| 171 | + len = obj->buffer.length; | |
| 172 | + kfree(buffer.pointer); | |
| 173 | + return len; | |
| 174 | +} | |
| 175 | + | |
| 176 | +static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) | |
| 177 | +{ | |
| 178 | + int ret; | |
| 179 | + int size = 256 * 1024; | |
| 180 | + int i; | |
| 181 | + struct pci_dev *pdev = NULL; | |
| 182 | + acpi_handle dhandle, atrm_handle; | |
| 183 | + acpi_status status; | |
| 184 | + bool found = false; | |
| 185 | + | |
| 186 | + /* ATRM is for the discrete card only */ | |
| 187 | + if (adev->flags & AMDGPU_IS_APU) | |
| 188 | + return false; | |
| 189 | + | |
| 190 | + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | |
| 191 | + dhandle = ACPI_HANDLE(&pdev->dev); | |
| 192 | + if (!dhandle) | |
| 193 | + continue; | |
| 194 | + | |
| 195 | + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); | |
| 196 | + if (!ACPI_FAILURE(status)) { | |
| 197 | + found = true; | |
| 198 | + break; | |
| 199 | + } | |
| 200 | + } | |
| 201 | + | |
| 202 | + if (!found) { | |
| 203 | + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { | |
| 204 | + dhandle = ACPI_HANDLE(&pdev->dev); | |
| 205 | + if (!dhandle) | |
| 206 | + continue; | |
| 207 | + | |
| 208 | + status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); | |
| 209 | + if (!ACPI_FAILURE(status)) { | |
| 210 | + found = true; | |
| 211 | + break; | |
| 212 | + } | |
| 213 | + } | |
| 214 | + } | |
| 215 | + | |
| 216 | + if (!found) | |
| 217 | + return false; | |
| 218 | + | |
| 219 | + adev->bios = kmalloc(size, GFP_KERNEL); | |
| 220 | + if (!adev->bios) { | |
| 221 | + DRM_ERROR("Unable to allocate bios\n"); | |
| 222 | + return false; | |
| 223 | + } | |
| 224 | + | |
| 225 | + for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { | |
| 226 | + ret = amdgpu_atrm_call(atrm_handle, | |
| 227 | + adev->bios, | |
| 228 | + (i * ATRM_BIOS_PAGE), | |
| 229 | + ATRM_BIOS_PAGE); | |
| 230 | + if (ret < ATRM_BIOS_PAGE) | |
| 231 | + break; | |
| 232 | + } | |
| 233 | + | |
| 234 | + if (i == 0 || adev->bios[0] != 0x55 || adev->bios[1] != 0xaa) { | |
| 235 | + kfree(adev->bios); | |
| 236 | + return false; | |
| 237 | + } | |
| 238 | + return true; | |
| 239 | +} | |
| 240 | +#else | |
| 241 | +static inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) | |
| 242 | +{ | |
| 243 | + return false; | |
| 244 | +} | |
| 245 | +#endif | |
| 246 | + | |
| 247 | +static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) | |
| 248 | +{ | |
| 249 | + if (adev->flags & AMDGPU_IS_APU) | |
| 250 | + return igp_read_bios_from_vram(adev); | |
| 251 | + else | |
| 252 | + return amdgpu_asic_read_disabled_bios(adev); | |
| 253 | +} | |
| 254 | + | |
| 255 | +#ifdef CONFIG_ACPI | |
| 256 | +static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) | |
| 257 | +{ | |
| 258 | + bool ret = false; | |
| 259 | + struct acpi_table_header *hdr; | |
| 260 | + acpi_size tbl_size; | |
| 261 | + UEFI_ACPI_VFCT *vfct; | |
| 262 | + GOP_VBIOS_CONTENT *vbios; | |
| 263 | + VFCT_IMAGE_HEADER *vhdr; | |
| 264 | + | |
| 265 | + if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size))) | |
| 266 | + return false; | |
| 267 | + if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { | |
| 268 | + DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); | |
| 269 | + goto out_unmap; | |
| 270 | + } | |
| 271 | + | |
| 272 | + vfct = (UEFI_ACPI_VFCT *)hdr; | |
| 273 | + if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { | |
| 274 | + DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); | |
| 275 | + goto out_unmap; | |
| 276 | + } | |
| 277 | + | |
| 278 | + vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); | |
| 279 | + vhdr = &vbios->VbiosHeader; | |
| 280 | + DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", | |
| 281 | + vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, | |
| 282 | + vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); | |
| 283 | + | |
| 284 | + if (vhdr->PCIBus != adev->pdev->bus->number || | |
| 285 | + vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) || | |
| 286 | + vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn) || | |
| 287 | + vhdr->VendorID != adev->pdev->vendor || | |
| 288 | + vhdr->DeviceID != adev->pdev->device) { | |
| 289 | + DRM_INFO("ACPI VFCT table is not for this card\n"); | |
| 290 | + goto out_unmap; | |
| 291 | + } | |
| 292 | + | |
| 293 | + if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { | |
| 294 | + DRM_ERROR("ACPI VFCT image truncated\n"); | |
| 295 | + goto out_unmap; | |
| 296 | + } | |
| 297 | + | |
| 298 | + adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); | |
| 299 | + ret = !!adev->bios; | |
| 300 | + | |
| 301 | +out_unmap: | |
| 302 | + return ret; | |
| 303 | +} | |
| 304 | +#else | |
| 305 | +static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) | |
| 306 | +{ | |
| 307 | + return false; | |
| 308 | +} | |
| 309 | +#endif | |
| 310 | + | |
| 311 | +bool amdgpu_get_bios(struct amdgpu_device *adev) | |
| 312 | +{ | |
| 313 | + bool r; | |
| 314 | + uint16_t tmp; | |
| 315 | + | |
| 316 | + r = amdgpu_atrm_get_bios(adev); | |
| 317 | + if (r == false) | |
| 318 | + r = amdgpu_acpi_vfct_bios(adev); | |
| 319 | + if (r == false) | |
| 320 | + r = igp_read_bios_from_vram(adev); | |
| 321 | + if (r == false) | |
| 322 | + r = amdgpu_read_bios(adev); | |
| 323 | + if (r == false) { | |
| 324 | + r = amdgpu_read_disabled_bios(adev); | |
| 325 | + } | |
| 326 | + if (r == false) { | |
| 327 | + r = amdgpu_read_platform_bios(adev); | |
| 328 | + } | |
| 329 | + if (r == false || adev->bios == NULL) { | |
| 330 | + DRM_ERROR("Unable to locate a BIOS ROM\n"); | |
| 331 | + adev->bios = NULL; | |
| 332 | + return false; | |
| 333 | + } | |
| 334 | + if (adev->bios[0] != 0x55 || adev->bios[1] != 0xaa) { | |
| 335 | + printk("BIOS signature incorrect %x %x\n", adev->bios[0], adev->bios[1]); | |
| 336 | + goto free_bios; | |
| 337 | + } | |
| 338 | + | |
| 339 | + tmp = RBIOS16(0x18); | |
| 340 | + if (RBIOS8(tmp + 0x14) != 0x0) { | |
| 341 | + DRM_INFO("Not an x86 BIOS ROM, not using.\n"); | |
| 342 | + goto free_bios; | |
| 343 | + } | |
| 344 | + | |
| 345 | + adev->bios_header_start = RBIOS16(0x48); | |
| 346 | + if (!adev->bios_header_start) { | |
| 347 | + goto free_bios; | |
| 348 | + } | |
| 349 | + tmp = adev->bios_header_start + 4; | |
| 350 | + if (!memcmp(adev->bios + tmp, "ATOM", 4) || | |
| 351 | + !memcmp(adev->bios + tmp, "MOTA", 4)) { | |
| 352 | + adev->is_atom_bios = true; | |
| 353 | + } else { | |
| 354 | + adev->is_atom_bios = false; | |
| 355 | + } | |
| 356 | + | |
| 357 | + DRM_DEBUG("%sBIOS detected\n", adev->is_atom_bios ? "ATOM" : "COM"); | |
| 358 | + return true; | |
| 359 | +free_bios: | |
| 360 | + kfree(adev->bios); | |
| 361 | + adev->bios = NULL; | |
| 362 | + return false; | |
| 363 | +} | |
| @@ -0,0 +1,268 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2015 Advanced Micro Devices, Inc. | |
| 3 | + * All Rights Reserved. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the | |
| 7 | + * "Software"), to deal in the Software without restriction, including | |
| 8 | + * without limitation the rights to use, copy, modify, merge, publish, | |
| 9 | + * distribute, sub license, and/or sell copies of the Software, and to | |
| 10 | + * permit persons to whom the Software is furnished to do so, subject to | |
| 11 | + * the following conditions: | |
| 12 | + * | |
| 13 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 14 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 15 | + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
| 16 | + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | |
| 17 | + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
| 18 | + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
| 19 | + * USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 20 | + * | |
| 21 | + * The above copyright notice and this permission notice (including the | |
| 22 | + * next paragraph) shall be included in all copies or substantial portions | |
| 23 | + * of the Software. | |
| 24 | + * | |
| 25 | + */ | |
| 26 | +/* | |
| 27 | + * Authors: | |
| 28 | + * Christian König <deathsimple@vodafone.de> | |
| 29 | + */ | |
| 30 | + | |
| 31 | +#include <drm/drmP.h> | |
| 32 | +#include "amdgpu.h" | |
| 33 | + | |
| 34 | +static int amdgpu_bo_list_create(struct amdgpu_fpriv *fpriv, | |
| 35 | + struct amdgpu_bo_list **result, | |
| 36 | + int *id) | |
| 37 | +{ | |
| 38 | + int r; | |
| 39 | + | |
| 40 | + *result = kzalloc(sizeof(struct amdgpu_bo_list), GFP_KERNEL); | |
| 41 | + if (!*result) | |
| 42 | + return -ENOMEM; | |
| 43 | + | |
| 44 | + mutex_lock(&fpriv->bo_list_lock); | |
| 45 | + r = idr_alloc(&fpriv->bo_list_handles, *result, | |
| 46 | + 1, 0, GFP_KERNEL); | |
| 47 | + if (r < 0) { | |
| 48 | + mutex_unlock(&fpriv->bo_list_lock); | |
| 49 | + kfree(*result); | |
| 50 | + return r; | |
| 51 | + } | |
| 52 | + *id = r; | |
| 53 | + | |
| 54 | + mutex_init(&(*result)->lock); | |
| 55 | + (*result)->num_entries = 0; | |
| 56 | + (*result)->array = NULL; | |
| 57 | + | |
| 58 | + mutex_lock(&(*result)->lock); | |
| 59 | + mutex_unlock(&fpriv->bo_list_lock); | |
| 60 | + | |
| 61 | + return 0; | |
| 62 | +} | |
| 63 | + | |
| 64 | +static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) | |
| 65 | +{ | |
| 66 | + struct amdgpu_bo_list *list; | |
| 67 | + | |
| 68 | + mutex_lock(&fpriv->bo_list_lock); | |
| 69 | + list = idr_find(&fpriv->bo_list_handles, id); | |
| 70 | + if (list) { | |
| 71 | + mutex_lock(&list->lock); | |
| 72 | + idr_remove(&fpriv->bo_list_handles, id); | |
| 73 | + mutex_unlock(&list->lock); | |
| 74 | + amdgpu_bo_list_free(list); | |
| 75 | + } | |
| 76 | + mutex_unlock(&fpriv->bo_list_lock); | |
| 77 | +} | |
| 78 | + | |
| 79 | +static int amdgpu_bo_list_set(struct amdgpu_device *adev, | |
| 80 | + struct drm_file *filp, | |
| 81 | + struct amdgpu_bo_list *list, | |
| 82 | + struct drm_amdgpu_bo_list_entry *info, | |
| 83 | + unsigned num_entries) | |
| 84 | +{ | |
| 85 | + struct amdgpu_bo_list_entry *array; | |
| 86 | + struct amdgpu_bo *gds_obj = adev->gds.gds_gfx_bo; | |
| 87 | + struct amdgpu_bo *gws_obj = adev->gds.gws_gfx_bo; | |
| 88 | + struct amdgpu_bo *oa_obj = adev->gds.oa_gfx_bo; | |
| 89 | + | |
| 90 | + bool has_userptr = false; | |
| 91 | + unsigned i; | |
| 92 | + | |
| 93 | + array = drm_malloc_ab(num_entries, sizeof(struct amdgpu_bo_list_entry)); | |
| 94 | + if (!array) | |
| 95 | + return -ENOMEM; | |
| 96 | + memset(array, 0, num_entries * sizeof(struct amdgpu_bo_list_entry)); | |
| 97 | + | |
| 98 | + for (i = 0; i < num_entries; ++i) { | |
| 99 | + struct amdgpu_bo_list_entry *entry = &array[i]; | |
| 100 | + struct drm_gem_object *gobj; | |
| 101 | + | |
| 102 | + gobj = drm_gem_object_lookup(adev->ddev, filp, info[i].bo_handle); | |
| 103 | + if (!gobj) | |
| 104 | + goto error_free; | |
| 105 | + | |
| 106 | + entry->robj = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); | |
| 107 | + drm_gem_object_unreference_unlocked(gobj); | |
| 108 | + entry->priority = info[i].bo_priority; | |
| 109 | + entry->prefered_domains = entry->robj->initial_domain; | |
| 110 | + entry->allowed_domains = entry->prefered_domains; | |
| 111 | + if (entry->allowed_domains == AMDGPU_GEM_DOMAIN_VRAM) | |
| 112 | + entry->allowed_domains |= AMDGPU_GEM_DOMAIN_GTT; | |
| 113 | + if (amdgpu_ttm_tt_has_userptr(entry->robj->tbo.ttm)) { | |
| 114 | + has_userptr = true; | |
| 115 | + entry->prefered_domains = AMDGPU_GEM_DOMAIN_GTT; | |
| 116 | + entry->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; | |
| 117 | + } | |
| 118 | + entry->tv.bo = &entry->robj->tbo; | |
| 119 | + entry->tv.shared = true; | |
| 120 | + | |
| 121 | + if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GDS) | |
| 122 | + gds_obj = entry->robj; | |
| 123 | + if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GWS) | |
| 124 | + gws_obj = entry->robj; | |
| 125 | + if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_OA) | |
| 126 | + oa_obj = entry->robj; | |
| 127 | + } | |
| 128 | + | |
| 129 | + for (i = 0; i < list->num_entries; ++i) | |
| 130 | + amdgpu_bo_unref(&list->array[i].robj); | |
| 131 | + | |
| 132 | + drm_free_large(list->array); | |
| 133 | + | |
| 134 | + list->gds_obj = gds_obj; | |
| 135 | + list->gws_obj = gws_obj; | |
| 136 | + list->oa_obj = oa_obj; | |
| 137 | + list->has_userptr = has_userptr; | |
| 138 | + list->array = array; | |
| 139 | + list->num_entries = num_entries; | |
| 140 | + | |
| 141 | + return 0; | |
| 142 | + | |
| 143 | +error_free: | |
| 144 | + drm_free_large(array); | |
| 145 | + return -ENOENT; | |
| 146 | +} | |
| 147 | + | |
| 148 | +struct amdgpu_bo_list * | |
| 149 | +amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id) | |
| 150 | +{ | |
| 151 | + struct amdgpu_bo_list *result; | |
| 152 | + | |
| 153 | + mutex_lock(&fpriv->bo_list_lock); | |
| 154 | + result = idr_find(&fpriv->bo_list_handles, id); | |
| 155 | + if (result) | |
| 156 | + mutex_lock(&result->lock); | |
| 157 | + mutex_unlock(&fpriv->bo_list_lock); | |
| 158 | + return result; | |
| 159 | +} | |
| 160 | + | |
| 161 | +void amdgpu_bo_list_put(struct amdgpu_bo_list *list) | |
| 162 | +{ | |
| 163 | + mutex_unlock(&list->lock); | |
| 164 | +} | |
| 165 | + | |
| 166 | +void amdgpu_bo_list_free(struct amdgpu_bo_list *list) | |
| 167 | +{ | |
| 168 | + unsigned i; | |
| 169 | + | |
| 170 | + for (i = 0; i < list->num_entries; ++i) | |
| 171 | + amdgpu_bo_unref(&list->array[i].robj); | |
| 172 | + | |
| 173 | + mutex_destroy(&list->lock); | |
| 174 | + drm_free_large(list->array); | |
| 175 | + kfree(list); | |
| 176 | +} | |
| 177 | + | |
| 178 | +int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, | |
| 179 | + struct drm_file *filp) | |
| 180 | +{ | |
| 181 | + const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry); | |
| 182 | + | |
| 183 | + struct amdgpu_device *adev = dev->dev_private; | |
| 184 | + struct amdgpu_fpriv *fpriv = filp->driver_priv; | |
| 185 | + union drm_amdgpu_bo_list *args = data; | |
| 186 | + uint32_t handle = args->in.list_handle; | |
| 187 | + const void __user *uptr = (const void*)(long)args->in.bo_info_ptr; | |
| 188 | + | |
| 189 | + struct drm_amdgpu_bo_list_entry *info; | |
| 190 | + struct amdgpu_bo_list *list; | |
| 191 | + | |
| 192 | + int r; | |
| 193 | + | |
| 194 | + info = drm_malloc_ab(args->in.bo_number, | |
| 195 | + sizeof(struct drm_amdgpu_bo_list_entry)); | |
| 196 | + if (!info) | |
| 197 | + return -ENOMEM; | |
| 198 | + | |
| 199 | + /* copy the handle array from userspace to a kernel buffer */ | |
| 200 | + r = -EFAULT; | |
| 201 | + if (likely(info_size == args->in.bo_info_size)) { | |
| 202 | + unsigned long bytes = args->in.bo_number * | |
| 203 | + args->in.bo_info_size; | |
| 204 | + | |
| 205 | + if (copy_from_user(info, uptr, bytes)) | |
| 206 | + goto error_free; | |
| 207 | + | |
| 208 | + } else { | |
| 209 | + unsigned long bytes = min(args->in.bo_info_size, info_size); | |
| 210 | + unsigned i; | |
| 211 | + | |
| 212 | + memset(info, 0, args->in.bo_number * info_size); | |
| 213 | + for (i = 0; i < args->in.bo_number; ++i) { | |
| 214 | + if (copy_from_user(&info[i], uptr, bytes)) | |
| 215 | + goto error_free; | |
| 216 | + | |
| 217 | + uptr += args->in.bo_info_size; | |
| 218 | + } | |
| 219 | + } | |
| 220 | + | |
| 221 | + switch (args->in.operation) { | |
| 222 | + case AMDGPU_BO_LIST_OP_CREATE: | |
| 223 | + r = amdgpu_bo_list_create(fpriv, &list, &handle); | |
| 224 | + if (r) | |
| 225 | + goto error_free; | |
| 226 | + | |
| 227 | + r = amdgpu_bo_list_set(adev, filp, list, info, | |
| 228 | + args->in.bo_number); | |
| 229 | + amdgpu_bo_list_put(list); | |
| 230 | + if (r) | |
| 231 | + goto error_free; | |
| 232 | + | |
| 233 | + break; | |
| 234 | + | |
| 235 | + case AMDGPU_BO_LIST_OP_DESTROY: | |
| 236 | + amdgpu_bo_list_destroy(fpriv, handle); | |
| 237 | + handle = 0; | |
| 238 | + break; | |
| 239 | + | |
| 240 | + case AMDGPU_BO_LIST_OP_UPDATE: | |
| 241 | + r = -ENOENT; | |
| 242 | + list = amdgpu_bo_list_get(fpriv, handle); | |
| 243 | + if (!list) | |
| 244 | + goto error_free; | |
| 245 | + | |
| 246 | + r = amdgpu_bo_list_set(adev, filp, list, info, | |
| 247 | + args->in.bo_number); | |
| 248 | + amdgpu_bo_list_put(list); | |
| 249 | + if (r) | |
| 250 | + goto error_free; | |
| 251 | + | |
| 252 | + break; | |
| 253 | + | |
| 254 | + default: | |
| 255 | + r = -EINVAL; | |
| 256 | + goto error_free; | |
| 257 | + } | |
| 258 | + | |
| 259 | + memset(args, 0, sizeof(*args)); | |
| 260 | + args->out.list_handle = handle; | |
| 261 | + drm_free_large(info); | |
| 262 | + | |
| 263 | + return 0; | |
| 264 | + | |
| 265 | +error_free: | |
| 266 | + drm_free_large(info); | |
| 267 | + return r; | |
| 268 | +} | |
| @@ -0,0 +1,1907 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2007-8 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the "Software"), | |
| 7 | + * to deal in the Software without restriction, including without limitation | |
| 8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 10 | + * Software is furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in | |
| 13 | + * all copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 18 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 19 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 20 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 21 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 22 | + * | |
| 23 | + * Authors: Dave Airlie | |
| 24 | + * Alex Deucher | |
| 25 | + */ | |
| 26 | +#include <drm/drmP.h> | |
| 27 | +#include <drm/drm_edid.h> | |
| 28 | +#include <drm/drm_crtc_helper.h> | |
| 29 | +#include <drm/drm_fb_helper.h> | |
| 30 | +#include <drm/amdgpu_drm.h> | |
| 31 | +#include "amdgpu.h" | |
| 32 | +#include "atom.h" | |
| 33 | +#include "atombios_encoders.h" | |
| 34 | +#include "atombios_dp.h" | |
| 35 | +#include "amdgpu_connectors.h" | |
| 36 | +#include "amdgpu_i2c.h" | |
| 37 | + | |
| 38 | +#include <linux/pm_runtime.h> | |
| 39 | + | |
| 40 | +void amdgpu_connector_hotplug(struct drm_connector *connector) | |
| 41 | +{ | |
| 42 | + struct drm_device *dev = connector->dev; | |
| 43 | + struct amdgpu_device *adev = dev->dev_private; | |
| 44 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 45 | + | |
| 46 | + /* bail if the connector does not have hpd pin, e.g., | |
| 47 | + * VGA, TV, etc. | |
| 48 | + */ | |
| 49 | + if (amdgpu_connector->hpd.hpd == AMDGPU_HPD_NONE) | |
| 50 | + return; | |
| 51 | + | |
| 52 | + amdgpu_display_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd); | |
| 53 | + | |
| 54 | + /* if the connector is already off, don't turn it back on */ | |
| 55 | + if (connector->dpms != DRM_MODE_DPMS_ON) | |
| 56 | + return; | |
| 57 | + | |
| 58 | + /* just deal with DP (not eDP) here. */ | |
| 59 | + if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { | |
| 60 | + struct amdgpu_connector_atom_dig *dig_connector = | |
| 61 | + amdgpu_connector->con_priv; | |
| 62 | + | |
| 63 | + /* if existing sink type was not DP no need to retrain */ | |
| 64 | + if (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) | |
| 65 | + return; | |
| 66 | + | |
| 67 | + /* first get sink type as it may be reset after (un)plug */ | |
| 68 | + dig_connector->dp_sink_type = amdgpu_atombios_dp_get_sinktype(amdgpu_connector); | |
| 69 | + /* don't do anything if sink is not display port, i.e., | |
| 70 | + * passive dp->(dvi|hdmi) adaptor | |
| 71 | + */ | |
| 72 | + if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { | |
| 73 | + int saved_dpms = connector->dpms; | |
| 74 | + /* Only turn off the display if it's physically disconnected */ | |
| 75 | + if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { | |
| 76 | + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); | |
| 77 | + } else if (amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) { | |
| 78 | + /* set it to OFF so that drm_helper_connector_dpms() | |
| 79 | + * won't return immediately since the current state | |
| 80 | + * is ON at this point. | |
| 81 | + */ | |
| 82 | + connector->dpms = DRM_MODE_DPMS_OFF; | |
| 83 | + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); | |
| 84 | + } | |
| 85 | + connector->dpms = saved_dpms; | |
| 86 | + } | |
| 87 | + } | |
| 88 | +} | |
| 89 | + | |
| 90 | +static void amdgpu_connector_property_change_mode(struct drm_encoder *encoder) | |
| 91 | +{ | |
| 92 | + struct drm_crtc *crtc = encoder->crtc; | |
| 93 | + | |
| 94 | + if (crtc && crtc->enabled) { | |
| 95 | + drm_crtc_helper_set_mode(crtc, &crtc->mode, | |
| 96 | + crtc->x, crtc->y, crtc->primary->fb); | |
| 97 | + } | |
| 98 | +} | |
| 99 | + | |
| 100 | +int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector) | |
| 101 | +{ | |
| 102 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 103 | + struct amdgpu_connector_atom_dig *dig_connector; | |
| 104 | + int bpc = 8; | |
| 105 | + unsigned mode_clock, max_tmds_clock; | |
| 106 | + | |
| 107 | + switch (connector->connector_type) { | |
| 108 | + case DRM_MODE_CONNECTOR_DVII: | |
| 109 | + case DRM_MODE_CONNECTOR_HDMIB: | |
| 110 | + if (amdgpu_connector->use_digital) { | |
| 111 | + if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 112 | + if (connector->display_info.bpc) | |
| 113 | + bpc = connector->display_info.bpc; | |
| 114 | + } | |
| 115 | + } | |
| 116 | + break; | |
| 117 | + case DRM_MODE_CONNECTOR_DVID: | |
| 118 | + case DRM_MODE_CONNECTOR_HDMIA: | |
| 119 | + if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 120 | + if (connector->display_info.bpc) | |
| 121 | + bpc = connector->display_info.bpc; | |
| 122 | + } | |
| 123 | + break; | |
| 124 | + case DRM_MODE_CONNECTOR_DisplayPort: | |
| 125 | + dig_connector = amdgpu_connector->con_priv; | |
| 126 | + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || | |
| 127 | + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) || | |
| 128 | + drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 129 | + if (connector->display_info.bpc) | |
| 130 | + bpc = connector->display_info.bpc; | |
| 131 | + } | |
| 132 | + break; | |
| 133 | + case DRM_MODE_CONNECTOR_eDP: | |
| 134 | + case DRM_MODE_CONNECTOR_LVDS: | |
| 135 | + if (connector->display_info.bpc) | |
| 136 | + bpc = connector->display_info.bpc; | |
| 137 | + else { | |
| 138 | + const struct drm_connector_helper_funcs *connector_funcs = | |
| 139 | + connector->helper_private; | |
| 140 | + struct drm_encoder *encoder = connector_funcs->best_encoder(connector); | |
| 141 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 142 | + struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; | |
| 143 | + | |
| 144 | + if (dig->lcd_misc & ATOM_PANEL_MISC_V13_6BIT_PER_COLOR) | |
| 145 | + bpc = 6; | |
| 146 | + else if (dig->lcd_misc & ATOM_PANEL_MISC_V13_8BIT_PER_COLOR) | |
| 147 | + bpc = 8; | |
| 148 | + } | |
| 149 | + break; | |
| 150 | + } | |
| 151 | + | |
| 152 | + if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 153 | + /* | |
| 154 | + * Pre DCE-8 hw can't handle > 12 bpc, and more than 12 bpc doesn't make | |
| 155 | + * much sense without support for > 12 bpc framebuffers. RGB 4:4:4 at | |
| 156 | + * 12 bpc is always supported on hdmi deep color sinks, as this is | |
| 157 | + * required by the HDMI-1.3 spec. Clamp to a safe 12 bpc maximum. | |
| 158 | + */ | |
| 159 | + if (bpc > 12) { | |
| 160 | + DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 12 bpc.\n", | |
| 161 | + connector->name, bpc); | |
| 162 | + bpc = 12; | |
| 163 | + } | |
| 164 | + | |
| 165 | + /* Any defined maximum tmds clock limit we must not exceed? */ | |
| 166 | + if (connector->max_tmds_clock > 0) { | |
| 167 | + /* mode_clock is clock in kHz for mode to be modeset on this connector */ | |
| 168 | + mode_clock = amdgpu_connector->pixelclock_for_modeset; | |
| 169 | + | |
| 170 | + /* Maximum allowable input clock in kHz */ | |
| 171 | + max_tmds_clock = connector->max_tmds_clock * 1000; | |
| 172 | + | |
| 173 | + DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n", | |
| 174 | + connector->name, mode_clock, max_tmds_clock); | |
| 175 | + | |
| 176 | + /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */ | |
| 177 | + if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) { | |
| 178 | + if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) && | |
| 179 | + (mode_clock * 5/4 <= max_tmds_clock)) | |
| 180 | + bpc = 10; | |
| 181 | + else | |
| 182 | + bpc = 8; | |
| 183 | + | |
| 184 | + DRM_DEBUG("%s: HDMI deep color 12 bpc exceeds max tmds clock. Using %d bpc.\n", | |
| 185 | + connector->name, bpc); | |
| 186 | + } | |
| 187 | + | |
| 188 | + if ((bpc == 10) && (mode_clock * 5/4 > max_tmds_clock)) { | |
| 189 | + bpc = 8; | |
| 190 | + DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n", | |
| 191 | + connector->name, bpc); | |
| 192 | + } else if (bpc > 8) { | |
| 193 | + /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */ | |
| 194 | + DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n", | |
| 195 | + connector->name); | |
| 196 | + bpc = 8; | |
| 197 | + } | |
| 198 | + } | |
| 199 | + } | |
| 200 | + | |
| 201 | + if ((amdgpu_deep_color == 0) && (bpc > 8)) { | |
| 202 | + DRM_DEBUG("%s: Deep color disabled. Set amdgpu module param deep_color=1 to enable.\n", | |
| 203 | + connector->name); | |
| 204 | + bpc = 8; | |
| 205 | + } | |
| 206 | + | |
| 207 | + DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n", | |
| 208 | + connector->name, connector->display_info.bpc, bpc); | |
| 209 | + | |
| 210 | + return bpc; | |
| 211 | +} | |
| 212 | + | |
| 213 | +static void | |
| 214 | +amdgpu_connector_update_scratch_regs(struct drm_connector *connector, | |
| 215 | + enum drm_connector_status status) | |
| 216 | +{ | |
| 217 | + struct drm_encoder *best_encoder = NULL; | |
| 218 | + struct drm_encoder *encoder = NULL; | |
| 219 | + const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; | |
| 220 | + bool connected; | |
| 221 | + int i; | |
| 222 | + | |
| 223 | + best_encoder = connector_funcs->best_encoder(connector); | |
| 224 | + | |
| 225 | + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | |
| 226 | + if (connector->encoder_ids[i] == 0) | |
| 227 | + break; | |
| 228 | + | |
| 229 | + encoder = drm_encoder_find(connector->dev, | |
| 230 | + connector->encoder_ids[i]); | |
| 231 | + if (!encoder) | |
| 232 | + continue; | |
| 233 | + | |
| 234 | + if ((encoder == best_encoder) && (status == connector_status_connected)) | |
| 235 | + connected = true; | |
| 236 | + else | |
| 237 | + connected = false; | |
| 238 | + | |
| 239 | + amdgpu_atombios_encoder_set_bios_scratch_regs(connector, encoder, connected); | |
| 240 | + | |
| 241 | + } | |
| 242 | +} | |
| 243 | + | |
| 244 | +static struct drm_encoder * | |
| 245 | +amdgpu_connector_find_encoder(struct drm_connector *connector, | |
| 246 | + int encoder_type) | |
| 247 | +{ | |
| 248 | + struct drm_encoder *encoder; | |
| 249 | + int i; | |
| 250 | + | |
| 251 | + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | |
| 252 | + if (connector->encoder_ids[i] == 0) | |
| 253 | + break; | |
| 254 | + encoder = drm_encoder_find(connector->dev, | |
| 255 | + connector->encoder_ids[i]); | |
| 256 | + if (!encoder) | |
| 257 | + continue; | |
| 258 | + | |
| 259 | + if (encoder->encoder_type == encoder_type) | |
| 260 | + return encoder; | |
| 261 | + } | |
| 262 | + return NULL; | |
| 263 | +} | |
| 264 | + | |
| 265 | +struct edid *amdgpu_connector_edid(struct drm_connector *connector) | |
| 266 | +{ | |
| 267 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 268 | + struct drm_property_blob *edid_blob = connector->edid_blob_ptr; | |
| 269 | + | |
| 270 | + if (amdgpu_connector->edid) { | |
| 271 | + return amdgpu_connector->edid; | |
| 272 | + } else if (edid_blob) { | |
| 273 | + struct edid *edid = kmemdup(edid_blob->data, edid_blob->length, GFP_KERNEL); | |
| 274 | + if (edid) | |
| 275 | + amdgpu_connector->edid = edid; | |
| 276 | + } | |
| 277 | + return amdgpu_connector->edid; | |
| 278 | +} | |
| 279 | + | |
| 280 | +static struct edid * | |
| 281 | +amdgpu_connector_get_hardcoded_edid(struct amdgpu_device *adev) | |
| 282 | +{ | |
| 283 | + struct edid *edid; | |
| 284 | + | |
| 285 | + if (adev->mode_info.bios_hardcoded_edid) { | |
| 286 | + edid = kmalloc(adev->mode_info.bios_hardcoded_edid_size, GFP_KERNEL); | |
| 287 | + if (edid) { | |
| 288 | + memcpy((unsigned char *)edid, | |
| 289 | + (unsigned char *)adev->mode_info.bios_hardcoded_edid, | |
| 290 | + adev->mode_info.bios_hardcoded_edid_size); | |
| 291 | + return edid; | |
| 292 | + } | |
| 293 | + } | |
| 294 | + return NULL; | |
| 295 | +} | |
| 296 | + | |
| 297 | +static void amdgpu_connector_get_edid(struct drm_connector *connector) | |
| 298 | +{ | |
| 299 | + struct drm_device *dev = connector->dev; | |
| 300 | + struct amdgpu_device *adev = dev->dev_private; | |
| 301 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 302 | + | |
| 303 | + if (amdgpu_connector->edid) | |
| 304 | + return; | |
| 305 | + | |
| 306 | + /* on hw with routers, select right port */ | |
| 307 | + if (amdgpu_connector->router.ddc_valid) | |
| 308 | + amdgpu_i2c_router_select_ddc_port(amdgpu_connector); | |
| 309 | + | |
| 310 | + if ((amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != | |
| 311 | + ENCODER_OBJECT_ID_NONE) && | |
| 312 | + amdgpu_connector->ddc_bus->has_aux) { | |
| 313 | + amdgpu_connector->edid = drm_get_edid(connector, | |
| 314 | + &amdgpu_connector->ddc_bus->aux.ddc); | |
| 315 | + } else if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || | |
| 316 | + (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { | |
| 317 | + struct amdgpu_connector_atom_dig *dig = amdgpu_connector->con_priv; | |
| 318 | + | |
| 319 | + if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT || | |
| 320 | + dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && | |
| 321 | + amdgpu_connector->ddc_bus->has_aux) | |
| 322 | + amdgpu_connector->edid = drm_get_edid(connector, | |
| 323 | + &amdgpu_connector->ddc_bus->aux.ddc); | |
| 324 | + else if (amdgpu_connector->ddc_bus) | |
| 325 | + amdgpu_connector->edid = drm_get_edid(connector, | |
| 326 | + &amdgpu_connector->ddc_bus->adapter); | |
| 327 | + } else if (amdgpu_connector->ddc_bus) { | |
| 328 | + amdgpu_connector->edid = drm_get_edid(connector, | |
| 329 | + &amdgpu_connector->ddc_bus->adapter); | |
| 330 | + } | |
| 331 | + | |
| 332 | + if (!amdgpu_connector->edid) { | |
| 333 | + /* some laptops provide a hardcoded edid in rom for LCDs */ | |
| 334 | + if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) || | |
| 335 | + (connector->connector_type == DRM_MODE_CONNECTOR_eDP))) | |
| 336 | + amdgpu_connector->edid = amdgpu_connector_get_hardcoded_edid(adev); | |
| 337 | + } | |
| 338 | +} | |
| 339 | + | |
| 340 | +static void amdgpu_connector_free_edid(struct drm_connector *connector) | |
| 341 | +{ | |
| 342 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 343 | + | |
| 344 | + if (amdgpu_connector->edid) { | |
| 345 | + kfree(amdgpu_connector->edid); | |
| 346 | + amdgpu_connector->edid = NULL; | |
| 347 | + } | |
| 348 | +} | |
| 349 | + | |
| 350 | +static int amdgpu_connector_ddc_get_modes(struct drm_connector *connector) | |
| 351 | +{ | |
| 352 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 353 | + int ret; | |
| 354 | + | |
| 355 | + if (amdgpu_connector->edid) { | |
| 356 | + drm_mode_connector_update_edid_property(connector, amdgpu_connector->edid); | |
| 357 | + ret = drm_add_edid_modes(connector, amdgpu_connector->edid); | |
| 358 | + drm_edid_to_eld(connector, amdgpu_connector->edid); | |
| 359 | + return ret; | |
| 360 | + } | |
| 361 | + drm_mode_connector_update_edid_property(connector, NULL); | |
| 362 | + return 0; | |
| 363 | +} | |
| 364 | + | |
| 365 | +static struct drm_encoder * | |
| 366 | +amdgpu_connector_best_single_encoder(struct drm_connector *connector) | |
| 367 | +{ | |
| 368 | + int enc_id = connector->encoder_ids[0]; | |
| 369 | + | |
| 370 | + /* pick the encoder ids */ | |
| 371 | + if (enc_id) | |
| 372 | + return drm_encoder_find(connector->dev, enc_id); | |
| 373 | + return NULL; | |
| 374 | +} | |
| 375 | + | |
| 376 | +static void amdgpu_get_native_mode(struct drm_connector *connector) | |
| 377 | +{ | |
| 378 | + struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
| 379 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 380 | + | |
| 381 | + if (encoder == NULL) | |
| 382 | + return; | |
| 383 | + | |
| 384 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 385 | + | |
| 386 | + if (!list_empty(&connector->probed_modes)) { | |
| 387 | + struct drm_display_mode *preferred_mode = | |
| 388 | + list_first_entry(&connector->probed_modes, | |
| 389 | + struct drm_display_mode, head); | |
| 390 | + | |
| 391 | + amdgpu_encoder->native_mode = *preferred_mode; | |
| 392 | + } else { | |
| 393 | + amdgpu_encoder->native_mode.clock = 0; | |
| 394 | + } | |
| 395 | +} | |
| 396 | + | |
| 397 | +static struct drm_display_mode * | |
| 398 | +amdgpu_connector_lcd_native_mode(struct drm_encoder *encoder) | |
| 399 | +{ | |
| 400 | + struct drm_device *dev = encoder->dev; | |
| 401 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 402 | + struct drm_display_mode *mode = NULL; | |
| 403 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 404 | + | |
| 405 | + if (native_mode->hdisplay != 0 && | |
| 406 | + native_mode->vdisplay != 0 && | |
| 407 | + native_mode->clock != 0) { | |
| 408 | + mode = drm_mode_duplicate(dev, native_mode); | |
| 409 | + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; | |
| 410 | + drm_mode_set_name(mode); | |
| 411 | + | |
| 412 | + DRM_DEBUG_KMS("Adding native panel mode %s\n", mode->name); | |
| 413 | + } else if (native_mode->hdisplay != 0 && | |
| 414 | + native_mode->vdisplay != 0) { | |
| 415 | + /* mac laptops without an edid */ | |
| 416 | + /* Note that this is not necessarily the exact panel mode, | |
| 417 | + * but an approximation based on the cvt formula. For these | |
| 418 | + * systems we should ideally read the mode info out of the | |
| 419 | + * registers or add a mode table, but this works and is much | |
| 420 | + * simpler. | |
| 421 | + */ | |
| 422 | + mode = drm_cvt_mode(dev, native_mode->hdisplay, native_mode->vdisplay, 60, true, false, false); | |
| 423 | + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; | |
| 424 | + DRM_DEBUG_KMS("Adding cvt approximation of native panel mode %s\n", mode->name); | |
| 425 | + } | |
| 426 | + return mode; | |
| 427 | +} | |
| 428 | + | |
| 429 | +static void amdgpu_connector_add_common_modes(struct drm_encoder *encoder, | |
| 430 | + struct drm_connector *connector) | |
| 431 | +{ | |
| 432 | + struct drm_device *dev = encoder->dev; | |
| 433 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 434 | + struct drm_display_mode *mode = NULL; | |
| 435 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 436 | + int i; | |
| 437 | + struct mode_size { | |
| 438 | + int w; | |
| 439 | + int h; | |
| 440 | + } common_modes[17] = { | |
| 441 | + { 640, 480}, | |
| 442 | + { 720, 480}, | |
| 443 | + { 800, 600}, | |
| 444 | + { 848, 480}, | |
| 445 | + {1024, 768}, | |
| 446 | + {1152, 768}, | |
| 447 | + {1280, 720}, | |
| 448 | + {1280, 800}, | |
| 449 | + {1280, 854}, | |
| 450 | + {1280, 960}, | |
| 451 | + {1280, 1024}, | |
| 452 | + {1440, 900}, | |
| 453 | + {1400, 1050}, | |
| 454 | + {1680, 1050}, | |
| 455 | + {1600, 1200}, | |
| 456 | + {1920, 1080}, | |
| 457 | + {1920, 1200} | |
| 458 | + }; | |
| 459 | + | |
| 460 | + for (i = 0; i < 17; i++) { | |
| 461 | + if (amdgpu_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { | |
| 462 | + if (common_modes[i].w > 1024 || | |
| 463 | + common_modes[i].h > 768) | |
| 464 | + continue; | |
| 465 | + } | |
| 466 | + if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { | |
| 467 | + if (common_modes[i].w > native_mode->hdisplay || | |
| 468 | + common_modes[i].h > native_mode->vdisplay || | |
| 469 | + (common_modes[i].w == native_mode->hdisplay && | |
| 470 | + common_modes[i].h == native_mode->vdisplay)) | |
| 471 | + continue; | |
| 472 | + } | |
| 473 | + if (common_modes[i].w < 320 || common_modes[i].h < 200) | |
| 474 | + continue; | |
| 475 | + | |
| 476 | + mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); | |
| 477 | + drm_mode_probed_add(connector, mode); | |
| 478 | + } | |
| 479 | +} | |
| 480 | + | |
| 481 | +static int amdgpu_connector_set_property(struct drm_connector *connector, | |
| 482 | + struct drm_property *property, | |
| 483 | + uint64_t val) | |
| 484 | +{ | |
| 485 | + struct drm_device *dev = connector->dev; | |
| 486 | + struct amdgpu_device *adev = dev->dev_private; | |
| 487 | + struct drm_encoder *encoder; | |
| 488 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 489 | + | |
| 490 | + if (property == adev->mode_info.coherent_mode_property) { | |
| 491 | + struct amdgpu_encoder_atom_dig *dig; | |
| 492 | + bool new_coherent_mode; | |
| 493 | + | |
| 494 | + /* need to find digital encoder on connector */ | |
| 495 | + encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); | |
| 496 | + if (!encoder) | |
| 497 | + return 0; | |
| 498 | + | |
| 499 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 500 | + | |
| 501 | + if (!amdgpu_encoder->enc_priv) | |
| 502 | + return 0; | |
| 503 | + | |
| 504 | + dig = amdgpu_encoder->enc_priv; | |
| 505 | + new_coherent_mode = val ? true : false; | |
| 506 | + if (dig->coherent_mode != new_coherent_mode) { | |
| 507 | + dig->coherent_mode = new_coherent_mode; | |
| 508 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 509 | + } | |
| 510 | + } | |
| 511 | + | |
| 512 | + if (property == adev->mode_info.audio_property) { | |
| 513 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 514 | + /* need to find digital encoder on connector */ | |
| 515 | + encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); | |
| 516 | + if (!encoder) | |
| 517 | + return 0; | |
| 518 | + | |
| 519 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 520 | + | |
| 521 | + if (amdgpu_connector->audio != val) { | |
| 522 | + amdgpu_connector->audio = val; | |
| 523 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 524 | + } | |
| 525 | + } | |
| 526 | + | |
| 527 | + if (property == adev->mode_info.dither_property) { | |
| 528 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 529 | + /* need to find digital encoder on connector */ | |
| 530 | + encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); | |
| 531 | + if (!encoder) | |
| 532 | + return 0; | |
| 533 | + | |
| 534 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 535 | + | |
| 536 | + if (amdgpu_connector->dither != val) { | |
| 537 | + amdgpu_connector->dither = val; | |
| 538 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 539 | + } | |
| 540 | + } | |
| 541 | + | |
| 542 | + if (property == adev->mode_info.underscan_property) { | |
| 543 | + /* need to find digital encoder on connector */ | |
| 544 | + encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); | |
| 545 | + if (!encoder) | |
| 546 | + return 0; | |
| 547 | + | |
| 548 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 549 | + | |
| 550 | + if (amdgpu_encoder->underscan_type != val) { | |
| 551 | + amdgpu_encoder->underscan_type = val; | |
| 552 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 553 | + } | |
| 554 | + } | |
| 555 | + | |
| 556 | + if (property == adev->mode_info.underscan_hborder_property) { | |
| 557 | + /* need to find digital encoder on connector */ | |
| 558 | + encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); | |
| 559 | + if (!encoder) | |
| 560 | + return 0; | |
| 561 | + | |
| 562 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 563 | + | |
| 564 | + if (amdgpu_encoder->underscan_hborder != val) { | |
| 565 | + amdgpu_encoder->underscan_hborder = val; | |
| 566 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 567 | + } | |
| 568 | + } | |
| 569 | + | |
| 570 | + if (property == adev->mode_info.underscan_vborder_property) { | |
| 571 | + /* need to find digital encoder on connector */ | |
| 572 | + encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); | |
| 573 | + if (!encoder) | |
| 574 | + return 0; | |
| 575 | + | |
| 576 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 577 | + | |
| 578 | + if (amdgpu_encoder->underscan_vborder != val) { | |
| 579 | + amdgpu_encoder->underscan_vborder = val; | |
| 580 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 581 | + } | |
| 582 | + } | |
| 583 | + | |
| 584 | + if (property == adev->mode_info.load_detect_property) { | |
| 585 | + struct amdgpu_connector *amdgpu_connector = | |
| 586 | + to_amdgpu_connector(connector); | |
| 587 | + | |
| 588 | + if (val == 0) | |
| 589 | + amdgpu_connector->dac_load_detect = false; | |
| 590 | + else | |
| 591 | + amdgpu_connector->dac_load_detect = true; | |
| 592 | + } | |
| 593 | + | |
| 594 | + if (property == dev->mode_config.scaling_mode_property) { | |
| 595 | + enum amdgpu_rmx_type rmx_type; | |
| 596 | + | |
| 597 | + if (connector->encoder) { | |
| 598 | + amdgpu_encoder = to_amdgpu_encoder(connector->encoder); | |
| 599 | + } else { | |
| 600 | + const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; | |
| 601 | + amdgpu_encoder = to_amdgpu_encoder(connector_funcs->best_encoder(connector)); | |
| 602 | + } | |
| 603 | + | |
| 604 | + switch (val) { | |
| 605 | + default: | |
| 606 | + case DRM_MODE_SCALE_NONE: rmx_type = RMX_OFF; break; | |
| 607 | + case DRM_MODE_SCALE_CENTER: rmx_type = RMX_CENTER; break; | |
| 608 | + case DRM_MODE_SCALE_ASPECT: rmx_type = RMX_ASPECT; break; | |
| 609 | + case DRM_MODE_SCALE_FULLSCREEN: rmx_type = RMX_FULL; break; | |
| 610 | + } | |
| 611 | + if (amdgpu_encoder->rmx_type == rmx_type) | |
| 612 | + return 0; | |
| 613 | + | |
| 614 | + if ((rmx_type != DRM_MODE_SCALE_NONE) && | |
| 615 | + (amdgpu_encoder->native_mode.clock == 0)) | |
| 616 | + return 0; | |
| 617 | + | |
| 618 | + amdgpu_encoder->rmx_type = rmx_type; | |
| 619 | + | |
| 620 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 621 | + } | |
| 622 | + | |
| 623 | + return 0; | |
| 624 | +} | |
| 625 | + | |
| 626 | +static void | |
| 627 | +amdgpu_connector_fixup_lcd_native_mode(struct drm_encoder *encoder, | |
| 628 | + struct drm_connector *connector) | |
| 629 | +{ | |
| 630 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 631 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 632 | + struct drm_display_mode *t, *mode; | |
| 633 | + | |
| 634 | + /* If the EDID preferred mode doesn't match the native mode, use it */ | |
| 635 | + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { | |
| 636 | + if (mode->type & DRM_MODE_TYPE_PREFERRED) { | |
| 637 | + if (mode->hdisplay != native_mode->hdisplay || | |
| 638 | + mode->vdisplay != native_mode->vdisplay) | |
| 639 | + memcpy(native_mode, mode, sizeof(*mode)); | |
| 640 | + } | |
| 641 | + } | |
| 642 | + | |
| 643 | + /* Try to get native mode details from EDID if necessary */ | |
| 644 | + if (!native_mode->clock) { | |
| 645 | + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { | |
| 646 | + if (mode->hdisplay == native_mode->hdisplay && | |
| 647 | + mode->vdisplay == native_mode->vdisplay) { | |
| 648 | + *native_mode = *mode; | |
| 649 | + drm_mode_set_crtcinfo(native_mode, CRTC_INTERLACE_HALVE_V); | |
| 650 | + DRM_DEBUG_KMS("Determined LVDS native mode details from EDID\n"); | |
| 651 | + break; | |
| 652 | + } | |
| 653 | + } | |
| 654 | + } | |
| 655 | + | |
| 656 | + if (!native_mode->clock) { | |
| 657 | + DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n"); | |
| 658 | + amdgpu_encoder->rmx_type = RMX_OFF; | |
| 659 | + } | |
| 660 | +} | |
| 661 | + | |
| 662 | +static int amdgpu_connector_lvds_get_modes(struct drm_connector *connector) | |
| 663 | +{ | |
| 664 | + struct drm_encoder *encoder; | |
| 665 | + int ret = 0; | |
| 666 | + struct drm_display_mode *mode; | |
| 667 | + | |
| 668 | + amdgpu_connector_get_edid(connector); | |
| 669 | + ret = amdgpu_connector_ddc_get_modes(connector); | |
| 670 | + if (ret > 0) { | |
| 671 | + encoder = amdgpu_connector_best_single_encoder(connector); | |
| 672 | + if (encoder) { | |
| 673 | + amdgpu_connector_fixup_lcd_native_mode(encoder, connector); | |
| 674 | + /* add scaled modes */ | |
| 675 | + amdgpu_connector_add_common_modes(encoder, connector); | |
| 676 | + } | |
| 677 | + return ret; | |
| 678 | + } | |
| 679 | + | |
| 680 | + encoder = amdgpu_connector_best_single_encoder(connector); | |
| 681 | + if (!encoder) | |
| 682 | + return 0; | |
| 683 | + | |
| 684 | + /* we have no EDID modes */ | |
| 685 | + mode = amdgpu_connector_lcd_native_mode(encoder); | |
| 686 | + if (mode) { | |
| 687 | + ret = 1; | |
| 688 | + drm_mode_probed_add(connector, mode); | |
| 689 | + /* add the width/height from vbios tables if available */ | |
| 690 | + connector->display_info.width_mm = mode->width_mm; | |
| 691 | + connector->display_info.height_mm = mode->height_mm; | |
| 692 | + /* add scaled modes */ | |
| 693 | + amdgpu_connector_add_common_modes(encoder, connector); | |
| 694 | + } | |
| 695 | + | |
| 696 | + return ret; | |
| 697 | +} | |
| 698 | + | |
| 699 | +static int amdgpu_connector_lvds_mode_valid(struct drm_connector *connector, | |
| 700 | + struct drm_display_mode *mode) | |
| 701 | +{ | |
| 702 | + struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
| 703 | + | |
| 704 | + if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) | |
| 705 | + return MODE_PANEL; | |
| 706 | + | |
| 707 | + if (encoder) { | |
| 708 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 709 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 710 | + | |
| 711 | + /* AVIVO hardware supports downscaling modes larger than the panel | |
| 712 | + * to the panel size, but I'm not sure this is desirable. | |
| 713 | + */ | |
| 714 | + if ((mode->hdisplay > native_mode->hdisplay) || | |
| 715 | + (mode->vdisplay > native_mode->vdisplay)) | |
| 716 | + return MODE_PANEL; | |
| 717 | + | |
| 718 | + /* if scaling is disabled, block non-native modes */ | |
| 719 | + if (amdgpu_encoder->rmx_type == RMX_OFF) { | |
| 720 | + if ((mode->hdisplay != native_mode->hdisplay) || | |
| 721 | + (mode->vdisplay != native_mode->vdisplay)) | |
| 722 | + return MODE_PANEL; | |
| 723 | + } | |
| 724 | + } | |
| 725 | + | |
| 726 | + return MODE_OK; | |
| 727 | +} | |
| 728 | + | |
| 729 | +static enum drm_connector_status | |
| 730 | +amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force) | |
| 731 | +{ | |
| 732 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 733 | + struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
| 734 | + enum drm_connector_status ret = connector_status_disconnected; | |
| 735 | + int r; | |
| 736 | + | |
| 737 | + r = pm_runtime_get_sync(connector->dev->dev); | |
| 738 | + if (r < 0) | |
| 739 | + return connector_status_disconnected; | |
| 740 | + | |
| 741 | + if (encoder) { | |
| 742 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 743 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 744 | + | |
| 745 | + /* check if panel is valid */ | |
| 746 | + if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) | |
| 747 | + ret = connector_status_connected; | |
| 748 | + | |
| 749 | + } | |
| 750 | + | |
| 751 | + /* check for edid as well */ | |
| 752 | + amdgpu_connector_get_edid(connector); | |
| 753 | + if (amdgpu_connector->edid) | |
| 754 | + ret = connector_status_connected; | |
| 755 | + /* check acpi lid status ??? */ | |
| 756 | + | |
| 757 | + amdgpu_connector_update_scratch_regs(connector, ret); | |
| 758 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
| 759 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
| 760 | + return ret; | |
| 761 | +} | |
| 762 | + | |
| 763 | +static void amdgpu_connector_destroy(struct drm_connector *connector) | |
| 764 | +{ | |
| 765 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 766 | + | |
| 767 | + if (amdgpu_connector->ddc_bus->has_aux) | |
| 768 | + drm_dp_aux_unregister(&amdgpu_connector->ddc_bus->aux); | |
| 769 | + amdgpu_connector_free_edid(connector); | |
| 770 | + kfree(amdgpu_connector->con_priv); | |
| 771 | + drm_connector_unregister(connector); | |
| 772 | + drm_connector_cleanup(connector); | |
| 773 | + kfree(connector); | |
| 774 | +} | |
| 775 | + | |
| 776 | +static int amdgpu_connector_set_lcd_property(struct drm_connector *connector, | |
| 777 | + struct drm_property *property, | |
| 778 | + uint64_t value) | |
| 779 | +{ | |
| 780 | + struct drm_device *dev = connector->dev; | |
| 781 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 782 | + enum amdgpu_rmx_type rmx_type; | |
| 783 | + | |
| 784 | + DRM_DEBUG_KMS("\n"); | |
| 785 | + if (property != dev->mode_config.scaling_mode_property) | |
| 786 | + return 0; | |
| 787 | + | |
| 788 | + if (connector->encoder) | |
| 789 | + amdgpu_encoder = to_amdgpu_encoder(connector->encoder); | |
| 790 | + else { | |
| 791 | + const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; | |
| 792 | + amdgpu_encoder = to_amdgpu_encoder(connector_funcs->best_encoder(connector)); | |
| 793 | + } | |
| 794 | + | |
| 795 | + switch (value) { | |
| 796 | + case DRM_MODE_SCALE_NONE: rmx_type = RMX_OFF; break; | |
| 797 | + case DRM_MODE_SCALE_CENTER: rmx_type = RMX_CENTER; break; | |
| 798 | + case DRM_MODE_SCALE_ASPECT: rmx_type = RMX_ASPECT; break; | |
| 799 | + default: | |
| 800 | + case DRM_MODE_SCALE_FULLSCREEN: rmx_type = RMX_FULL; break; | |
| 801 | + } | |
| 802 | + if (amdgpu_encoder->rmx_type == rmx_type) | |
| 803 | + return 0; | |
| 804 | + | |
| 805 | + amdgpu_encoder->rmx_type = rmx_type; | |
| 806 | + | |
| 807 | + amdgpu_connector_property_change_mode(&amdgpu_encoder->base); | |
| 808 | + return 0; | |
| 809 | +} | |
| 810 | + | |
| 811 | + | |
| 812 | +static const struct drm_connector_helper_funcs amdgpu_connector_lvds_helper_funcs = { | |
| 813 | + .get_modes = amdgpu_connector_lvds_get_modes, | |
| 814 | + .mode_valid = amdgpu_connector_lvds_mode_valid, | |
| 815 | + .best_encoder = amdgpu_connector_best_single_encoder, | |
| 816 | +}; | |
| 817 | + | |
| 818 | +static const struct drm_connector_funcs amdgpu_connector_lvds_funcs = { | |
| 819 | + .dpms = drm_helper_connector_dpms, | |
| 820 | + .detect = amdgpu_connector_lvds_detect, | |
| 821 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
| 822 | + .destroy = amdgpu_connector_destroy, | |
| 823 | + .set_property = amdgpu_connector_set_lcd_property, | |
| 824 | +}; | |
| 825 | + | |
| 826 | +static int amdgpu_connector_vga_get_modes(struct drm_connector *connector) | |
| 827 | +{ | |
| 828 | + int ret; | |
| 829 | + | |
| 830 | + amdgpu_connector_get_edid(connector); | |
| 831 | + ret = amdgpu_connector_ddc_get_modes(connector); | |
| 832 | + | |
| 833 | + return ret; | |
| 834 | +} | |
| 835 | + | |
| 836 | +static int amdgpu_connector_vga_mode_valid(struct drm_connector *connector, | |
| 837 | + struct drm_display_mode *mode) | |
| 838 | +{ | |
| 839 | + struct drm_device *dev = connector->dev; | |
| 840 | + struct amdgpu_device *adev = dev->dev_private; | |
| 841 | + | |
| 842 | + /* XXX check mode bandwidth */ | |
| 843 | + | |
| 844 | + if ((mode->clock / 10) > adev->clock.max_pixel_clock) | |
| 845 | + return MODE_CLOCK_HIGH; | |
| 846 | + | |
| 847 | + return MODE_OK; | |
| 848 | +} | |
| 849 | + | |
| 850 | +static enum drm_connector_status | |
| 851 | +amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) | |
| 852 | +{ | |
| 853 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 854 | + struct drm_encoder *encoder; | |
| 855 | + const struct drm_encoder_helper_funcs *encoder_funcs; | |
| 856 | + bool dret = false; | |
| 857 | + enum drm_connector_status ret = connector_status_disconnected; | |
| 858 | + int r; | |
| 859 | + | |
| 860 | + r = pm_runtime_get_sync(connector->dev->dev); | |
| 861 | + if (r < 0) | |
| 862 | + return connector_status_disconnected; | |
| 863 | + | |
| 864 | + encoder = amdgpu_connector_best_single_encoder(connector); | |
| 865 | + if (!encoder) | |
| 866 | + ret = connector_status_disconnected; | |
| 867 | + | |
| 868 | + if (amdgpu_connector->ddc_bus) | |
| 869 | + dret = amdgpu_ddc_probe(amdgpu_connector, false); | |
| 870 | + if (dret) { | |
| 871 | + amdgpu_connector->detected_by_load = false; | |
| 872 | + amdgpu_connector_free_edid(connector); | |
| 873 | + amdgpu_connector_get_edid(connector); | |
| 874 | + | |
| 875 | + if (!amdgpu_connector->edid) { | |
| 876 | + DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", | |
| 877 | + connector->name); | |
| 878 | + ret = connector_status_connected; | |
| 879 | + } else { | |
| 880 | + amdgpu_connector->use_digital = | |
| 881 | + !!(amdgpu_connector->edid->input & DRM_EDID_INPUT_DIGITAL); | |
| 882 | + | |
| 883 | + /* some oems have boards with separate digital and analog connectors | |
| 884 | + * with a shared ddc line (often vga + hdmi) | |
| 885 | + */ | |
| 886 | + if (amdgpu_connector->use_digital && amdgpu_connector->shared_ddc) { | |
| 887 | + amdgpu_connector_free_edid(connector); | |
| 888 | + ret = connector_status_disconnected; | |
| 889 | + } else { | |
| 890 | + ret = connector_status_connected; | |
| 891 | + } | |
| 892 | + } | |
| 893 | + } else { | |
| 894 | + | |
| 895 | + /* if we aren't forcing don't do destructive polling */ | |
| 896 | + if (!force) { | |
| 897 | + /* only return the previous status if we last | |
| 898 | + * detected a monitor via load. | |
| 899 | + */ | |
| 900 | + if (amdgpu_connector->detected_by_load) | |
| 901 | + ret = connector->status; | |
| 902 | + goto out; | |
| 903 | + } | |
| 904 | + | |
| 905 | + if (amdgpu_connector->dac_load_detect && encoder) { | |
| 906 | + encoder_funcs = encoder->helper_private; | |
| 907 | + ret = encoder_funcs->detect(encoder, connector); | |
| 908 | + if (ret != connector_status_disconnected) | |
| 909 | + amdgpu_connector->detected_by_load = true; | |
| 910 | + } | |
| 911 | + } | |
| 912 | + | |
| 913 | + amdgpu_connector_update_scratch_regs(connector, ret); | |
| 914 | + | |
| 915 | +out: | |
| 916 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
| 917 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
| 918 | + | |
| 919 | + return ret; | |
| 920 | +} | |
| 921 | + | |
| 922 | +static const struct drm_connector_helper_funcs amdgpu_connector_vga_helper_funcs = { | |
| 923 | + .get_modes = amdgpu_connector_vga_get_modes, | |
| 924 | + .mode_valid = amdgpu_connector_vga_mode_valid, | |
| 925 | + .best_encoder = amdgpu_connector_best_single_encoder, | |
| 926 | +}; | |
| 927 | + | |
| 928 | +static const struct drm_connector_funcs amdgpu_connector_vga_funcs = { | |
| 929 | + .dpms = drm_helper_connector_dpms, | |
| 930 | + .detect = amdgpu_connector_vga_detect, | |
| 931 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
| 932 | + .destroy = amdgpu_connector_destroy, | |
| 933 | + .set_property = amdgpu_connector_set_property, | |
| 934 | +}; | |
| 935 | + | |
| 936 | +static bool | |
| 937 | +amdgpu_connector_check_hpd_status_unchanged(struct drm_connector *connector) | |
| 938 | +{ | |
| 939 | + struct drm_device *dev = connector->dev; | |
| 940 | + struct amdgpu_device *adev = dev->dev_private; | |
| 941 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 942 | + enum drm_connector_status status; | |
| 943 | + | |
| 944 | + if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE) { | |
| 945 | + if (amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) | |
| 946 | + status = connector_status_connected; | |
| 947 | + else | |
| 948 | + status = connector_status_disconnected; | |
| 949 | + if (connector->status == status) | |
| 950 | + return true; | |
| 951 | + } | |
| 952 | + | |
| 953 | + return false; | |
| 954 | +} | |
| 955 | + | |
| 956 | +/* | |
| 957 | + * DVI is complicated | |
| 958 | + * Do a DDC probe, if DDC probe passes, get the full EDID so | |
| 959 | + * we can do analog/digital monitor detection at this point. | |
| 960 | + * If the monitor is an analog monitor or we got no DDC, | |
| 961 | + * we need to find the DAC encoder object for this connector. | |
| 962 | + * If we got no DDC, we do load detection on the DAC encoder object. | |
| 963 | + * If we got analog DDC or load detection passes on the DAC encoder | |
| 964 | + * we have to check if this analog encoder is shared with anyone else (TV) | |
| 965 | + * if its shared we have to set the other connector to disconnected. | |
| 966 | + */ | |
| 967 | +static enum drm_connector_status | |
| 968 | +amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) | |
| 969 | +{ | |
| 970 | + struct drm_device *dev = connector->dev; | |
| 971 | + struct amdgpu_device *adev = dev->dev_private; | |
| 972 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 973 | + struct drm_encoder *encoder = NULL; | |
| 974 | + const struct drm_encoder_helper_funcs *encoder_funcs; | |
| 975 | + int i, r; | |
| 976 | + enum drm_connector_status ret = connector_status_disconnected; | |
| 977 | + bool dret = false, broken_edid = false; | |
| 978 | + | |
| 979 | + r = pm_runtime_get_sync(connector->dev->dev); | |
| 980 | + if (r < 0) | |
| 981 | + return connector_status_disconnected; | |
| 982 | + | |
| 983 | + if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { | |
| 984 | + ret = connector->status; | |
| 985 | + goto exit; | |
| 986 | + } | |
| 987 | + | |
| 988 | + if (amdgpu_connector->ddc_bus) | |
| 989 | + dret = amdgpu_ddc_probe(amdgpu_connector, false); | |
| 990 | + if (dret) { | |
| 991 | + amdgpu_connector->detected_by_load = false; | |
| 992 | + amdgpu_connector_free_edid(connector); | |
| 993 | + amdgpu_connector_get_edid(connector); | |
| 994 | + | |
| 995 | + if (!amdgpu_connector->edid) { | |
| 996 | + DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", | |
| 997 | + connector->name); | |
| 998 | + ret = connector_status_connected; | |
| 999 | + broken_edid = true; /* defer use_digital to later */ | |
| 1000 | + } else { | |
| 1001 | + amdgpu_connector->use_digital = | |
| 1002 | + !!(amdgpu_connector->edid->input & DRM_EDID_INPUT_DIGITAL); | |
| 1003 | + | |
| 1004 | + /* some oems have boards with separate digital and analog connectors | |
| 1005 | + * with a shared ddc line (often vga + hdmi) | |
| 1006 | + */ | |
| 1007 | + if ((!amdgpu_connector->use_digital) && amdgpu_connector->shared_ddc) { | |
| 1008 | + amdgpu_connector_free_edid(connector); | |
| 1009 | + ret = connector_status_disconnected; | |
| 1010 | + } else { | |
| 1011 | + ret = connector_status_connected; | |
| 1012 | + } | |
| 1013 | + | |
| 1014 | + /* This gets complicated. We have boards with VGA + HDMI with a | |
| 1015 | + * shared DDC line and we have boards with DVI-D + HDMI with a shared | |
| 1016 | + * DDC line. The latter is more complex because with DVI<->HDMI adapters | |
| 1017 | + * you don't really know what's connected to which port as both are digital. | |
| 1018 | + */ | |
| 1019 | + if (amdgpu_connector->shared_ddc && (ret == connector_status_connected)) { | |
| 1020 | + struct drm_connector *list_connector; | |
| 1021 | + struct amdgpu_connector *list_amdgpu_connector; | |
| 1022 | + list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { | |
| 1023 | + if (connector == list_connector) | |
| 1024 | + continue; | |
| 1025 | + list_amdgpu_connector = to_amdgpu_connector(list_connector); | |
| 1026 | + if (list_amdgpu_connector->shared_ddc && | |
| 1027 | + (list_amdgpu_connector->ddc_bus->rec.i2c_id == | |
| 1028 | + amdgpu_connector->ddc_bus->rec.i2c_id)) { | |
| 1029 | + /* cases where both connectors are digital */ | |
| 1030 | + if (list_connector->connector_type != DRM_MODE_CONNECTOR_VGA) { | |
| 1031 | + /* hpd is our only option in this case */ | |
| 1032 | + if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { | |
| 1033 | + amdgpu_connector_free_edid(connector); | |
| 1034 | + ret = connector_status_disconnected; | |
| 1035 | + } | |
| 1036 | + } | |
| 1037 | + } | |
| 1038 | + } | |
| 1039 | + } | |
| 1040 | + } | |
| 1041 | + } | |
| 1042 | + | |
| 1043 | + if ((ret == connector_status_connected) && (amdgpu_connector->use_digital == true)) | |
| 1044 | + goto out; | |
| 1045 | + | |
| 1046 | + /* DVI-D and HDMI-A are digital only */ | |
| 1047 | + if ((connector->connector_type == DRM_MODE_CONNECTOR_DVID) || | |
| 1048 | + (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)) | |
| 1049 | + goto out; | |
| 1050 | + | |
| 1051 | + /* if we aren't forcing don't do destructive polling */ | |
| 1052 | + if (!force) { | |
| 1053 | + /* only return the previous status if we last | |
| 1054 | + * detected a monitor via load. | |
| 1055 | + */ | |
| 1056 | + if (amdgpu_connector->detected_by_load) | |
| 1057 | + ret = connector->status; | |
| 1058 | + goto out; | |
| 1059 | + } | |
| 1060 | + | |
| 1061 | + /* find analog encoder */ | |
| 1062 | + if (amdgpu_connector->dac_load_detect) { | |
| 1063 | + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | |
| 1064 | + if (connector->encoder_ids[i] == 0) | |
| 1065 | + break; | |
| 1066 | + | |
| 1067 | + encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); | |
| 1068 | + if (!encoder) | |
| 1069 | + continue; | |
| 1070 | + | |
| 1071 | + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && | |
| 1072 | + encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) | |
| 1073 | + continue; | |
| 1074 | + | |
| 1075 | + encoder_funcs = encoder->helper_private; | |
| 1076 | + if (encoder_funcs->detect) { | |
| 1077 | + if (!broken_edid) { | |
| 1078 | + if (ret != connector_status_connected) { | |
| 1079 | + /* deal with analog monitors without DDC */ | |
| 1080 | + ret = encoder_funcs->detect(encoder, connector); | |
| 1081 | + if (ret == connector_status_connected) { | |
| 1082 | + amdgpu_connector->use_digital = false; | |
| 1083 | + } | |
| 1084 | + if (ret != connector_status_disconnected) | |
| 1085 | + amdgpu_connector->detected_by_load = true; | |
| 1086 | + } | |
| 1087 | + } else { | |
| 1088 | + enum drm_connector_status lret; | |
| 1089 | + /* assume digital unless load detected otherwise */ | |
| 1090 | + amdgpu_connector->use_digital = true; | |
| 1091 | + lret = encoder_funcs->detect(encoder, connector); | |
| 1092 | + DRM_DEBUG_KMS("load_detect %x returned: %x\n",encoder->encoder_type,lret); | |
| 1093 | + if (lret == connector_status_connected) | |
| 1094 | + amdgpu_connector->use_digital = false; | |
| 1095 | + } | |
| 1096 | + break; | |
| 1097 | + } | |
| 1098 | + } | |
| 1099 | + } | |
| 1100 | + | |
| 1101 | +out: | |
| 1102 | + /* updated in get modes as well since we need to know if it's analog or digital */ | |
| 1103 | + amdgpu_connector_update_scratch_regs(connector, ret); | |
| 1104 | + | |
| 1105 | +exit: | |
| 1106 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
| 1107 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
| 1108 | + | |
| 1109 | + return ret; | |
| 1110 | +} | |
| 1111 | + | |
| 1112 | +/* okay need to be smart in here about which encoder to pick */ | |
| 1113 | +static struct drm_encoder * | |
| 1114 | +amdgpu_connector_dvi_encoder(struct drm_connector *connector) | |
| 1115 | +{ | |
| 1116 | + int enc_id = connector->encoder_ids[0]; | |
| 1117 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 1118 | + struct drm_encoder *encoder; | |
| 1119 | + int i; | |
| 1120 | + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | |
| 1121 | + if (connector->encoder_ids[i] == 0) | |
| 1122 | + break; | |
| 1123 | + | |
| 1124 | + encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); | |
| 1125 | + if (!encoder) | |
| 1126 | + continue; | |
| 1127 | + | |
| 1128 | + if (amdgpu_connector->use_digital == true) { | |
| 1129 | + if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) | |
| 1130 | + return encoder; | |
| 1131 | + } else { | |
| 1132 | + if (encoder->encoder_type == DRM_MODE_ENCODER_DAC || | |
| 1133 | + encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) | |
| 1134 | + return encoder; | |
| 1135 | + } | |
| 1136 | + } | |
| 1137 | + | |
| 1138 | + /* see if we have a default encoder TODO */ | |
| 1139 | + | |
| 1140 | + /* then check use digitial */ | |
| 1141 | + /* pick the first one */ | |
| 1142 | + if (enc_id) | |
| 1143 | + return drm_encoder_find(connector->dev, enc_id); | |
| 1144 | + return NULL; | |
| 1145 | +} | |
| 1146 | + | |
| 1147 | +static void amdgpu_connector_dvi_force(struct drm_connector *connector) | |
| 1148 | +{ | |
| 1149 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 1150 | + if (connector->force == DRM_FORCE_ON) | |
| 1151 | + amdgpu_connector->use_digital = false; | |
| 1152 | + if (connector->force == DRM_FORCE_ON_DIGITAL) | |
| 1153 | + amdgpu_connector->use_digital = true; | |
| 1154 | +} | |
| 1155 | + | |
| 1156 | +static int amdgpu_connector_dvi_mode_valid(struct drm_connector *connector, | |
| 1157 | + struct drm_display_mode *mode) | |
| 1158 | +{ | |
| 1159 | + struct drm_device *dev = connector->dev; | |
| 1160 | + struct amdgpu_device *adev = dev->dev_private; | |
| 1161 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 1162 | + | |
| 1163 | + /* XXX check mode bandwidth */ | |
| 1164 | + | |
| 1165 | + if (amdgpu_connector->use_digital && (mode->clock > 165000)) { | |
| 1166 | + if ((amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) || | |
| 1167 | + (amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || | |
| 1168 | + (amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) { | |
| 1169 | + return MODE_OK; | |
| 1170 | + } else if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 1171 | + /* HDMI 1.3+ supports max clock of 340 Mhz */ | |
| 1172 | + if (mode->clock > 340000) | |
| 1173 | + return MODE_CLOCK_HIGH; | |
| 1174 | + else | |
| 1175 | + return MODE_OK; | |
| 1176 | + } else { | |
| 1177 | + return MODE_CLOCK_HIGH; | |
| 1178 | + } | |
| 1179 | + } | |
| 1180 | + | |
| 1181 | + /* check against the max pixel clock */ | |
| 1182 | + if ((mode->clock / 10) > adev->clock.max_pixel_clock) | |
| 1183 | + return MODE_CLOCK_HIGH; | |
| 1184 | + | |
| 1185 | + return MODE_OK; | |
| 1186 | +} | |
| 1187 | + | |
| 1188 | +static const struct drm_connector_helper_funcs amdgpu_connector_dvi_helper_funcs = { | |
| 1189 | + .get_modes = amdgpu_connector_vga_get_modes, | |
| 1190 | + .mode_valid = amdgpu_connector_dvi_mode_valid, | |
| 1191 | + .best_encoder = amdgpu_connector_dvi_encoder, | |
| 1192 | +}; | |
| 1193 | + | |
| 1194 | +static const struct drm_connector_funcs amdgpu_connector_dvi_funcs = { | |
| 1195 | + .dpms = drm_helper_connector_dpms, | |
| 1196 | + .detect = amdgpu_connector_dvi_detect, | |
| 1197 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
| 1198 | + .set_property = amdgpu_connector_set_property, | |
| 1199 | + .destroy = amdgpu_connector_destroy, | |
| 1200 | + .force = amdgpu_connector_dvi_force, | |
| 1201 | +}; | |
| 1202 | + | |
| 1203 | +static int amdgpu_connector_dp_get_modes(struct drm_connector *connector) | |
| 1204 | +{ | |
| 1205 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 1206 | + struct amdgpu_connector_atom_dig *amdgpu_dig_connector = amdgpu_connector->con_priv; | |
| 1207 | + struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
| 1208 | + int ret; | |
| 1209 | + | |
| 1210 | + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || | |
| 1211 | + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { | |
| 1212 | + struct drm_display_mode *mode; | |
| 1213 | + | |
| 1214 | + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { | |
| 1215 | + if (!amdgpu_dig_connector->edp_on) | |
| 1216 | + amdgpu_atombios_encoder_set_edp_panel_power(connector, | |
| 1217 | + ATOM_TRANSMITTER_ACTION_POWER_ON); | |
| 1218 | + amdgpu_connector_get_edid(connector); | |
| 1219 | + ret = amdgpu_connector_ddc_get_modes(connector); | |
| 1220 | + if (!amdgpu_dig_connector->edp_on) | |
| 1221 | + amdgpu_atombios_encoder_set_edp_panel_power(connector, | |
| 1222 | + ATOM_TRANSMITTER_ACTION_POWER_OFF); | |
| 1223 | + } else { | |
| 1224 | + /* need to setup ddc on the bridge */ | |
| 1225 | + if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != | |
| 1226 | + ENCODER_OBJECT_ID_NONE) { | |
| 1227 | + if (encoder) | |
| 1228 | + amdgpu_atombios_encoder_setup_ext_encoder_ddc(encoder); | |
| 1229 | + } | |
| 1230 | + amdgpu_connector_get_edid(connector); | |
| 1231 | + ret = amdgpu_connector_ddc_get_modes(connector); | |
| 1232 | + } | |
| 1233 | + | |
| 1234 | + if (ret > 0) { | |
| 1235 | + if (encoder) { | |
| 1236 | + amdgpu_connector_fixup_lcd_native_mode(encoder, connector); | |
| 1237 | + /* add scaled modes */ | |
| 1238 | + amdgpu_connector_add_common_modes(encoder, connector); | |
| 1239 | + } | |
| 1240 | + return ret; | |
| 1241 | + } | |
| 1242 | + | |
| 1243 | + if (!encoder) | |
| 1244 | + return 0; | |
| 1245 | + | |
| 1246 | + /* we have no EDID modes */ | |
| 1247 | + mode = amdgpu_connector_lcd_native_mode(encoder); | |
| 1248 | + if (mode) { | |
| 1249 | + ret = 1; | |
| 1250 | + drm_mode_probed_add(connector, mode); | |
| 1251 | + /* add the width/height from vbios tables if available */ | |
| 1252 | + connector->display_info.width_mm = mode->width_mm; | |
| 1253 | + connector->display_info.height_mm = mode->height_mm; | |
| 1254 | + /* add scaled modes */ | |
| 1255 | + amdgpu_connector_add_common_modes(encoder, connector); | |
| 1256 | + } | |
| 1257 | + } else { | |
| 1258 | + /* need to setup ddc on the bridge */ | |
| 1259 | + if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != | |
| 1260 | + ENCODER_OBJECT_ID_NONE) { | |
| 1261 | + if (encoder) | |
| 1262 | + amdgpu_atombios_encoder_setup_ext_encoder_ddc(encoder); | |
| 1263 | + } | |
| 1264 | + amdgpu_connector_get_edid(connector); | |
| 1265 | + ret = amdgpu_connector_ddc_get_modes(connector); | |
| 1266 | + | |
| 1267 | + amdgpu_get_native_mode(connector); | |
| 1268 | + } | |
| 1269 | + | |
| 1270 | + return ret; | |
| 1271 | +} | |
| 1272 | + | |
| 1273 | +u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector) | |
| 1274 | +{ | |
| 1275 | + struct drm_encoder *encoder; | |
| 1276 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 1277 | + int i; | |
| 1278 | + | |
| 1279 | + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | |
| 1280 | + if (connector->encoder_ids[i] == 0) | |
| 1281 | + break; | |
| 1282 | + | |
| 1283 | + encoder = drm_encoder_find(connector->dev, | |
| 1284 | + connector->encoder_ids[i]); | |
| 1285 | + if (!encoder) | |
| 1286 | + continue; | |
| 1287 | + | |
| 1288 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 1289 | + | |
| 1290 | + switch (amdgpu_encoder->encoder_id) { | |
| 1291 | + case ENCODER_OBJECT_ID_TRAVIS: | |
| 1292 | + case ENCODER_OBJECT_ID_NUTMEG: | |
| 1293 | + return amdgpu_encoder->encoder_id; | |
| 1294 | + default: | |
| 1295 | + break; | |
| 1296 | + } | |
| 1297 | + } | |
| 1298 | + | |
| 1299 | + return ENCODER_OBJECT_ID_NONE; | |
| 1300 | +} | |
| 1301 | + | |
| 1302 | +static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector) | |
| 1303 | +{ | |
| 1304 | + struct drm_encoder *encoder; | |
| 1305 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 1306 | + int i; | |
| 1307 | + bool found = false; | |
| 1308 | + | |
| 1309 | + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | |
| 1310 | + if (connector->encoder_ids[i] == 0) | |
| 1311 | + break; | |
| 1312 | + encoder = drm_encoder_find(connector->dev, | |
| 1313 | + connector->encoder_ids[i]); | |
| 1314 | + if (!encoder) | |
| 1315 | + continue; | |
| 1316 | + | |
| 1317 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 1318 | + if (amdgpu_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) | |
| 1319 | + found = true; | |
| 1320 | + } | |
| 1321 | + | |
| 1322 | + return found; | |
| 1323 | +} | |
| 1324 | + | |
| 1325 | +bool amdgpu_connector_is_dp12_capable(struct drm_connector *connector) | |
| 1326 | +{ | |
| 1327 | + struct drm_device *dev = connector->dev; | |
| 1328 | + struct amdgpu_device *adev = dev->dev_private; | |
| 1329 | + | |
| 1330 | + if ((adev->clock.default_dispclk >= 53900) && | |
| 1331 | + amdgpu_connector_encoder_is_hbr2(connector)) { | |
| 1332 | + return true; | |
| 1333 | + } | |
| 1334 | + | |
| 1335 | + return false; | |
| 1336 | +} | |
| 1337 | + | |
| 1338 | +static enum drm_connector_status | |
| 1339 | +amdgpu_connector_dp_detect(struct drm_connector *connector, bool force) | |
| 1340 | +{ | |
| 1341 | + struct drm_device *dev = connector->dev; | |
| 1342 | + struct amdgpu_device *adev = dev->dev_private; | |
| 1343 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 1344 | + enum drm_connector_status ret = connector_status_disconnected; | |
| 1345 | + struct amdgpu_connector_atom_dig *amdgpu_dig_connector = amdgpu_connector->con_priv; | |
| 1346 | + struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
| 1347 | + int r; | |
| 1348 | + | |
| 1349 | + r = pm_runtime_get_sync(connector->dev->dev); | |
| 1350 | + if (r < 0) | |
| 1351 | + return connector_status_disconnected; | |
| 1352 | + | |
| 1353 | + if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { | |
| 1354 | + ret = connector->status; | |
| 1355 | + goto out; | |
| 1356 | + } | |
| 1357 | + | |
| 1358 | + amdgpu_connector_free_edid(connector); | |
| 1359 | + | |
| 1360 | + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || | |
| 1361 | + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { | |
| 1362 | + if (encoder) { | |
| 1363 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 1364 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 1365 | + | |
| 1366 | + /* check if panel is valid */ | |
| 1367 | + if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) | |
| 1368 | + ret = connector_status_connected; | |
| 1369 | + } | |
| 1370 | + /* eDP is always DP */ | |
| 1371 | + amdgpu_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; | |
| 1372 | + if (!amdgpu_dig_connector->edp_on) | |
| 1373 | + amdgpu_atombios_encoder_set_edp_panel_power(connector, | |
| 1374 | + ATOM_TRANSMITTER_ACTION_POWER_ON); | |
| 1375 | + if (!amdgpu_atombios_dp_get_dpcd(amdgpu_connector)) | |
| 1376 | + ret = connector_status_connected; | |
| 1377 | + if (!amdgpu_dig_connector->edp_on) | |
| 1378 | + amdgpu_atombios_encoder_set_edp_panel_power(connector, | |
| 1379 | + ATOM_TRANSMITTER_ACTION_POWER_OFF); | |
| 1380 | + } else if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != | |
| 1381 | + ENCODER_OBJECT_ID_NONE) { | |
| 1382 | + /* DP bridges are always DP */ | |
| 1383 | + amdgpu_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; | |
| 1384 | + /* get the DPCD from the bridge */ | |
| 1385 | + amdgpu_atombios_dp_get_dpcd(amdgpu_connector); | |
| 1386 | + | |
| 1387 | + if (encoder) { | |
| 1388 | + /* setup ddc on the bridge */ | |
| 1389 | + amdgpu_atombios_encoder_setup_ext_encoder_ddc(encoder); | |
| 1390 | + /* bridge chips are always aux */ | |
| 1391 | + if (amdgpu_ddc_probe(amdgpu_connector, true)) /* try DDC */ | |
| 1392 | + ret = connector_status_connected; | |
| 1393 | + else if (amdgpu_connector->dac_load_detect) { /* try load detection */ | |
| 1394 | + const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; | |
| 1395 | + ret = encoder_funcs->detect(encoder, connector); | |
| 1396 | + } | |
| 1397 | + } | |
| 1398 | + } else { | |
| 1399 | + amdgpu_dig_connector->dp_sink_type = | |
| 1400 | + amdgpu_atombios_dp_get_sinktype(amdgpu_connector); | |
| 1401 | + if (amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { | |
| 1402 | + ret = connector_status_connected; | |
| 1403 | + if (amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) | |
| 1404 | + amdgpu_atombios_dp_get_dpcd(amdgpu_connector); | |
| 1405 | + } else { | |
| 1406 | + if (amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { | |
| 1407 | + if (!amdgpu_atombios_dp_get_dpcd(amdgpu_connector)) | |
| 1408 | + ret = connector_status_connected; | |
| 1409 | + } else { | |
| 1410 | + /* try non-aux ddc (DP to DVI/HDMI/etc. adapter) */ | |
| 1411 | + if (amdgpu_ddc_probe(amdgpu_connector, false)) | |
| 1412 | + ret = connector_status_connected; | |
| 1413 | + } | |
| 1414 | + } | |
| 1415 | + } | |
| 1416 | + | |
| 1417 | + amdgpu_connector_update_scratch_regs(connector, ret); | |
| 1418 | +out: | |
| 1419 | + pm_runtime_mark_last_busy(connector->dev->dev); | |
| 1420 | + pm_runtime_put_autosuspend(connector->dev->dev); | |
| 1421 | + | |
| 1422 | + return ret; | |
| 1423 | +} | |
| 1424 | + | |
| 1425 | +static int amdgpu_connector_dp_mode_valid(struct drm_connector *connector, | |
| 1426 | + struct drm_display_mode *mode) | |
| 1427 | +{ | |
| 1428 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 1429 | + struct amdgpu_connector_atom_dig *amdgpu_dig_connector = amdgpu_connector->con_priv; | |
| 1430 | + | |
| 1431 | + /* XXX check mode bandwidth */ | |
| 1432 | + | |
| 1433 | + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || | |
| 1434 | + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { | |
| 1435 | + struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); | |
| 1436 | + | |
| 1437 | + if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) | |
| 1438 | + return MODE_PANEL; | |
| 1439 | + | |
| 1440 | + if (encoder) { | |
| 1441 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 1442 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 1443 | + | |
| 1444 | + /* AVIVO hardware supports downscaling modes larger than the panel | |
| 1445 | + * to the panel size, but I'm not sure this is desirable. | |
| 1446 | + */ | |
| 1447 | + if ((mode->hdisplay > native_mode->hdisplay) || | |
| 1448 | + (mode->vdisplay > native_mode->vdisplay)) | |
| 1449 | + return MODE_PANEL; | |
| 1450 | + | |
| 1451 | + /* if scaling is disabled, block non-native modes */ | |
| 1452 | + if (amdgpu_encoder->rmx_type == RMX_OFF) { | |
| 1453 | + if ((mode->hdisplay != native_mode->hdisplay) || | |
| 1454 | + (mode->vdisplay != native_mode->vdisplay)) | |
| 1455 | + return MODE_PANEL; | |
| 1456 | + } | |
| 1457 | + } | |
| 1458 | + return MODE_OK; | |
| 1459 | + } else { | |
| 1460 | + if ((amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || | |
| 1461 | + (amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { | |
| 1462 | + return amdgpu_atombios_dp_mode_valid_helper(connector, mode); | |
| 1463 | + } else { | |
| 1464 | + if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 1465 | + /* HDMI 1.3+ supports max clock of 340 Mhz */ | |
| 1466 | + if (mode->clock > 340000) | |
| 1467 | + return MODE_CLOCK_HIGH; | |
| 1468 | + } else { | |
| 1469 | + if (mode->clock > 165000) | |
| 1470 | + return MODE_CLOCK_HIGH; | |
| 1471 | + } | |
| 1472 | + } | |
| 1473 | + } | |
| 1474 | + | |
| 1475 | + return MODE_OK; | |
| 1476 | +} | |
| 1477 | + | |
| 1478 | +static const struct drm_connector_helper_funcs amdgpu_connector_dp_helper_funcs = { | |
| 1479 | + .get_modes = amdgpu_connector_dp_get_modes, | |
| 1480 | + .mode_valid = amdgpu_connector_dp_mode_valid, | |
| 1481 | + .best_encoder = amdgpu_connector_dvi_encoder, | |
| 1482 | +}; | |
| 1483 | + | |
| 1484 | +static const struct drm_connector_funcs amdgpu_connector_dp_funcs = { | |
| 1485 | + .dpms = drm_helper_connector_dpms, | |
| 1486 | + .detect = amdgpu_connector_dp_detect, | |
| 1487 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
| 1488 | + .set_property = amdgpu_connector_set_property, | |
| 1489 | + .destroy = amdgpu_connector_destroy, | |
| 1490 | + .force = amdgpu_connector_dvi_force, | |
| 1491 | +}; | |
| 1492 | + | |
| 1493 | +static const struct drm_connector_funcs amdgpu_connector_edp_funcs = { | |
| 1494 | + .dpms = drm_helper_connector_dpms, | |
| 1495 | + .detect = amdgpu_connector_dp_detect, | |
| 1496 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
| 1497 | + .set_property = amdgpu_connector_set_lcd_property, | |
| 1498 | + .destroy = amdgpu_connector_destroy, | |
| 1499 | + .force = amdgpu_connector_dvi_force, | |
| 1500 | +}; | |
| 1501 | + | |
| 1502 | +void | |
| 1503 | +amdgpu_connector_add(struct amdgpu_device *adev, | |
| 1504 | + uint32_t connector_id, | |
| 1505 | + uint32_t supported_device, | |
| 1506 | + int connector_type, | |
| 1507 | + struct amdgpu_i2c_bus_rec *i2c_bus, | |
| 1508 | + uint16_t connector_object_id, | |
| 1509 | + struct amdgpu_hpd *hpd, | |
| 1510 | + struct amdgpu_router *router) | |
| 1511 | +{ | |
| 1512 | + struct drm_device *dev = adev->ddev; | |
| 1513 | + struct drm_connector *connector; | |
| 1514 | + struct amdgpu_connector *amdgpu_connector; | |
| 1515 | + struct amdgpu_connector_atom_dig *amdgpu_dig_connector; | |
| 1516 | + struct drm_encoder *encoder; | |
| 1517 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 1518 | + uint32_t subpixel_order = SubPixelNone; | |
| 1519 | + bool shared_ddc = false; | |
| 1520 | + bool is_dp_bridge = false; | |
| 1521 | + bool has_aux = false; | |
| 1522 | + | |
| 1523 | + if (connector_type == DRM_MODE_CONNECTOR_Unknown) | |
| 1524 | + return; | |
| 1525 | + | |
| 1526 | + /* see if we already added it */ | |
| 1527 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 1528 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 1529 | + if (amdgpu_connector->connector_id == connector_id) { | |
| 1530 | + amdgpu_connector->devices |= supported_device; | |
| 1531 | + return; | |
| 1532 | + } | |
| 1533 | + if (amdgpu_connector->ddc_bus && i2c_bus->valid) { | |
| 1534 | + if (amdgpu_connector->ddc_bus->rec.i2c_id == i2c_bus->i2c_id) { | |
| 1535 | + amdgpu_connector->shared_ddc = true; | |
| 1536 | + shared_ddc = true; | |
| 1537 | + } | |
| 1538 | + if (amdgpu_connector->router_bus && router->ddc_valid && | |
| 1539 | + (amdgpu_connector->router.router_id == router->router_id)) { | |
| 1540 | + amdgpu_connector->shared_ddc = false; | |
| 1541 | + shared_ddc = false; | |
| 1542 | + } | |
| 1543 | + } | |
| 1544 | + } | |
| 1545 | + | |
| 1546 | + /* check if it's a dp bridge */ | |
| 1547 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
| 1548 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 1549 | + if (amdgpu_encoder->devices & supported_device) { | |
| 1550 | + switch (amdgpu_encoder->encoder_id) { | |
| 1551 | + case ENCODER_OBJECT_ID_TRAVIS: | |
| 1552 | + case ENCODER_OBJECT_ID_NUTMEG: | |
| 1553 | + is_dp_bridge = true; | |
| 1554 | + break; | |
| 1555 | + default: | |
| 1556 | + break; | |
| 1557 | + } | |
| 1558 | + } | |
| 1559 | + } | |
| 1560 | + | |
| 1561 | + amdgpu_connector = kzalloc(sizeof(struct amdgpu_connector), GFP_KERNEL); | |
| 1562 | + if (!amdgpu_connector) | |
| 1563 | + return; | |
| 1564 | + | |
| 1565 | + connector = &amdgpu_connector->base; | |
| 1566 | + | |
| 1567 | + amdgpu_connector->connector_id = connector_id; | |
| 1568 | + amdgpu_connector->devices = supported_device; | |
| 1569 | + amdgpu_connector->shared_ddc = shared_ddc; | |
| 1570 | + amdgpu_connector->connector_object_id = connector_object_id; | |
| 1571 | + amdgpu_connector->hpd = *hpd; | |
| 1572 | + | |
| 1573 | + amdgpu_connector->router = *router; | |
| 1574 | + if (router->ddc_valid || router->cd_valid) { | |
| 1575 | + amdgpu_connector->router_bus = amdgpu_i2c_lookup(adev, &router->i2c_info); | |
| 1576 | + if (!amdgpu_connector->router_bus) | |
| 1577 | + DRM_ERROR("Failed to assign router i2c bus! Check dmesg for i2c errors.\n"); | |
| 1578 | + } | |
| 1579 | + | |
| 1580 | + if (is_dp_bridge) { | |
| 1581 | + amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); | |
| 1582 | + if (!amdgpu_dig_connector) | |
| 1583 | + goto failed; | |
| 1584 | + amdgpu_connector->con_priv = amdgpu_dig_connector; | |
| 1585 | + if (i2c_bus->valid) { | |
| 1586 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1587 | + if (amdgpu_connector->ddc_bus) | |
| 1588 | + has_aux = true; | |
| 1589 | + else | |
| 1590 | + DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1591 | + } | |
| 1592 | + switch (connector_type) { | |
| 1593 | + case DRM_MODE_CONNECTOR_VGA: | |
| 1594 | + case DRM_MODE_CONNECTOR_DVIA: | |
| 1595 | + default: | |
| 1596 | + drm_connector_init(dev, &amdgpu_connector->base, | |
| 1597 | + &amdgpu_connector_dp_funcs, connector_type); | |
| 1598 | + drm_connector_helper_add(&amdgpu_connector->base, | |
| 1599 | + &amdgpu_connector_dp_helper_funcs); | |
| 1600 | + connector->interlace_allowed = true; | |
| 1601 | + connector->doublescan_allowed = true; | |
| 1602 | + amdgpu_connector->dac_load_detect = true; | |
| 1603 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1604 | + adev->mode_info.load_detect_property, | |
| 1605 | + 1); | |
| 1606 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1607 | + dev->mode_config.scaling_mode_property, | |
| 1608 | + DRM_MODE_SCALE_NONE); | |
| 1609 | + break; | |
| 1610 | + case DRM_MODE_CONNECTOR_DVII: | |
| 1611 | + case DRM_MODE_CONNECTOR_DVID: | |
| 1612 | + case DRM_MODE_CONNECTOR_HDMIA: | |
| 1613 | + case DRM_MODE_CONNECTOR_HDMIB: | |
| 1614 | + case DRM_MODE_CONNECTOR_DisplayPort: | |
| 1615 | + drm_connector_init(dev, &amdgpu_connector->base, | |
| 1616 | + &amdgpu_connector_dp_funcs, connector_type); | |
| 1617 | + drm_connector_helper_add(&amdgpu_connector->base, | |
| 1618 | + &amdgpu_connector_dp_helper_funcs); | |
| 1619 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1620 | + adev->mode_info.underscan_property, | |
| 1621 | + UNDERSCAN_OFF); | |
| 1622 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1623 | + adev->mode_info.underscan_hborder_property, | |
| 1624 | + 0); | |
| 1625 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1626 | + adev->mode_info.underscan_vborder_property, | |
| 1627 | + 0); | |
| 1628 | + | |
| 1629 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1630 | + dev->mode_config.scaling_mode_property, | |
| 1631 | + DRM_MODE_SCALE_NONE); | |
| 1632 | + | |
| 1633 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1634 | + adev->mode_info.dither_property, | |
| 1635 | + AMDGPU_FMT_DITHER_DISABLE); | |
| 1636 | + | |
| 1637 | + if (amdgpu_audio != 0) | |
| 1638 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1639 | + adev->mode_info.audio_property, | |
| 1640 | + AMDGPU_AUDIO_AUTO); | |
| 1641 | + | |
| 1642 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1643 | + connector->interlace_allowed = true; | |
| 1644 | + if (connector_type == DRM_MODE_CONNECTOR_HDMIB) | |
| 1645 | + connector->doublescan_allowed = true; | |
| 1646 | + else | |
| 1647 | + connector->doublescan_allowed = false; | |
| 1648 | + if (connector_type == DRM_MODE_CONNECTOR_DVII) { | |
| 1649 | + amdgpu_connector->dac_load_detect = true; | |
| 1650 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1651 | + adev->mode_info.load_detect_property, | |
| 1652 | + 1); | |
| 1653 | + } | |
| 1654 | + break; | |
| 1655 | + case DRM_MODE_CONNECTOR_LVDS: | |
| 1656 | + case DRM_MODE_CONNECTOR_eDP: | |
| 1657 | + drm_connector_init(dev, &amdgpu_connector->base, | |
| 1658 | + &amdgpu_connector_edp_funcs, connector_type); | |
| 1659 | + drm_connector_helper_add(&amdgpu_connector->base, | |
| 1660 | + &amdgpu_connector_dp_helper_funcs); | |
| 1661 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1662 | + dev->mode_config.scaling_mode_property, | |
| 1663 | + DRM_MODE_SCALE_FULLSCREEN); | |
| 1664 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1665 | + connector->interlace_allowed = false; | |
| 1666 | + connector->doublescan_allowed = false; | |
| 1667 | + break; | |
| 1668 | + } | |
| 1669 | + } else { | |
| 1670 | + switch (connector_type) { | |
| 1671 | + case DRM_MODE_CONNECTOR_VGA: | |
| 1672 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_vga_funcs, connector_type); | |
| 1673 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_vga_helper_funcs); | |
| 1674 | + if (i2c_bus->valid) { | |
| 1675 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1676 | + if (!amdgpu_connector->ddc_bus) | |
| 1677 | + DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1678 | + } | |
| 1679 | + amdgpu_connector->dac_load_detect = true; | |
| 1680 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1681 | + adev->mode_info.load_detect_property, | |
| 1682 | + 1); | |
| 1683 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1684 | + dev->mode_config.scaling_mode_property, | |
| 1685 | + DRM_MODE_SCALE_NONE); | |
| 1686 | + /* no HPD on analog connectors */ | |
| 1687 | + amdgpu_connector->hpd.hpd = AMDGPU_HPD_NONE; | |
| 1688 | + connector->polled = DRM_CONNECTOR_POLL_CONNECT; | |
| 1689 | + connector->interlace_allowed = true; | |
| 1690 | + connector->doublescan_allowed = true; | |
| 1691 | + break; | |
| 1692 | + case DRM_MODE_CONNECTOR_DVIA: | |
| 1693 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_vga_funcs, connector_type); | |
| 1694 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_vga_helper_funcs); | |
| 1695 | + if (i2c_bus->valid) { | |
| 1696 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1697 | + if (!amdgpu_connector->ddc_bus) | |
| 1698 | + DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1699 | + } | |
| 1700 | + amdgpu_connector->dac_load_detect = true; | |
| 1701 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1702 | + adev->mode_info.load_detect_property, | |
| 1703 | + 1); | |
| 1704 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1705 | + dev->mode_config.scaling_mode_property, | |
| 1706 | + DRM_MODE_SCALE_NONE); | |
| 1707 | + /* no HPD on analog connectors */ | |
| 1708 | + amdgpu_connector->hpd.hpd = AMDGPU_HPD_NONE; | |
| 1709 | + connector->interlace_allowed = true; | |
| 1710 | + connector->doublescan_allowed = true; | |
| 1711 | + break; | |
| 1712 | + case DRM_MODE_CONNECTOR_DVII: | |
| 1713 | + case DRM_MODE_CONNECTOR_DVID: | |
| 1714 | + amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); | |
| 1715 | + if (!amdgpu_dig_connector) | |
| 1716 | + goto failed; | |
| 1717 | + amdgpu_connector->con_priv = amdgpu_dig_connector; | |
| 1718 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_dvi_funcs, connector_type); | |
| 1719 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dvi_helper_funcs); | |
| 1720 | + if (i2c_bus->valid) { | |
| 1721 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1722 | + if (!amdgpu_connector->ddc_bus) | |
| 1723 | + DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1724 | + } | |
| 1725 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1726 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1727 | + adev->mode_info.coherent_mode_property, | |
| 1728 | + 1); | |
| 1729 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1730 | + adev->mode_info.underscan_property, | |
| 1731 | + UNDERSCAN_OFF); | |
| 1732 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1733 | + adev->mode_info.underscan_hborder_property, | |
| 1734 | + 0); | |
| 1735 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1736 | + adev->mode_info.underscan_vborder_property, | |
| 1737 | + 0); | |
| 1738 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1739 | + dev->mode_config.scaling_mode_property, | |
| 1740 | + DRM_MODE_SCALE_NONE); | |
| 1741 | + | |
| 1742 | + if (amdgpu_audio != 0) { | |
| 1743 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1744 | + adev->mode_info.audio_property, | |
| 1745 | + AMDGPU_AUDIO_AUTO); | |
| 1746 | + } | |
| 1747 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1748 | + adev->mode_info.dither_property, | |
| 1749 | + AMDGPU_FMT_DITHER_DISABLE); | |
| 1750 | + if (connector_type == DRM_MODE_CONNECTOR_DVII) { | |
| 1751 | + amdgpu_connector->dac_load_detect = true; | |
| 1752 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1753 | + adev->mode_info.load_detect_property, | |
| 1754 | + 1); | |
| 1755 | + } | |
| 1756 | + connector->interlace_allowed = true; | |
| 1757 | + if (connector_type == DRM_MODE_CONNECTOR_DVII) | |
| 1758 | + connector->doublescan_allowed = true; | |
| 1759 | + else | |
| 1760 | + connector->doublescan_allowed = false; | |
| 1761 | + break; | |
| 1762 | + case DRM_MODE_CONNECTOR_HDMIA: | |
| 1763 | + case DRM_MODE_CONNECTOR_HDMIB: | |
| 1764 | + amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); | |
| 1765 | + if (!amdgpu_dig_connector) | |
| 1766 | + goto failed; | |
| 1767 | + amdgpu_connector->con_priv = amdgpu_dig_connector; | |
| 1768 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_dvi_funcs, connector_type); | |
| 1769 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dvi_helper_funcs); | |
| 1770 | + if (i2c_bus->valid) { | |
| 1771 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1772 | + if (!amdgpu_connector->ddc_bus) | |
| 1773 | + DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1774 | + } | |
| 1775 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1776 | + adev->mode_info.coherent_mode_property, | |
| 1777 | + 1); | |
| 1778 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1779 | + adev->mode_info.underscan_property, | |
| 1780 | + UNDERSCAN_OFF); | |
| 1781 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1782 | + adev->mode_info.underscan_hborder_property, | |
| 1783 | + 0); | |
| 1784 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1785 | + adev->mode_info.underscan_vborder_property, | |
| 1786 | + 0); | |
| 1787 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1788 | + dev->mode_config.scaling_mode_property, | |
| 1789 | + DRM_MODE_SCALE_NONE); | |
| 1790 | + if (amdgpu_audio != 0) { | |
| 1791 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1792 | + adev->mode_info.audio_property, | |
| 1793 | + AMDGPU_AUDIO_AUTO); | |
| 1794 | + } | |
| 1795 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1796 | + adev->mode_info.dither_property, | |
| 1797 | + AMDGPU_FMT_DITHER_DISABLE); | |
| 1798 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1799 | + connector->interlace_allowed = true; | |
| 1800 | + if (connector_type == DRM_MODE_CONNECTOR_HDMIB) | |
| 1801 | + connector->doublescan_allowed = true; | |
| 1802 | + else | |
| 1803 | + connector->doublescan_allowed = false; | |
| 1804 | + break; | |
| 1805 | + case DRM_MODE_CONNECTOR_DisplayPort: | |
| 1806 | + amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); | |
| 1807 | + if (!amdgpu_dig_connector) | |
| 1808 | + goto failed; | |
| 1809 | + amdgpu_connector->con_priv = amdgpu_dig_connector; | |
| 1810 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_dp_funcs, connector_type); | |
| 1811 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dp_helper_funcs); | |
| 1812 | + if (i2c_bus->valid) { | |
| 1813 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1814 | + if (amdgpu_connector->ddc_bus) | |
| 1815 | + has_aux = true; | |
| 1816 | + else | |
| 1817 | + DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1818 | + } | |
| 1819 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1820 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1821 | + adev->mode_info.coherent_mode_property, | |
| 1822 | + 1); | |
| 1823 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1824 | + adev->mode_info.underscan_property, | |
| 1825 | + UNDERSCAN_OFF); | |
| 1826 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1827 | + adev->mode_info.underscan_hborder_property, | |
| 1828 | + 0); | |
| 1829 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1830 | + adev->mode_info.underscan_vborder_property, | |
| 1831 | + 0); | |
| 1832 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1833 | + dev->mode_config.scaling_mode_property, | |
| 1834 | + DRM_MODE_SCALE_NONE); | |
| 1835 | + if (amdgpu_audio != 0) { | |
| 1836 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1837 | + adev->mode_info.audio_property, | |
| 1838 | + AMDGPU_AUDIO_AUTO); | |
| 1839 | + } | |
| 1840 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1841 | + adev->mode_info.dither_property, | |
| 1842 | + AMDGPU_FMT_DITHER_DISABLE); | |
| 1843 | + connector->interlace_allowed = true; | |
| 1844 | + /* in theory with a DP to VGA converter... */ | |
| 1845 | + connector->doublescan_allowed = false; | |
| 1846 | + break; | |
| 1847 | + case DRM_MODE_CONNECTOR_eDP: | |
| 1848 | + amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); | |
| 1849 | + if (!amdgpu_dig_connector) | |
| 1850 | + goto failed; | |
| 1851 | + amdgpu_connector->con_priv = amdgpu_dig_connector; | |
| 1852 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_edp_funcs, connector_type); | |
| 1853 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dp_helper_funcs); | |
| 1854 | + if (i2c_bus->valid) { | |
| 1855 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1856 | + if (amdgpu_connector->ddc_bus) | |
| 1857 | + has_aux = true; | |
| 1858 | + else | |
| 1859 | + DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1860 | + } | |
| 1861 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1862 | + dev->mode_config.scaling_mode_property, | |
| 1863 | + DRM_MODE_SCALE_FULLSCREEN); | |
| 1864 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1865 | + connector->interlace_allowed = false; | |
| 1866 | + connector->doublescan_allowed = false; | |
| 1867 | + break; | |
| 1868 | + case DRM_MODE_CONNECTOR_LVDS: | |
| 1869 | + amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); | |
| 1870 | + if (!amdgpu_dig_connector) | |
| 1871 | + goto failed; | |
| 1872 | + amdgpu_connector->con_priv = amdgpu_dig_connector; | |
| 1873 | + drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_lvds_funcs, connector_type); | |
| 1874 | + drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_lvds_helper_funcs); | |
| 1875 | + if (i2c_bus->valid) { | |
| 1876 | + amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); | |
| 1877 | + if (!amdgpu_connector->ddc_bus) | |
| 1878 | + DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); | |
| 1879 | + } | |
| 1880 | + drm_object_attach_property(&amdgpu_connector->base.base, | |
| 1881 | + dev->mode_config.scaling_mode_property, | |
| 1882 | + DRM_MODE_SCALE_FULLSCREEN); | |
| 1883 | + subpixel_order = SubPixelHorizontalRGB; | |
| 1884 | + connector->interlace_allowed = false; | |
| 1885 | + connector->doublescan_allowed = false; | |
| 1886 | + break; | |
| 1887 | + } | |
| 1888 | + } | |
| 1889 | + | |
| 1890 | + if (amdgpu_connector->hpd.hpd == AMDGPU_HPD_NONE) { | |
| 1891 | + if (i2c_bus->valid) | |
| 1892 | + connector->polled = DRM_CONNECTOR_POLL_CONNECT; | |
| 1893 | + } else | |
| 1894 | + connector->polled = DRM_CONNECTOR_POLL_HPD; | |
| 1895 | + | |
| 1896 | + connector->display_info.subpixel_order = subpixel_order; | |
| 1897 | + drm_connector_register(connector); | |
| 1898 | + | |
| 1899 | + if (has_aux) | |
| 1900 | + amdgpu_atombios_dp_aux_init(amdgpu_connector); | |
| 1901 | + | |
| 1902 | + return; | |
| 1903 | + | |
| 1904 | +failed: | |
| 1905 | + drm_connector_cleanup(connector); | |
| 1906 | + kfree(connector); | |
| 1907 | +} | |
| @@ -0,0 +1,42 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#ifndef __AMDGPU_CONNECTORS_H__ | |
| 25 | +#define __AMDGPU_CONNECTORS_H__ | |
| 26 | + | |
| 27 | +struct edid *amdgpu_connector_edid(struct drm_connector *connector); | |
| 28 | +void amdgpu_connector_hotplug(struct drm_connector *connector); | |
| 29 | +int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector); | |
| 30 | +u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector); | |
| 31 | +bool amdgpu_connector_is_dp12_capable(struct drm_connector *connector); | |
| 32 | +void | |
| 33 | +amdgpu_connector_add(struct amdgpu_device *adev, | |
| 34 | + uint32_t connector_id, | |
| 35 | + uint32_t supported_device, | |
| 36 | + int connector_type, | |
| 37 | + struct amdgpu_i2c_bus_rec *i2c_bus, | |
| 38 | + uint16_t connector_object_id, | |
| 39 | + struct amdgpu_hpd *hpd, | |
| 40 | + struct amdgpu_router *router); | |
| 41 | + | |
| 42 | +#endif | |
| @@ -0,0 +1,796 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Jerome Glisse. | |
| 3 | + * All Rights Reserved. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the "Software"), | |
| 7 | + * to deal in the Software without restriction, including without limitation | |
| 8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 10 | + * Software is furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice (including the next | |
| 13 | + * paragraph) shall be included in all copies or substantial portions of the | |
| 14 | + * Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 22 | + * DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: | |
| 25 | + * Jerome Glisse <glisse@freedesktop.org> | |
| 26 | + */ | |
| 27 | +#include <linux/list_sort.h> | |
| 28 | +#include <drm/drmP.h> | |
| 29 | +#include <drm/amdgpu_drm.h> | |
| 30 | +#include "amdgpu.h" | |
| 31 | +#include "amdgpu_trace.h" | |
| 32 | + | |
| 33 | +#define AMDGPU_CS_MAX_PRIORITY 32u | |
| 34 | +#define AMDGPU_CS_NUM_BUCKETS (AMDGPU_CS_MAX_PRIORITY + 1) | |
| 35 | + | |
| 36 | +/* This is based on the bucket sort with O(n) time complexity. | |
| 37 | + * An item with priority "i" is added to bucket[i]. The lists are then | |
| 38 | + * concatenated in descending order. | |
| 39 | + */ | |
| 40 | +struct amdgpu_cs_buckets { | |
| 41 | + struct list_head bucket[AMDGPU_CS_NUM_BUCKETS]; | |
| 42 | +}; | |
| 43 | + | |
| 44 | +static void amdgpu_cs_buckets_init(struct amdgpu_cs_buckets *b) | |
| 45 | +{ | |
| 46 | + unsigned i; | |
| 47 | + | |
| 48 | + for (i = 0; i < AMDGPU_CS_NUM_BUCKETS; i++) | |
| 49 | + INIT_LIST_HEAD(&b->bucket[i]); | |
| 50 | +} | |
| 51 | + | |
| 52 | +static void amdgpu_cs_buckets_add(struct amdgpu_cs_buckets *b, | |
| 53 | + struct list_head *item, unsigned priority) | |
| 54 | +{ | |
| 55 | + /* Since buffers which appear sooner in the relocation list are | |
| 56 | + * likely to be used more often than buffers which appear later | |
| 57 | + * in the list, the sort mustn't change the ordering of buffers | |
| 58 | + * with the same priority, i.e. it must be stable. | |
| 59 | + */ | |
| 60 | + list_add_tail(item, &b->bucket[min(priority, AMDGPU_CS_MAX_PRIORITY)]); | |
| 61 | +} | |
| 62 | + | |
| 63 | +static void amdgpu_cs_buckets_get_list(struct amdgpu_cs_buckets *b, | |
| 64 | + struct list_head *out_list) | |
| 65 | +{ | |
| 66 | + unsigned i; | |
| 67 | + | |
| 68 | + /* Connect the sorted buckets in the output list. */ | |
| 69 | + for (i = 0; i < AMDGPU_CS_NUM_BUCKETS; i++) { | |
| 70 | + list_splice(&b->bucket[i], out_list); | |
| 71 | + } | |
| 72 | +} | |
| 73 | + | |
| 74 | +int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type, | |
| 75 | + u32 ip_instance, u32 ring, | |
| 76 | + struct amdgpu_ring **out_ring) | |
| 77 | +{ | |
| 78 | + /* Right now all IPs have only one instance - multiple rings. */ | |
| 79 | + if (ip_instance != 0) { | |
| 80 | + DRM_ERROR("invalid ip instance: %d\n", ip_instance); | |
| 81 | + return -EINVAL; | |
| 82 | + } | |
| 83 | + | |
| 84 | + switch (ip_type) { | |
| 85 | + default: | |
| 86 | + DRM_ERROR("unknown ip type: %d\n", ip_type); | |
| 87 | + return -EINVAL; | |
| 88 | + case AMDGPU_HW_IP_GFX: | |
| 89 | + if (ring < adev->gfx.num_gfx_rings) { | |
| 90 | + *out_ring = &adev->gfx.gfx_ring[ring]; | |
| 91 | + } else { | |
| 92 | + DRM_ERROR("only %d gfx rings are supported now\n", | |
| 93 | + adev->gfx.num_gfx_rings); | |
| 94 | + return -EINVAL; | |
| 95 | + } | |
| 96 | + break; | |
| 97 | + case AMDGPU_HW_IP_COMPUTE: | |
| 98 | + if (ring < adev->gfx.num_compute_rings) { | |
| 99 | + *out_ring = &adev->gfx.compute_ring[ring]; | |
| 100 | + } else { | |
| 101 | + DRM_ERROR("only %d compute rings are supported now\n", | |
| 102 | + adev->gfx.num_compute_rings); | |
| 103 | + return -EINVAL; | |
| 104 | + } | |
| 105 | + break; | |
| 106 | + case AMDGPU_HW_IP_DMA: | |
| 107 | + if (ring < 2) { | |
| 108 | + *out_ring = &adev->sdma[ring].ring; | |
| 109 | + } else { | |
| 110 | + DRM_ERROR("only two SDMA rings are supported\n"); | |
| 111 | + return -EINVAL; | |
| 112 | + } | |
| 113 | + break; | |
| 114 | + case AMDGPU_HW_IP_UVD: | |
| 115 | + *out_ring = &adev->uvd.ring; | |
| 116 | + break; | |
| 117 | + case AMDGPU_HW_IP_VCE: | |
| 118 | + if (ring < 2){ | |
| 119 | + *out_ring = &adev->vce.ring[ring]; | |
| 120 | + } else { | |
| 121 | + DRM_ERROR("only two VCE rings are supported\n"); | |
| 122 | + return -EINVAL; | |
| 123 | + } | |
| 124 | + break; | |
| 125 | + } | |
| 126 | + return 0; | |
| 127 | +} | |
| 128 | + | |
| 129 | +int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) | |
| 130 | +{ | |
| 131 | + union drm_amdgpu_cs *cs = data; | |
| 132 | + uint64_t *chunk_array_user; | |
| 133 | + uint64_t *chunk_array = NULL; | |
| 134 | + struct amdgpu_fpriv *fpriv = p->filp->driver_priv; | |
| 135 | + unsigned size, i; | |
| 136 | + int r = 0; | |
| 137 | + | |
| 138 | + if (!cs->in.num_chunks) | |
| 139 | + goto out; | |
| 140 | + | |
| 141 | + p->ctx = amdgpu_ctx_get(fpriv, cs->in.ctx_id); | |
| 142 | + if (!p->ctx) { | |
| 143 | + r = -EINVAL; | |
| 144 | + goto out; | |
| 145 | + } | |
| 146 | + p->bo_list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle); | |
| 147 | + | |
| 148 | + /* get chunks */ | |
| 149 | + INIT_LIST_HEAD(&p->validated); | |
| 150 | + chunk_array = kcalloc(cs->in.num_chunks, sizeof(uint64_t), GFP_KERNEL); | |
| 151 | + if (chunk_array == NULL) { | |
| 152 | + r = -ENOMEM; | |
| 153 | + goto out; | |
| 154 | + } | |
| 155 | + | |
| 156 | + chunk_array_user = (uint64_t *)(unsigned long)(cs->in.chunks); | |
| 157 | + if (copy_from_user(chunk_array, chunk_array_user, | |
| 158 | + sizeof(uint64_t)*cs->in.num_chunks)) { | |
| 159 | + r = -EFAULT; | |
| 160 | + goto out; | |
| 161 | + } | |
| 162 | + | |
| 163 | + p->nchunks = cs->in.num_chunks; | |
| 164 | + p->chunks = kcalloc(p->nchunks, sizeof(struct amdgpu_cs_chunk), | |
| 165 | + GFP_KERNEL); | |
| 166 | + if (p->chunks == NULL) { | |
| 167 | + r = -ENOMEM; | |
| 168 | + goto out; | |
| 169 | + } | |
| 170 | + | |
| 171 | + for (i = 0; i < p->nchunks; i++) { | |
| 172 | + struct drm_amdgpu_cs_chunk __user **chunk_ptr = NULL; | |
| 173 | + struct drm_amdgpu_cs_chunk user_chunk; | |
| 174 | + uint32_t __user *cdata; | |
| 175 | + | |
| 176 | + chunk_ptr = (void __user *)(unsigned long)chunk_array[i]; | |
| 177 | + if (copy_from_user(&user_chunk, chunk_ptr, | |
| 178 | + sizeof(struct drm_amdgpu_cs_chunk))) { | |
| 179 | + r = -EFAULT; | |
| 180 | + goto out; | |
| 181 | + } | |
| 182 | + p->chunks[i].chunk_id = user_chunk.chunk_id; | |
| 183 | + p->chunks[i].length_dw = user_chunk.length_dw; | |
| 184 | + if (p->chunks[i].chunk_id == AMDGPU_CHUNK_ID_IB) | |
| 185 | + p->num_ibs++; | |
| 186 | + | |
| 187 | + size = p->chunks[i].length_dw; | |
| 188 | + cdata = (void __user *)(unsigned long)user_chunk.chunk_data; | |
| 189 | + p->chunks[i].user_ptr = cdata; | |
| 190 | + | |
| 191 | + p->chunks[i].kdata = drm_malloc_ab(size, sizeof(uint32_t)); | |
| 192 | + if (p->chunks[i].kdata == NULL) { | |
| 193 | + r = -ENOMEM; | |
| 194 | + goto out; | |
| 195 | + } | |
| 196 | + size *= sizeof(uint32_t); | |
| 197 | + if (copy_from_user(p->chunks[i].kdata, cdata, size)) { | |
| 198 | + r = -EFAULT; | |
| 199 | + goto out; | |
| 200 | + } | |
| 201 | + | |
| 202 | + if (p->chunks[i].chunk_id == AMDGPU_CHUNK_ID_FENCE) { | |
| 203 | + size = sizeof(struct drm_amdgpu_cs_chunk_fence); | |
| 204 | + if (p->chunks[i].length_dw * sizeof(uint32_t) >= size) { | |
| 205 | + uint32_t handle; | |
| 206 | + struct drm_gem_object *gobj; | |
| 207 | + struct drm_amdgpu_cs_chunk_fence *fence_data; | |
| 208 | + | |
| 209 | + fence_data = (void *)p->chunks[i].kdata; | |
| 210 | + handle = fence_data->handle; | |
| 211 | + gobj = drm_gem_object_lookup(p->adev->ddev, | |
| 212 | + p->filp, handle); | |
| 213 | + if (gobj == NULL) { | |
| 214 | + r = -EINVAL; | |
| 215 | + goto out; | |
| 216 | + } | |
| 217 | + | |
| 218 | + p->uf.bo = gem_to_amdgpu_bo(gobj); | |
| 219 | + p->uf.offset = fence_data->offset; | |
| 220 | + } else { | |
| 221 | + r = -EINVAL; | |
| 222 | + goto out; | |
| 223 | + } | |
| 224 | + } | |
| 225 | + } | |
| 226 | + | |
| 227 | + p->ibs = kcalloc(p->num_ibs, sizeof(struct amdgpu_ib), GFP_KERNEL); | |
| 228 | + if (!p->ibs) { | |
| 229 | + r = -ENOMEM; | |
| 230 | + goto out; | |
| 231 | + } | |
| 232 | + | |
| 233 | +out: | |
| 234 | + kfree(chunk_array); | |
| 235 | + return r; | |
| 236 | +} | |
| 237 | + | |
| 238 | +/* Returns how many bytes TTM can move per IB. | |
| 239 | + */ | |
| 240 | +static u64 amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev) | |
| 241 | +{ | |
| 242 | + u64 real_vram_size = adev->mc.real_vram_size; | |
| 243 | + u64 vram_usage = atomic64_read(&adev->vram_usage); | |
| 244 | + | |
| 245 | + /* This function is based on the current VRAM usage. | |
| 246 | + * | |
| 247 | + * - If all of VRAM is free, allow relocating the number of bytes that | |
| 248 | + * is equal to 1/4 of the size of VRAM for this IB. | |
| 249 | + | |
| 250 | + * - If more than one half of VRAM is occupied, only allow relocating | |
| 251 | + * 1 MB of data for this IB. | |
| 252 | + * | |
| 253 | + * - From 0 to one half of used VRAM, the threshold decreases | |
| 254 | + * linearly. | |
| 255 | + * __________________ | |
| 256 | + * 1/4 of -|\ | | |
| 257 | + * VRAM | \ | | |
| 258 | + * | \ | | |
| 259 | + * | \ | | |
| 260 | + * | \ | | |
| 261 | + * | \ | | |
| 262 | + * | \ | | |
| 263 | + * | \________|1 MB | |
| 264 | + * |----------------| | |
| 265 | + * VRAM 0 % 100 % | |
| 266 | + * used used | |
| 267 | + * | |
| 268 | + * Note: It's a threshold, not a limit. The threshold must be crossed | |
| 269 | + * for buffer relocations to stop, so any buffer of an arbitrary size | |
| 270 | + * can be moved as long as the threshold isn't crossed before | |
| 271 | + * the relocation takes place. We don't want to disable buffer | |
| 272 | + * relocations completely. | |
| 273 | + * | |
| 274 | + * The idea is that buffers should be placed in VRAM at creation time | |
| 275 | + * and TTM should only do a minimum number of relocations during | |
| 276 | + * command submission. In practice, you need to submit at least | |
| 277 | + * a dozen IBs to move all buffers to VRAM if they are in GTT. | |
| 278 | + * | |
| 279 | + * Also, things can get pretty crazy under memory pressure and actual | |
| 280 | + * VRAM usage can change a lot, so playing safe even at 50% does | |
| 281 | + * consistently increase performance. | |
| 282 | + */ | |
| 283 | + | |
| 284 | + u64 half_vram = real_vram_size >> 1; | |
| 285 | + u64 half_free_vram = vram_usage >= half_vram ? 0 : half_vram - vram_usage; | |
| 286 | + u64 bytes_moved_threshold = half_free_vram >> 1; | |
| 287 | + return max(bytes_moved_threshold, 1024*1024ull); | |
| 288 | +} | |
| 289 | + | |
| 290 | +int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p) | |
| 291 | +{ | |
| 292 | + struct amdgpu_fpriv *fpriv = p->filp->driver_priv; | |
| 293 | + struct amdgpu_vm *vm = &fpriv->vm; | |
| 294 | + struct amdgpu_device *adev = p->adev; | |
| 295 | + struct amdgpu_bo_list_entry *lobj; | |
| 296 | + struct list_head duplicates; | |
| 297 | + struct amdgpu_bo *bo; | |
| 298 | + u64 bytes_moved = 0, initial_bytes_moved; | |
| 299 | + u64 bytes_moved_threshold = amdgpu_cs_get_threshold_for_moves(adev); | |
| 300 | + int r; | |
| 301 | + | |
| 302 | + INIT_LIST_HEAD(&duplicates); | |
| 303 | + r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true, &duplicates); | |
| 304 | + if (unlikely(r != 0)) { | |
| 305 | + return r; | |
| 306 | + } | |
| 307 | + | |
| 308 | + list_for_each_entry(lobj, &p->validated, tv.head) { | |
| 309 | + bo = lobj->robj; | |
| 310 | + if (!bo->pin_count) { | |
| 311 | + u32 domain = lobj->prefered_domains; | |
| 312 | + u32 current_domain = | |
| 313 | + amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type); | |
| 314 | + | |
| 315 | + /* Check if this buffer will be moved and don't move it | |
| 316 | + * if we have moved too many buffers for this IB already. | |
| 317 | + * | |
| 318 | + * Note that this allows moving at least one buffer of | |
| 319 | + * any size, because it doesn't take the current "bo" | |
| 320 | + * into account. We don't want to disallow buffer moves | |
| 321 | + * completely. | |
| 322 | + */ | |
| 323 | + if (current_domain != AMDGPU_GEM_DOMAIN_CPU && | |
| 324 | + (domain & current_domain) == 0 && /* will be moved */ | |
| 325 | + bytes_moved > bytes_moved_threshold) { | |
| 326 | + /* don't move it */ | |
| 327 | + domain = current_domain; | |
| 328 | + } | |
| 329 | + | |
| 330 | + retry: | |
| 331 | + amdgpu_ttm_placement_from_domain(bo, domain); | |
| 332 | + initial_bytes_moved = atomic64_read(&adev->num_bytes_moved); | |
| 333 | + r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); | |
| 334 | + bytes_moved += atomic64_read(&adev->num_bytes_moved) - | |
| 335 | + initial_bytes_moved; | |
| 336 | + | |
| 337 | + if (unlikely(r)) { | |
| 338 | + if (r != -ERESTARTSYS && domain != lobj->allowed_domains) { | |
| 339 | + domain = lobj->allowed_domains; | |
| 340 | + goto retry; | |
| 341 | + } | |
| 342 | + ttm_eu_backoff_reservation(&p->ticket, &p->validated); | |
| 343 | + return r; | |
| 344 | + } | |
| 345 | + } | |
| 346 | + lobj->bo_va = amdgpu_vm_bo_find(vm, bo); | |
| 347 | + } | |
| 348 | + return 0; | |
| 349 | +} | |
| 350 | + | |
| 351 | +static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p) | |
| 352 | +{ | |
| 353 | + struct amdgpu_fpriv *fpriv = p->filp->driver_priv; | |
| 354 | + struct amdgpu_cs_buckets buckets; | |
| 355 | + bool need_mmap_lock = false; | |
| 356 | + int i, r; | |
| 357 | + | |
| 358 | + if (p->bo_list) { | |
| 359 | + need_mmap_lock = p->bo_list->has_userptr; | |
| 360 | + amdgpu_cs_buckets_init(&buckets); | |
| 361 | + for (i = 0; i < p->bo_list->num_entries; i++) | |
| 362 | + amdgpu_cs_buckets_add(&buckets, &p->bo_list->array[i].tv.head, | |
| 363 | + p->bo_list->array[i].priority); | |
| 364 | + | |
| 365 | + amdgpu_cs_buckets_get_list(&buckets, &p->validated); | |
| 366 | + } | |
| 367 | + | |
| 368 | + p->vm_bos = amdgpu_vm_get_bos(p->adev, &fpriv->vm, | |
| 369 | + &p->validated); | |
| 370 | + | |
| 371 | + if (need_mmap_lock) | |
| 372 | + down_read(¤t->mm->mmap_sem); | |
| 373 | + | |
| 374 | + r = amdgpu_cs_list_validate(p); | |
| 375 | + | |
| 376 | + if (need_mmap_lock) | |
| 377 | + up_read(¤t->mm->mmap_sem); | |
| 378 | + | |
| 379 | + return r; | |
| 380 | +} | |
| 381 | + | |
| 382 | +static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) | |
| 383 | +{ | |
| 384 | + struct amdgpu_bo_list_entry *e; | |
| 385 | + int r; | |
| 386 | + | |
| 387 | + list_for_each_entry(e, &p->validated, tv.head) { | |
| 388 | + struct reservation_object *resv = e->robj->tbo.resv; | |
| 389 | + r = amdgpu_sync_resv(p->adev, &p->ibs[0].sync, resv, p->filp); | |
| 390 | + | |
| 391 | + if (r) | |
| 392 | + return r; | |
| 393 | + } | |
| 394 | + return 0; | |
| 395 | +} | |
| 396 | + | |
| 397 | +static int cmp_size_smaller_first(void *priv, struct list_head *a, | |
| 398 | + struct list_head *b) | |
| 399 | +{ | |
| 400 | + struct amdgpu_bo_list_entry *la = list_entry(a, struct amdgpu_bo_list_entry, tv.head); | |
| 401 | + struct amdgpu_bo_list_entry *lb = list_entry(b, struct amdgpu_bo_list_entry, tv.head); | |
| 402 | + | |
| 403 | + /* Sort A before B if A is smaller. */ | |
| 404 | + return (int)la->robj->tbo.num_pages - (int)lb->robj->tbo.num_pages; | |
| 405 | +} | |
| 406 | + | |
| 407 | +/** | |
| 408 | + * cs_parser_fini() - clean parser states | |
| 409 | + * @parser: parser structure holding parsing context. | |
| 410 | + * @error: error number | |
| 411 | + * | |
| 412 | + * If error is set than unvalidate buffer, otherwise just free memory | |
| 413 | + * used by parsing context. | |
| 414 | + **/ | |
| 415 | +static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bool backoff) | |
| 416 | +{ | |
| 417 | + unsigned i; | |
| 418 | + | |
| 419 | + if (!error) { | |
| 420 | + /* Sort the buffer list from the smallest to largest buffer, | |
| 421 | + * which affects the order of buffers in the LRU list. | |
| 422 | + * This assures that the smallest buffers are added first | |
| 423 | + * to the LRU list, so they are likely to be later evicted | |
| 424 | + * first, instead of large buffers whose eviction is more | |
| 425 | + * expensive. | |
| 426 | + * | |
| 427 | + * This slightly lowers the number of bytes moved by TTM | |
| 428 | + * per frame under memory pressure. | |
| 429 | + */ | |
| 430 | + list_sort(NULL, &parser->validated, cmp_size_smaller_first); | |
| 431 | + | |
| 432 | + ttm_eu_fence_buffer_objects(&parser->ticket, | |
| 433 | + &parser->validated, | |
| 434 | + &parser->ibs[parser->num_ibs-1].fence->base); | |
| 435 | + } else if (backoff) { | |
| 436 | + ttm_eu_backoff_reservation(&parser->ticket, | |
| 437 | + &parser->validated); | |
| 438 | + } | |
| 439 | + | |
| 440 | + if (parser->ctx) | |
| 441 | + amdgpu_ctx_put(parser->ctx); | |
| 442 | + if (parser->bo_list) | |
| 443 | + amdgpu_bo_list_put(parser->bo_list); | |
| 444 | + drm_free_large(parser->vm_bos); | |
| 445 | + for (i = 0; i < parser->nchunks; i++) | |
| 446 | + drm_free_large(parser->chunks[i].kdata); | |
| 447 | + kfree(parser->chunks); | |
| 448 | + for (i = 0; i < parser->num_ibs; i++) | |
| 449 | + amdgpu_ib_free(parser->adev, &parser->ibs[i]); | |
| 450 | + kfree(parser->ibs); | |
| 451 | + if (parser->uf.bo) | |
| 452 | + drm_gem_object_unreference_unlocked(&parser->uf.bo->gem_base); | |
| 453 | +} | |
| 454 | + | |
| 455 | +static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p, | |
| 456 | + struct amdgpu_vm *vm) | |
| 457 | +{ | |
| 458 | + struct amdgpu_device *adev = p->adev; | |
| 459 | + struct amdgpu_bo_va *bo_va; | |
| 460 | + struct amdgpu_bo *bo; | |
| 461 | + int i, r; | |
| 462 | + | |
| 463 | + r = amdgpu_vm_update_page_directory(adev, vm); | |
| 464 | + if (r) | |
| 465 | + return r; | |
| 466 | + | |
| 467 | + r = amdgpu_vm_clear_freed(adev, vm); | |
| 468 | + if (r) | |
| 469 | + return r; | |
| 470 | + | |
| 471 | + if (p->bo_list) { | |
| 472 | + for (i = 0; i < p->bo_list->num_entries; i++) { | |
| 473 | + /* ignore duplicates */ | |
| 474 | + bo = p->bo_list->array[i].robj; | |
| 475 | + if (!bo) | |
| 476 | + continue; | |
| 477 | + | |
| 478 | + bo_va = p->bo_list->array[i].bo_va; | |
| 479 | + if (bo_va == NULL) | |
| 480 | + continue; | |
| 481 | + | |
| 482 | + r = amdgpu_vm_bo_update(adev, bo_va, &bo->tbo.mem); | |
| 483 | + if (r) | |
| 484 | + return r; | |
| 485 | + | |
| 486 | + amdgpu_sync_fence(&p->ibs[0].sync, bo_va->last_pt_update); | |
| 487 | + } | |
| 488 | + } | |
| 489 | + | |
| 490 | + return amdgpu_vm_clear_invalids(adev, vm, &p->ibs[0].sync); | |
| 491 | +} | |
| 492 | + | |
| 493 | +static int amdgpu_cs_ib_vm_chunk(struct amdgpu_device *adev, | |
| 494 | + struct amdgpu_cs_parser *parser) | |
| 495 | +{ | |
| 496 | + struct amdgpu_fpriv *fpriv = parser->filp->driver_priv; | |
| 497 | + struct amdgpu_vm *vm = &fpriv->vm; | |
| 498 | + struct amdgpu_ring *ring; | |
| 499 | + int i, r; | |
| 500 | + | |
| 501 | + if (parser->num_ibs == 0) | |
| 502 | + return 0; | |
| 503 | + | |
| 504 | + /* Only for UVD/VCE VM emulation */ | |
| 505 | + for (i = 0; i < parser->num_ibs; i++) { | |
| 506 | + ring = parser->ibs[i].ring; | |
| 507 | + if (ring->funcs->parse_cs) { | |
| 508 | + r = amdgpu_ring_parse_cs(ring, parser, i); | |
| 509 | + if (r) | |
| 510 | + return r; | |
| 511 | + } | |
| 512 | + } | |
| 513 | + | |
| 514 | + mutex_lock(&vm->mutex); | |
| 515 | + r = amdgpu_bo_vm_update_pte(parser, vm); | |
| 516 | + if (r) { | |
| 517 | + goto out; | |
| 518 | + } | |
| 519 | + amdgpu_cs_sync_rings(parser); | |
| 520 | + | |
| 521 | + r = amdgpu_ib_schedule(adev, parser->num_ibs, parser->ibs, | |
| 522 | + parser->filp); | |
| 523 | + | |
| 524 | +out: | |
| 525 | + mutex_unlock(&vm->mutex); | |
| 526 | + return r; | |
| 527 | +} | |
| 528 | + | |
| 529 | +static int amdgpu_cs_handle_lockup(struct amdgpu_device *adev, int r) | |
| 530 | +{ | |
| 531 | + if (r == -EDEADLK) { | |
| 532 | + r = amdgpu_gpu_reset(adev); | |
| 533 | + if (!r) | |
| 534 | + r = -EAGAIN; | |
| 535 | + } | |
| 536 | + return r; | |
| 537 | +} | |
| 538 | + | |
| 539 | +static int amdgpu_cs_ib_fill(struct amdgpu_device *adev, | |
| 540 | + struct amdgpu_cs_parser *parser) | |
| 541 | +{ | |
| 542 | + struct amdgpu_fpriv *fpriv = parser->filp->driver_priv; | |
| 543 | + struct amdgpu_vm *vm = &fpriv->vm; | |
| 544 | + int i, j; | |
| 545 | + int r; | |
| 546 | + | |
| 547 | + for (i = 0, j = 0; i < parser->nchunks && j < parser->num_ibs; i++) { | |
| 548 | + struct amdgpu_cs_chunk *chunk; | |
| 549 | + struct amdgpu_ib *ib; | |
| 550 | + struct drm_amdgpu_cs_chunk_ib *chunk_ib; | |
| 551 | + struct amdgpu_ring *ring; | |
| 552 | + | |
| 553 | + chunk = &parser->chunks[i]; | |
| 554 | + ib = &parser->ibs[j]; | |
| 555 | + chunk_ib = (struct drm_amdgpu_cs_chunk_ib *)chunk->kdata; | |
| 556 | + | |
| 557 | + if (chunk->chunk_id != AMDGPU_CHUNK_ID_IB) | |
| 558 | + continue; | |
| 559 | + | |
| 560 | + r = amdgpu_cs_get_ring(adev, chunk_ib->ip_type, | |
| 561 | + chunk_ib->ip_instance, chunk_ib->ring, | |
| 562 | + &ring); | |
| 563 | + if (r) | |
| 564 | + return r; | |
| 565 | + | |
| 566 | + if (ring->funcs->parse_cs) { | |
| 567 | + struct amdgpu_bo_va_mapping *m; | |
| 568 | + struct amdgpu_bo *aobj = NULL; | |
| 569 | + uint64_t offset; | |
| 570 | + uint8_t *kptr; | |
| 571 | + | |
| 572 | + m = amdgpu_cs_find_mapping(parser, chunk_ib->va_start, | |
| 573 | + &aobj); | |
| 574 | + if (!aobj) { | |
| 575 | + DRM_ERROR("IB va_start is invalid\n"); | |
| 576 | + return -EINVAL; | |
| 577 | + } | |
| 578 | + | |
| 579 | + if ((chunk_ib->va_start + chunk_ib->ib_bytes) > | |
| 580 | + (m->it.last + 1) * AMDGPU_GPU_PAGE_SIZE) { | |
| 581 | + DRM_ERROR("IB va_start+ib_bytes is invalid\n"); | |
| 582 | + return -EINVAL; | |
| 583 | + } | |
| 584 | + | |
| 585 | + /* the IB should be reserved at this point */ | |
| 586 | + r = amdgpu_bo_kmap(aobj, (void **)&kptr); | |
| 587 | + if (r) { | |
| 588 | + return r; | |
| 589 | + } | |
| 590 | + | |
| 591 | + offset = ((uint64_t)m->it.start) * AMDGPU_GPU_PAGE_SIZE; | |
| 592 | + kptr += chunk_ib->va_start - offset; | |
| 593 | + | |
| 594 | + r = amdgpu_ib_get(ring, NULL, chunk_ib->ib_bytes, ib); | |
| 595 | + if (r) { | |
| 596 | + DRM_ERROR("Failed to get ib !\n"); | |
| 597 | + return r; | |
| 598 | + } | |
| 599 | + | |
| 600 | + memcpy(ib->ptr, kptr, chunk_ib->ib_bytes); | |
| 601 | + amdgpu_bo_kunmap(aobj); | |
| 602 | + } else { | |
| 603 | + r = amdgpu_ib_get(ring, vm, 0, ib); | |
| 604 | + if (r) { | |
| 605 | + DRM_ERROR("Failed to get ib !\n"); | |
| 606 | + return r; | |
| 607 | + } | |
| 608 | + | |
| 609 | + ib->gpu_addr = chunk_ib->va_start; | |
| 610 | + } | |
| 611 | + | |
| 612 | + ib->length_dw = chunk_ib->ib_bytes / 4; | |
| 613 | + ib->flags = chunk_ib->flags; | |
| 614 | + ib->ctx = parser->ctx; | |
| 615 | + j++; | |
| 616 | + } | |
| 617 | + | |
| 618 | + if (!parser->num_ibs) | |
| 619 | + return 0; | |
| 620 | + | |
| 621 | + /* add GDS resources to first IB */ | |
| 622 | + if (parser->bo_list) { | |
| 623 | + struct amdgpu_bo *gds = parser->bo_list->gds_obj; | |
| 624 | + struct amdgpu_bo *gws = parser->bo_list->gws_obj; | |
| 625 | + struct amdgpu_bo *oa = parser->bo_list->oa_obj; | |
| 626 | + struct amdgpu_ib *ib = &parser->ibs[0]; | |
| 627 | + | |
| 628 | + if (gds) { | |
| 629 | + ib->gds_base = amdgpu_bo_gpu_offset(gds); | |
| 630 | + ib->gds_size = amdgpu_bo_size(gds); | |
| 631 | + } | |
| 632 | + if (gws) { | |
| 633 | + ib->gws_base = amdgpu_bo_gpu_offset(gws); | |
| 634 | + ib->gws_size = amdgpu_bo_size(gws); | |
| 635 | + } | |
| 636 | + if (oa) { | |
| 637 | + ib->oa_base = amdgpu_bo_gpu_offset(oa); | |
| 638 | + ib->oa_size = amdgpu_bo_size(oa); | |
| 639 | + } | |
| 640 | + } | |
| 641 | + | |
| 642 | + /* wrap the last IB with user fence */ | |
| 643 | + if (parser->uf.bo) { | |
| 644 | + struct amdgpu_ib *ib = &parser->ibs[parser->num_ibs - 1]; | |
| 645 | + | |
| 646 | + /* UVD & VCE fw doesn't support user fences */ | |
| 647 | + if (ib->ring->type == AMDGPU_RING_TYPE_UVD || | |
| 648 | + ib->ring->type == AMDGPU_RING_TYPE_VCE) | |
| 649 | + return -EINVAL; | |
| 650 | + | |
| 651 | + ib->user = &parser->uf; | |
| 652 | + } | |
| 653 | + | |
| 654 | + return 0; | |
| 655 | +} | |
| 656 | + | |
| 657 | +int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) | |
| 658 | +{ | |
| 659 | + struct amdgpu_device *adev = dev->dev_private; | |
| 660 | + union drm_amdgpu_cs *cs = data; | |
| 661 | + struct amdgpu_cs_parser parser; | |
| 662 | + int r, i; | |
| 663 | + bool reserved_buffers = false; | |
| 664 | + | |
| 665 | + down_read(&adev->exclusive_lock); | |
| 666 | + if (!adev->accel_working) { | |
| 667 | + up_read(&adev->exclusive_lock); | |
| 668 | + return -EBUSY; | |
| 669 | + } | |
| 670 | + /* initialize parser */ | |
| 671 | + memset(&parser, 0, sizeof(struct amdgpu_cs_parser)); | |
| 672 | + parser.filp = filp; | |
| 673 | + parser.adev = adev; | |
| 674 | + r = amdgpu_cs_parser_init(&parser, data); | |
| 675 | + if (r) { | |
| 676 | + DRM_ERROR("Failed to initialize parser !\n"); | |
| 677 | + amdgpu_cs_parser_fini(&parser, r, false); | |
| 678 | + up_read(&adev->exclusive_lock); | |
| 679 | + r = amdgpu_cs_handle_lockup(adev, r); | |
| 680 | + return r; | |
| 681 | + } | |
| 682 | + | |
| 683 | + r = amdgpu_cs_parser_relocs(&parser); | |
| 684 | + if (r) { | |
| 685 | + if (r != -ERESTARTSYS) { | |
| 686 | + if (r == -ENOMEM) | |
| 687 | + DRM_ERROR("Not enough memory for command submission!\n"); | |
| 688 | + else | |
| 689 | + DRM_ERROR("Failed to process the buffer list %d!\n", r); | |
| 690 | + } | |
| 691 | + } else { | |
| 692 | + reserved_buffers = true; | |
| 693 | + r = amdgpu_cs_ib_fill(adev, &parser); | |
| 694 | + } | |
| 695 | + | |
| 696 | + if (r) { | |
| 697 | + amdgpu_cs_parser_fini(&parser, r, reserved_buffers); | |
| 698 | + up_read(&adev->exclusive_lock); | |
| 699 | + r = amdgpu_cs_handle_lockup(adev, r); | |
| 700 | + return r; | |
| 701 | + } | |
| 702 | + | |
| 703 | + for (i = 0; i < parser.num_ibs; i++) | |
| 704 | + trace_amdgpu_cs(&parser, i); | |
| 705 | + | |
| 706 | + r = amdgpu_cs_ib_vm_chunk(adev, &parser); | |
| 707 | + if (r) { | |
| 708 | + goto out; | |
| 709 | + } | |
| 710 | + | |
| 711 | + cs->out.handle = parser.ibs[parser.num_ibs - 1].fence->seq; | |
| 712 | +out: | |
| 713 | + amdgpu_cs_parser_fini(&parser, r, true); | |
| 714 | + up_read(&adev->exclusive_lock); | |
| 715 | + r = amdgpu_cs_handle_lockup(adev, r); | |
| 716 | + return r; | |
| 717 | +} | |
| 718 | + | |
| 719 | +/** | |
| 720 | + * amdgpu_cs_wait_ioctl - wait for a command submission to finish | |
| 721 | + * | |
| 722 | + * @dev: drm device | |
| 723 | + * @data: data from userspace | |
| 724 | + * @filp: file private | |
| 725 | + * | |
| 726 | + * Wait for the command submission identified by handle to finish. | |
| 727 | + */ | |
| 728 | +int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data, | |
| 729 | + struct drm_file *filp) | |
| 730 | +{ | |
| 731 | + union drm_amdgpu_wait_cs *wait = data; | |
| 732 | + struct amdgpu_device *adev = dev->dev_private; | |
| 733 | + uint64_t seq[AMDGPU_MAX_RINGS] = {0}; | |
| 734 | + struct amdgpu_ring *ring = NULL; | |
| 735 | + unsigned long timeout = amdgpu_gem_timeout(wait->in.timeout); | |
| 736 | + struct amdgpu_ctx *ctx; | |
| 737 | + long r; | |
| 738 | + | |
| 739 | + ctx = amdgpu_ctx_get(filp->driver_priv, wait->in.ctx_id); | |
| 740 | + if (ctx == NULL) | |
| 741 | + return -EINVAL; | |
| 742 | + | |
| 743 | + r = amdgpu_cs_get_ring(adev, wait->in.ip_type, wait->in.ip_instance, | |
| 744 | + wait->in.ring, &ring); | |
| 745 | + if (r) | |
| 746 | + return r; | |
| 747 | + | |
| 748 | + seq[ring->idx] = wait->in.handle; | |
| 749 | + | |
| 750 | + r = amdgpu_fence_wait_seq_timeout(adev, seq, true, timeout); | |
| 751 | + amdgpu_ctx_put(ctx); | |
| 752 | + if (r < 0) | |
| 753 | + return r; | |
| 754 | + | |
| 755 | + memset(wait, 0, sizeof(*wait)); | |
| 756 | + wait->out.status = (r == 0); | |
| 757 | + | |
| 758 | + return 0; | |
| 759 | +} | |
| 760 | + | |
| 761 | +/** | |
| 762 | + * amdgpu_cs_find_bo_va - find bo_va for VM address | |
| 763 | + * | |
| 764 | + * @parser: command submission parser context | |
| 765 | + * @addr: VM address | |
| 766 | + * @bo: resulting BO of the mapping found | |
| 767 | + * | |
| 768 | + * Search the buffer objects in the command submission context for a certain | |
| 769 | + * virtual memory address. Returns allocation structure when found, NULL | |
| 770 | + * otherwise. | |
| 771 | + */ | |
| 772 | +struct amdgpu_bo_va_mapping * | |
| 773 | +amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser, | |
| 774 | + uint64_t addr, struct amdgpu_bo **bo) | |
| 775 | +{ | |
| 776 | + struct amdgpu_bo_list_entry *reloc; | |
| 777 | + struct amdgpu_bo_va_mapping *mapping; | |
| 778 | + | |
| 779 | + addr /= AMDGPU_GPU_PAGE_SIZE; | |
| 780 | + | |
| 781 | + list_for_each_entry(reloc, &parser->validated, tv.head) { | |
| 782 | + if (!reloc->bo_va) | |
| 783 | + continue; | |
| 784 | + | |
| 785 | + list_for_each_entry(mapping, &reloc->bo_va->mappings, list) { | |
| 786 | + if (mapping->it.start > addr || | |
| 787 | + addr > mapping->it.last) | |
| 788 | + continue; | |
| 789 | + | |
| 790 | + *bo = reloc->bo_va->bo; | |
| 791 | + return mapping; | |
| 792 | + } | |
| 793 | + } | |
| 794 | + | |
| 795 | + return NULL; | |
| 796 | +} | |
| @@ -0,0 +1,193 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2015 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + * Authors: monk liu <monk.liu@amd.com> | |
| 23 | + */ | |
| 24 | + | |
| 25 | +#include <drm/drmP.h> | |
| 26 | +#include "amdgpu.h" | |
| 27 | + | |
| 28 | +static void amdgpu_ctx_do_release(struct kref *ref) | |
| 29 | +{ | |
| 30 | + struct amdgpu_ctx *ctx; | |
| 31 | + struct amdgpu_ctx_mgr *mgr; | |
| 32 | + | |
| 33 | + ctx = container_of(ref, struct amdgpu_ctx, refcount); | |
| 34 | + mgr = &ctx->fpriv->ctx_mgr; | |
| 35 | + | |
| 36 | + idr_remove(&mgr->ctx_handles, ctx->id); | |
| 37 | + kfree(ctx); | |
| 38 | +} | |
| 39 | + | |
| 40 | +int amdgpu_ctx_alloc(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t *id, uint32_t flags) | |
| 41 | +{ | |
| 42 | + int r; | |
| 43 | + struct amdgpu_ctx *ctx; | |
| 44 | + struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; | |
| 45 | + | |
| 46 | + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); | |
| 47 | + if (!ctx) | |
| 48 | + return -ENOMEM; | |
| 49 | + | |
| 50 | + mutex_lock(&mgr->lock); | |
| 51 | + r = idr_alloc(&mgr->ctx_handles, ctx, 0, 0, GFP_KERNEL); | |
| 52 | + if (r < 0) { | |
| 53 | + mutex_unlock(&mgr->lock); | |
| 54 | + kfree(ctx); | |
| 55 | + return r; | |
| 56 | + } | |
| 57 | + *id = (uint32_t)r; | |
| 58 | + | |
| 59 | + memset(ctx, 0, sizeof(*ctx)); | |
| 60 | + ctx->id = *id; | |
| 61 | + ctx->fpriv = fpriv; | |
| 62 | + kref_init(&ctx->refcount); | |
| 63 | + mutex_unlock(&mgr->lock); | |
| 64 | + | |
| 65 | + return 0; | |
| 66 | +} | |
| 67 | + | |
| 68 | +int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id) | |
| 69 | +{ | |
| 70 | + struct amdgpu_ctx *ctx; | |
| 71 | + struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; | |
| 72 | + | |
| 73 | + mutex_lock(&mgr->lock); | |
| 74 | + ctx = idr_find(&mgr->ctx_handles, id); | |
| 75 | + if (ctx) { | |
| 76 | + kref_put(&ctx->refcount, amdgpu_ctx_do_release); | |
| 77 | + mutex_unlock(&mgr->lock); | |
| 78 | + return 0; | |
| 79 | + } | |
| 80 | + mutex_unlock(&mgr->lock); | |
| 81 | + return -EINVAL; | |
| 82 | +} | |
| 83 | + | |
| 84 | +static int amdgpu_ctx_query(struct amdgpu_device *adev, | |
| 85 | + struct amdgpu_fpriv *fpriv, uint32_t id, | |
| 86 | + union drm_amdgpu_ctx_out *out) | |
| 87 | +{ | |
| 88 | + struct amdgpu_ctx *ctx; | |
| 89 | + struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; | |
| 90 | + unsigned reset_counter; | |
| 91 | + | |
| 92 | + mutex_lock(&mgr->lock); | |
| 93 | + ctx = idr_find(&mgr->ctx_handles, id); | |
| 94 | + if (!ctx) { | |
| 95 | + mutex_unlock(&mgr->lock); | |
| 96 | + return -EINVAL; | |
| 97 | + } | |
| 98 | + | |
| 99 | + /* TODO: these two are always zero */ | |
| 100 | + out->state.flags = ctx->state.flags; | |
| 101 | + out->state.hangs = ctx->state.hangs; | |
| 102 | + | |
| 103 | + /* determine if a GPU reset has occured since the last call */ | |
| 104 | + reset_counter = atomic_read(&adev->gpu_reset_counter); | |
| 105 | + /* TODO: this should ideally return NO, GUILTY, or INNOCENT. */ | |
| 106 | + if (ctx->reset_counter == reset_counter) | |
| 107 | + out->state.reset_status = AMDGPU_CTX_NO_RESET; | |
| 108 | + else | |
| 109 | + out->state.reset_status = AMDGPU_CTX_UNKNOWN_RESET; | |
| 110 | + ctx->reset_counter = reset_counter; | |
| 111 | + | |
| 112 | + mutex_unlock(&mgr->lock); | |
| 113 | + return 0; | |
| 114 | +} | |
| 115 | + | |
| 116 | +void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv) | |
| 117 | +{ | |
| 118 | + struct idr *idp; | |
| 119 | + struct amdgpu_ctx *ctx; | |
| 120 | + uint32_t id; | |
| 121 | + struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; | |
| 122 | + idp = &mgr->ctx_handles; | |
| 123 | + | |
| 124 | + idr_for_each_entry(idp,ctx,id) { | |
| 125 | + if (kref_put(&ctx->refcount, amdgpu_ctx_do_release) != 1) | |
| 126 | + DRM_ERROR("ctx (id=%ul) is still alive\n",ctx->id); | |
| 127 | + } | |
| 128 | + | |
| 129 | + mutex_destroy(&mgr->lock); | |
| 130 | +} | |
| 131 | + | |
| 132 | +int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, | |
| 133 | + struct drm_file *filp) | |
| 134 | +{ | |
| 135 | + int r; | |
| 136 | + uint32_t id; | |
| 137 | + uint32_t flags; | |
| 138 | + | |
| 139 | + union drm_amdgpu_ctx *args = data; | |
| 140 | + struct amdgpu_device *adev = dev->dev_private; | |
| 141 | + struct amdgpu_fpriv *fpriv = filp->driver_priv; | |
| 142 | + | |
| 143 | + r = 0; | |
| 144 | + id = args->in.ctx_id; | |
| 145 | + flags = args->in.flags; | |
| 146 | + | |
| 147 | + switch (args->in.op) { | |
| 148 | + case AMDGPU_CTX_OP_ALLOC_CTX: | |
| 149 | + r = amdgpu_ctx_alloc(adev, fpriv, &id, flags); | |
| 150 | + args->out.alloc.ctx_id = id; | |
| 151 | + break; | |
| 152 | + case AMDGPU_CTX_OP_FREE_CTX: | |
| 153 | + r = amdgpu_ctx_free(adev, fpriv, id); | |
| 154 | + break; | |
| 155 | + case AMDGPU_CTX_OP_QUERY_STATE: | |
| 156 | + r = amdgpu_ctx_query(adev, fpriv, id, &args->out); | |
| 157 | + break; | |
| 158 | + default: | |
| 159 | + return -EINVAL; | |
| 160 | + } | |
| 161 | + | |
| 162 | + return r; | |
| 163 | +} | |
| 164 | + | |
| 165 | +struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id) | |
| 166 | +{ | |
| 167 | + struct amdgpu_ctx *ctx; | |
| 168 | + struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; | |
| 169 | + | |
| 170 | + mutex_lock(&mgr->lock); | |
| 171 | + ctx = idr_find(&mgr->ctx_handles, id); | |
| 172 | + if (ctx) | |
| 173 | + kref_get(&ctx->refcount); | |
| 174 | + mutex_unlock(&mgr->lock); | |
| 175 | + return ctx; | |
| 176 | +} | |
| 177 | + | |
| 178 | +int amdgpu_ctx_put(struct amdgpu_ctx *ctx) | |
| 179 | +{ | |
| 180 | + struct amdgpu_fpriv *fpriv; | |
| 181 | + struct amdgpu_ctx_mgr *mgr; | |
| 182 | + | |
| 183 | + if (ctx == NULL) | |
| 184 | + return -EINVAL; | |
| 185 | + | |
| 186 | + fpriv = ctx->fpriv; | |
| 187 | + mgr = &fpriv->ctx_mgr; | |
| 188 | + mutex_lock(&mgr->lock); | |
| 189 | + kref_put(&ctx->refcount, amdgpu_ctx_do_release); | |
| 190 | + mutex_unlock(&mgr->lock); | |
| 191 | + | |
| 192 | + return 0; | |
| 193 | +} | |
| @@ -0,0 +1,2003 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + */ | |
| 28 | +#include <linux/console.h> | |
| 29 | +#include <linux/slab.h> | |
| 30 | +#include <linux/debugfs.h> | |
| 31 | +#include <drm/drmP.h> | |
| 32 | +#include <drm/drm_crtc_helper.h> | |
| 33 | +#include <drm/amdgpu_drm.h> | |
| 34 | +#include <linux/vgaarb.h> | |
| 35 | +#include <linux/vga_switcheroo.h> | |
| 36 | +#include <linux/efi.h> | |
| 37 | +#include "amdgpu.h" | |
| 38 | +#include "amdgpu_i2c.h" | |
| 39 | +#include "atom.h" | |
| 40 | +#include "amdgpu_atombios.h" | |
| 41 | +#ifdef CONFIG_DRM_AMDGPU_CIK | |
| 42 | +#include "cik.h" | |
| 43 | +#endif | |
| 44 | +#include "vi.h" | |
| 45 | +#include "bif/bif_4_1_d.h" | |
| 46 | + | |
| 47 | +static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev); | |
| 48 | +static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev); | |
| 49 | + | |
| 50 | +static const char *amdgpu_asic_name[] = { | |
| 51 | + "BONAIRE", | |
| 52 | + "KAVERI", | |
| 53 | + "KABINI", | |
| 54 | + "HAWAII", | |
| 55 | + "MULLINS", | |
| 56 | + "TOPAZ", | |
| 57 | + "TONGA", | |
| 58 | + "CARRIZO", | |
| 59 | + "LAST", | |
| 60 | +}; | |
| 61 | + | |
| 62 | +bool amdgpu_device_is_px(struct drm_device *dev) | |
| 63 | +{ | |
| 64 | + struct amdgpu_device *adev = dev->dev_private; | |
| 65 | + | |
| 66 | + if (adev->flags & AMDGPU_IS_PX) | |
| 67 | + return true; | |
| 68 | + return false; | |
| 69 | +} | |
| 70 | + | |
| 71 | +/* | |
| 72 | + * MMIO register access helper functions. | |
| 73 | + */ | |
| 74 | +uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, | |
| 75 | + bool always_indirect) | |
| 76 | +{ | |
| 77 | + if ((reg * 4) < adev->rmmio_size && !always_indirect) | |
| 78 | + return readl(((void __iomem *)adev->rmmio) + (reg * 4)); | |
| 79 | + else { | |
| 80 | + unsigned long flags; | |
| 81 | + uint32_t ret; | |
| 82 | + | |
| 83 | + spin_lock_irqsave(&adev->mmio_idx_lock, flags); | |
| 84 | + writel((reg * 4), ((void __iomem *)adev->rmmio) + (mmMM_INDEX * 4)); | |
| 85 | + ret = readl(((void __iomem *)adev->rmmio) + (mmMM_DATA * 4)); | |
| 86 | + spin_unlock_irqrestore(&adev->mmio_idx_lock, flags); | |
| 87 | + | |
| 88 | + return ret; | |
| 89 | + } | |
| 90 | +} | |
| 91 | + | |
| 92 | +void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, | |
| 93 | + bool always_indirect) | |
| 94 | +{ | |
| 95 | + if ((reg * 4) < adev->rmmio_size && !always_indirect) | |
| 96 | + writel(v, ((void __iomem *)adev->rmmio) + (reg * 4)); | |
| 97 | + else { | |
| 98 | + unsigned long flags; | |
| 99 | + | |
| 100 | + spin_lock_irqsave(&adev->mmio_idx_lock, flags); | |
| 101 | + writel((reg * 4), ((void __iomem *)adev->rmmio) + (mmMM_INDEX * 4)); | |
| 102 | + writel(v, ((void __iomem *)adev->rmmio) + (mmMM_DATA * 4)); | |
| 103 | + spin_unlock_irqrestore(&adev->mmio_idx_lock, flags); | |
| 104 | + } | |
| 105 | +} | |
| 106 | + | |
| 107 | +u32 amdgpu_io_rreg(struct amdgpu_device *adev, u32 reg) | |
| 108 | +{ | |
| 109 | + if ((reg * 4) < adev->rio_mem_size) | |
| 110 | + return ioread32(adev->rio_mem + (reg * 4)); | |
| 111 | + else { | |
| 112 | + iowrite32((reg * 4), adev->rio_mem + (mmMM_INDEX * 4)); | |
| 113 | + return ioread32(adev->rio_mem + (mmMM_DATA * 4)); | |
| 114 | + } | |
| 115 | +} | |
| 116 | + | |
| 117 | +void amdgpu_io_wreg(struct amdgpu_device *adev, u32 reg, u32 v) | |
| 118 | +{ | |
| 119 | + | |
| 120 | + if ((reg * 4) < adev->rio_mem_size) | |
| 121 | + iowrite32(v, adev->rio_mem + (reg * 4)); | |
| 122 | + else { | |
| 123 | + iowrite32((reg * 4), adev->rio_mem + (mmMM_INDEX * 4)); | |
| 124 | + iowrite32(v, adev->rio_mem + (mmMM_DATA * 4)); | |
| 125 | + } | |
| 126 | +} | |
| 127 | + | |
| 128 | +/** | |
| 129 | + * amdgpu_mm_rdoorbell - read a doorbell dword | |
| 130 | + * | |
| 131 | + * @adev: amdgpu_device pointer | |
| 132 | + * @index: doorbell index | |
| 133 | + * | |
| 134 | + * Returns the value in the doorbell aperture at the | |
| 135 | + * requested doorbell index (CIK). | |
| 136 | + */ | |
| 137 | +u32 amdgpu_mm_rdoorbell(struct amdgpu_device *adev, u32 index) | |
| 138 | +{ | |
| 139 | + if (index < adev->doorbell.num_doorbells) { | |
| 140 | + return readl(adev->doorbell.ptr + index); | |
| 141 | + } else { | |
| 142 | + DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", index); | |
| 143 | + return 0; | |
| 144 | + } | |
| 145 | +} | |
| 146 | + | |
| 147 | +/** | |
| 148 | + * amdgpu_mm_wdoorbell - write a doorbell dword | |
| 149 | + * | |
| 150 | + * @adev: amdgpu_device pointer | |
| 151 | + * @index: doorbell index | |
| 152 | + * @v: value to write | |
| 153 | + * | |
| 154 | + * Writes @v to the doorbell aperture at the | |
| 155 | + * requested doorbell index (CIK). | |
| 156 | + */ | |
| 157 | +void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v) | |
| 158 | +{ | |
| 159 | + if (index < adev->doorbell.num_doorbells) { | |
| 160 | + writel(v, adev->doorbell.ptr + index); | |
| 161 | + } else { | |
| 162 | + DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", index); | |
| 163 | + } | |
| 164 | +} | |
| 165 | + | |
| 166 | +/** | |
| 167 | + * amdgpu_invalid_rreg - dummy reg read function | |
| 168 | + * | |
| 169 | + * @adev: amdgpu device pointer | |
| 170 | + * @reg: offset of register | |
| 171 | + * | |
| 172 | + * Dummy register read function. Used for register blocks | |
| 173 | + * that certain asics don't have (all asics). | |
| 174 | + * Returns the value in the register. | |
| 175 | + */ | |
| 176 | +static uint32_t amdgpu_invalid_rreg(struct amdgpu_device *adev, uint32_t reg) | |
| 177 | +{ | |
| 178 | + DRM_ERROR("Invalid callback to read register 0x%04X\n", reg); | |
| 179 | + BUG(); | |
| 180 | + return 0; | |
| 181 | +} | |
| 182 | + | |
| 183 | +/** | |
| 184 | + * amdgpu_invalid_wreg - dummy reg write function | |
| 185 | + * | |
| 186 | + * @adev: amdgpu device pointer | |
| 187 | + * @reg: offset of register | |
| 188 | + * @v: value to write to the register | |
| 189 | + * | |
| 190 | + * Dummy register read function. Used for register blocks | |
| 191 | + * that certain asics don't have (all asics). | |
| 192 | + */ | |
| 193 | +static void amdgpu_invalid_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v) | |
| 194 | +{ | |
| 195 | + DRM_ERROR("Invalid callback to write register 0x%04X with 0x%08X\n", | |
| 196 | + reg, v); | |
| 197 | + BUG(); | |
| 198 | +} | |
| 199 | + | |
| 200 | +/** | |
| 201 | + * amdgpu_block_invalid_rreg - dummy reg read function | |
| 202 | + * | |
| 203 | + * @adev: amdgpu device pointer | |
| 204 | + * @block: offset of instance | |
| 205 | + * @reg: offset of register | |
| 206 | + * | |
| 207 | + * Dummy register read function. Used for register blocks | |
| 208 | + * that certain asics don't have (all asics). | |
| 209 | + * Returns the value in the register. | |
| 210 | + */ | |
| 211 | +static uint32_t amdgpu_block_invalid_rreg(struct amdgpu_device *adev, | |
| 212 | + uint32_t block, uint32_t reg) | |
| 213 | +{ | |
| 214 | + DRM_ERROR("Invalid callback to read register 0x%04X in block 0x%04X\n", | |
| 215 | + reg, block); | |
| 216 | + BUG(); | |
| 217 | + return 0; | |
| 218 | +} | |
| 219 | + | |
| 220 | +/** | |
| 221 | + * amdgpu_block_invalid_wreg - dummy reg write function | |
| 222 | + * | |
| 223 | + * @adev: amdgpu device pointer | |
| 224 | + * @block: offset of instance | |
| 225 | + * @reg: offset of register | |
| 226 | + * @v: value to write to the register | |
| 227 | + * | |
| 228 | + * Dummy register read function. Used for register blocks | |
| 229 | + * that certain asics don't have (all asics). | |
| 230 | + */ | |
| 231 | +static void amdgpu_block_invalid_wreg(struct amdgpu_device *adev, | |
| 232 | + uint32_t block, | |
| 233 | + uint32_t reg, uint32_t v) | |
| 234 | +{ | |
| 235 | + DRM_ERROR("Invalid block callback to write register 0x%04X in block 0x%04X with 0x%08X\n", | |
| 236 | + reg, block, v); | |
| 237 | + BUG(); | |
| 238 | +} | |
| 239 | + | |
| 240 | +static int amdgpu_vram_scratch_init(struct amdgpu_device *adev) | |
| 241 | +{ | |
| 242 | + int r; | |
| 243 | + | |
| 244 | + if (adev->vram_scratch.robj == NULL) { | |
| 245 | + r = amdgpu_bo_create(adev, AMDGPU_GPU_PAGE_SIZE, | |
| 246 | + PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, 0, | |
| 247 | + NULL, &adev->vram_scratch.robj); | |
| 248 | + if (r) { | |
| 249 | + return r; | |
| 250 | + } | |
| 251 | + } | |
| 252 | + | |
| 253 | + r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); | |
| 254 | + if (unlikely(r != 0)) | |
| 255 | + return r; | |
| 256 | + r = amdgpu_bo_pin(adev->vram_scratch.robj, | |
| 257 | + AMDGPU_GEM_DOMAIN_VRAM, &adev->vram_scratch.gpu_addr); | |
| 258 | + if (r) { | |
| 259 | + amdgpu_bo_unreserve(adev->vram_scratch.robj); | |
| 260 | + return r; | |
| 261 | + } | |
| 262 | + r = amdgpu_bo_kmap(adev->vram_scratch.robj, | |
| 263 | + (void **)&adev->vram_scratch.ptr); | |
| 264 | + if (r) | |
| 265 | + amdgpu_bo_unpin(adev->vram_scratch.robj); | |
| 266 | + amdgpu_bo_unreserve(adev->vram_scratch.robj); | |
| 267 | + | |
| 268 | + return r; | |
| 269 | +} | |
| 270 | + | |
| 271 | +static void amdgpu_vram_scratch_fini(struct amdgpu_device *adev) | |
| 272 | +{ | |
| 273 | + int r; | |
| 274 | + | |
| 275 | + if (adev->vram_scratch.robj == NULL) { | |
| 276 | + return; | |
| 277 | + } | |
| 278 | + r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); | |
| 279 | + if (likely(r == 0)) { | |
| 280 | + amdgpu_bo_kunmap(adev->vram_scratch.robj); | |
| 281 | + amdgpu_bo_unpin(adev->vram_scratch.robj); | |
| 282 | + amdgpu_bo_unreserve(adev->vram_scratch.robj); | |
| 283 | + } | |
| 284 | + amdgpu_bo_unref(&adev->vram_scratch.robj); | |
| 285 | +} | |
| 286 | + | |
| 287 | +/** | |
| 288 | + * amdgpu_program_register_sequence - program an array of registers. | |
| 289 | + * | |
| 290 | + * @adev: amdgpu_device pointer | |
| 291 | + * @registers: pointer to the register array | |
| 292 | + * @array_size: size of the register array | |
| 293 | + * | |
| 294 | + * Programs an array or registers with and and or masks. | |
| 295 | + * This is a helper for setting golden registers. | |
| 296 | + */ | |
| 297 | +void amdgpu_program_register_sequence(struct amdgpu_device *adev, | |
| 298 | + const u32 *registers, | |
| 299 | + const u32 array_size) | |
| 300 | +{ | |
| 301 | + u32 tmp, reg, and_mask, or_mask; | |
| 302 | + int i; | |
| 303 | + | |
| 304 | + if (array_size % 3) | |
| 305 | + return; | |
| 306 | + | |
| 307 | + for (i = 0; i < array_size; i +=3) { | |
| 308 | + reg = registers[i + 0]; | |
| 309 | + and_mask = registers[i + 1]; | |
| 310 | + or_mask = registers[i + 2]; | |
| 311 | + | |
| 312 | + if (and_mask == 0xffffffff) { | |
| 313 | + tmp = or_mask; | |
| 314 | + } else { | |
| 315 | + tmp = RREG32(reg); | |
| 316 | + tmp &= ~and_mask; | |
| 317 | + tmp |= or_mask; | |
| 318 | + } | |
| 319 | + WREG32(reg, tmp); | |
| 320 | + } | |
| 321 | +} | |
| 322 | + | |
| 323 | +void amdgpu_pci_config_reset(struct amdgpu_device *adev) | |
| 324 | +{ | |
| 325 | + pci_write_config_dword(adev->pdev, 0x7c, AMDGPU_ASIC_RESET_DATA); | |
| 326 | +} | |
| 327 | + | |
| 328 | +/* | |
| 329 | + * GPU doorbell aperture helpers function. | |
| 330 | + */ | |
| 331 | +/** | |
| 332 | + * amdgpu_doorbell_init - Init doorbell driver information. | |
| 333 | + * | |
| 334 | + * @adev: amdgpu_device pointer | |
| 335 | + * | |
| 336 | + * Init doorbell driver information (CIK) | |
| 337 | + * Returns 0 on success, error on failure. | |
| 338 | + */ | |
| 339 | +static int amdgpu_doorbell_init(struct amdgpu_device *adev) | |
| 340 | +{ | |
| 341 | + /* doorbell bar mapping */ | |
| 342 | + adev->doorbell.base = pci_resource_start(adev->pdev, 2); | |
| 343 | + adev->doorbell.size = pci_resource_len(adev->pdev, 2); | |
| 344 | + | |
| 345 | + adev->doorbell.num_doorbells = min_t(u32, adev->doorbell.size / sizeof(u32), | |
| 346 | + AMDGPU_DOORBELL_MAX_ASSIGNMENT+1); | |
| 347 | + if (adev->doorbell.num_doorbells == 0) | |
| 348 | + return -EINVAL; | |
| 349 | + | |
| 350 | + adev->doorbell.ptr = ioremap(adev->doorbell.base, adev->doorbell.num_doorbells * sizeof(u32)); | |
| 351 | + if (adev->doorbell.ptr == NULL) { | |
| 352 | + return -ENOMEM; | |
| 353 | + } | |
| 354 | + DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)adev->doorbell.base); | |
| 355 | + DRM_INFO("doorbell mmio size: %u\n", (unsigned)adev->doorbell.size); | |
| 356 | + | |
| 357 | + return 0; | |
| 358 | +} | |
| 359 | + | |
| 360 | +/** | |
| 361 | + * amdgpu_doorbell_fini - Tear down doorbell driver information. | |
| 362 | + * | |
| 363 | + * @adev: amdgpu_device pointer | |
| 364 | + * | |
| 365 | + * Tear down doorbell driver information (CIK) | |
| 366 | + */ | |
| 367 | +static void amdgpu_doorbell_fini(struct amdgpu_device *adev) | |
| 368 | +{ | |
| 369 | + iounmap(adev->doorbell.ptr); | |
| 370 | + adev->doorbell.ptr = NULL; | |
| 371 | +} | |
| 372 | + | |
| 373 | +/** | |
| 374 | + * amdgpu_doorbell_get_kfd_info - Report doorbell configuration required to | |
| 375 | + * setup amdkfd | |
| 376 | + * | |
| 377 | + * @adev: amdgpu_device pointer | |
| 378 | + * @aperture_base: output returning doorbell aperture base physical address | |
| 379 | + * @aperture_size: output returning doorbell aperture size in bytes | |
| 380 | + * @start_offset: output returning # of doorbell bytes reserved for amdgpu. | |
| 381 | + * | |
| 382 | + * amdgpu and amdkfd share the doorbell aperture. amdgpu sets it up, | |
| 383 | + * takes doorbells required for its own rings and reports the setup to amdkfd. | |
| 384 | + * amdgpu reserved doorbells are at the start of the doorbell aperture. | |
| 385 | + */ | |
| 386 | +void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev, | |
| 387 | + phys_addr_t *aperture_base, | |
| 388 | + size_t *aperture_size, | |
| 389 | + size_t *start_offset) | |
| 390 | +{ | |
| 391 | + /* | |
| 392 | + * The first num_doorbells are used by amdgpu. | |
| 393 | + * amdkfd takes whatever's left in the aperture. | |
| 394 | + */ | |
| 395 | + if (adev->doorbell.size > adev->doorbell.num_doorbells * sizeof(u32)) { | |
| 396 | + *aperture_base = adev->doorbell.base; | |
| 397 | + *aperture_size = adev->doorbell.size; | |
| 398 | + *start_offset = adev->doorbell.num_doorbells * sizeof(u32); | |
| 399 | + } else { | |
| 400 | + *aperture_base = 0; | |
| 401 | + *aperture_size = 0; | |
| 402 | + *start_offset = 0; | |
| 403 | + } | |
| 404 | +} | |
| 405 | + | |
| 406 | +/* | |
| 407 | + * amdgpu_wb_*() | |
| 408 | + * Writeback is the the method by which the the GPU updates special pages | |
| 409 | + * in memory with the status of certain GPU events (fences, ring pointers, | |
| 410 | + * etc.). | |
| 411 | + */ | |
| 412 | + | |
| 413 | +/** | |
| 414 | + * amdgpu_wb_fini - Disable Writeback and free memory | |
| 415 | + * | |
| 416 | + * @adev: amdgpu_device pointer | |
| 417 | + * | |
| 418 | + * Disables Writeback and frees the Writeback memory (all asics). | |
| 419 | + * Used at driver shutdown. | |
| 420 | + */ | |
| 421 | +static void amdgpu_wb_fini(struct amdgpu_device *adev) | |
| 422 | +{ | |
| 423 | + if (adev->wb.wb_obj) { | |
| 424 | + if (!amdgpu_bo_reserve(adev->wb.wb_obj, false)) { | |
| 425 | + amdgpu_bo_kunmap(adev->wb.wb_obj); | |
| 426 | + amdgpu_bo_unpin(adev->wb.wb_obj); | |
| 427 | + amdgpu_bo_unreserve(adev->wb.wb_obj); | |
| 428 | + } | |
| 429 | + amdgpu_bo_unref(&adev->wb.wb_obj); | |
| 430 | + adev->wb.wb = NULL; | |
| 431 | + adev->wb.wb_obj = NULL; | |
| 432 | + } | |
| 433 | +} | |
| 434 | + | |
| 435 | +/** | |
| 436 | + * amdgpu_wb_init- Init Writeback driver info and allocate memory | |
| 437 | + * | |
| 438 | + * @adev: amdgpu_device pointer | |
| 439 | + * | |
| 440 | + * Disables Writeback and frees the Writeback memory (all asics). | |
| 441 | + * Used at driver startup. | |
| 442 | + * Returns 0 on success or an -error on failure. | |
| 443 | + */ | |
| 444 | +static int amdgpu_wb_init(struct amdgpu_device *adev) | |
| 445 | +{ | |
| 446 | + int r; | |
| 447 | + | |
| 448 | + if (adev->wb.wb_obj == NULL) { | |
| 449 | + r = amdgpu_bo_create(adev, AMDGPU_MAX_WB * 4, PAGE_SIZE, true, | |
| 450 | + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, &adev->wb.wb_obj); | |
| 451 | + if (r) { | |
| 452 | + dev_warn(adev->dev, "(%d) create WB bo failed\n", r); | |
| 453 | + return r; | |
| 454 | + } | |
| 455 | + r = amdgpu_bo_reserve(adev->wb.wb_obj, false); | |
| 456 | + if (unlikely(r != 0)) { | |
| 457 | + amdgpu_wb_fini(adev); | |
| 458 | + return r; | |
| 459 | + } | |
| 460 | + r = amdgpu_bo_pin(adev->wb.wb_obj, AMDGPU_GEM_DOMAIN_GTT, | |
| 461 | + &adev->wb.gpu_addr); | |
| 462 | + if (r) { | |
| 463 | + amdgpu_bo_unreserve(adev->wb.wb_obj); | |
| 464 | + dev_warn(adev->dev, "(%d) pin WB bo failed\n", r); | |
| 465 | + amdgpu_wb_fini(adev); | |
| 466 | + return r; | |
| 467 | + } | |
| 468 | + r = amdgpu_bo_kmap(adev->wb.wb_obj, (void **)&adev->wb.wb); | |
| 469 | + amdgpu_bo_unreserve(adev->wb.wb_obj); | |
| 470 | + if (r) { | |
| 471 | + dev_warn(adev->dev, "(%d) map WB bo failed\n", r); | |
| 472 | + amdgpu_wb_fini(adev); | |
| 473 | + return r; | |
| 474 | + } | |
| 475 | + | |
| 476 | + adev->wb.num_wb = AMDGPU_MAX_WB; | |
| 477 | + memset(&adev->wb.used, 0, sizeof(adev->wb.used)); | |
| 478 | + | |
| 479 | + /* clear wb memory */ | |
| 480 | + memset((char *)adev->wb.wb, 0, AMDGPU_GPU_PAGE_SIZE); | |
| 481 | + } | |
| 482 | + | |
| 483 | + return 0; | |
| 484 | +} | |
| 485 | + | |
| 486 | +/** | |
| 487 | + * amdgpu_wb_get - Allocate a wb entry | |
| 488 | + * | |
| 489 | + * @adev: amdgpu_device pointer | |
| 490 | + * @wb: wb index | |
| 491 | + * | |
| 492 | + * Allocate a wb slot for use by the driver (all asics). | |
| 493 | + * Returns 0 on success or -EINVAL on failure. | |
| 494 | + */ | |
| 495 | +int amdgpu_wb_get(struct amdgpu_device *adev, u32 *wb) | |
| 496 | +{ | |
| 497 | + unsigned long offset = find_first_zero_bit(adev->wb.used, adev->wb.num_wb); | |
| 498 | + if (offset < adev->wb.num_wb) { | |
| 499 | + __set_bit(offset, adev->wb.used); | |
| 500 | + *wb = offset; | |
| 501 | + return 0; | |
| 502 | + } else { | |
| 503 | + return -EINVAL; | |
| 504 | + } | |
| 505 | +} | |
| 506 | + | |
| 507 | +/** | |
| 508 | + * amdgpu_wb_free - Free a wb entry | |
| 509 | + * | |
| 510 | + * @adev: amdgpu_device pointer | |
| 511 | + * @wb: wb index | |
| 512 | + * | |
| 513 | + * Free a wb slot allocated for use by the driver (all asics) | |
| 514 | + */ | |
| 515 | +void amdgpu_wb_free(struct amdgpu_device *adev, u32 wb) | |
| 516 | +{ | |
| 517 | + if (wb < adev->wb.num_wb) | |
| 518 | + __clear_bit(wb, adev->wb.used); | |
| 519 | +} | |
| 520 | + | |
| 521 | +/** | |
| 522 | + * amdgpu_vram_location - try to find VRAM location | |
| 523 | + * @adev: amdgpu device structure holding all necessary informations | |
| 524 | + * @mc: memory controller structure holding memory informations | |
| 525 | + * @base: base address at which to put VRAM | |
| 526 | + * | |
| 527 | + * Function will place try to place VRAM at base address provided | |
| 528 | + * as parameter (which is so far either PCI aperture address or | |
| 529 | + * for IGP TOM base address). | |
| 530 | + * | |
| 531 | + * If there is not enough space to fit the unvisible VRAM in the 32bits | |
| 532 | + * address space then we limit the VRAM size to the aperture. | |
| 533 | + * | |
| 534 | + * Note: We don't explicitly enforce VRAM start to be aligned on VRAM size, | |
| 535 | + * this shouldn't be a problem as we are using the PCI aperture as a reference. | |
| 536 | + * Otherwise this would be needed for rv280, all r3xx, and all r4xx, but | |
| 537 | + * not IGP. | |
| 538 | + * | |
| 539 | + * Note: we use mc_vram_size as on some board we need to program the mc to | |
| 540 | + * cover the whole aperture even if VRAM size is inferior to aperture size | |
| 541 | + * Novell bug 204882 + along with lots of ubuntu ones | |
| 542 | + * | |
| 543 | + * Note: when limiting vram it's safe to overwritte real_vram_size because | |
| 544 | + * we are not in case where real_vram_size is inferior to mc_vram_size (ie | |
| 545 | + * note afected by bogus hw of Novell bug 204882 + along with lots of ubuntu | |
| 546 | + * ones) | |
| 547 | + * | |
| 548 | + * Note: IGP TOM addr should be the same as the aperture addr, we don't | |
| 549 | + * explicitly check for that thought. | |
| 550 | + * | |
| 551 | + * FIXME: when reducing VRAM size align new size on power of 2. | |
| 552 | + */ | |
| 553 | +void amdgpu_vram_location(struct amdgpu_device *adev, struct amdgpu_mc *mc, u64 base) | |
| 554 | +{ | |
| 555 | + uint64_t limit = (uint64_t)amdgpu_vram_limit << 20; | |
| 556 | + | |
| 557 | + mc->vram_start = base; | |
| 558 | + if (mc->mc_vram_size > (adev->mc.mc_mask - base + 1)) { | |
| 559 | + dev_warn(adev->dev, "limiting VRAM to PCI aperture size\n"); | |
| 560 | + mc->real_vram_size = mc->aper_size; | |
| 561 | + mc->mc_vram_size = mc->aper_size; | |
| 562 | + } | |
| 563 | + mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; | |
| 564 | + if (limit && limit < mc->real_vram_size) | |
| 565 | + mc->real_vram_size = limit; | |
| 566 | + dev_info(adev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n", | |
| 567 | + mc->mc_vram_size >> 20, mc->vram_start, | |
| 568 | + mc->vram_end, mc->real_vram_size >> 20); | |
| 569 | +} | |
| 570 | + | |
| 571 | +/** | |
| 572 | + * amdgpu_gtt_location - try to find GTT location | |
| 573 | + * @adev: amdgpu device structure holding all necessary informations | |
| 574 | + * @mc: memory controller structure holding memory informations | |
| 575 | + * | |
| 576 | + * Function will place try to place GTT before or after VRAM. | |
| 577 | + * | |
| 578 | + * If GTT size is bigger than space left then we ajust GTT size. | |
| 579 | + * Thus function will never fails. | |
| 580 | + * | |
| 581 | + * FIXME: when reducing GTT size align new size on power of 2. | |
| 582 | + */ | |
| 583 | +void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc) | |
| 584 | +{ | |
| 585 | + u64 size_af, size_bf; | |
| 586 | + | |
| 587 | + size_af = ((adev->mc.mc_mask - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; | |
| 588 | + size_bf = mc->vram_start & ~mc->gtt_base_align; | |
| 589 | + if (size_bf > size_af) { | |
| 590 | + if (mc->gtt_size > size_bf) { | |
| 591 | + dev_warn(adev->dev, "limiting GTT\n"); | |
| 592 | + mc->gtt_size = size_bf; | |
| 593 | + } | |
| 594 | + mc->gtt_start = (mc->vram_start & ~mc->gtt_base_align) - mc->gtt_size; | |
| 595 | + } else { | |
| 596 | + if (mc->gtt_size > size_af) { | |
| 597 | + dev_warn(adev->dev, "limiting GTT\n"); | |
| 598 | + mc->gtt_size = size_af; | |
| 599 | + } | |
| 600 | + mc->gtt_start = (mc->vram_end + 1 + mc->gtt_base_align) & ~mc->gtt_base_align; | |
| 601 | + } | |
| 602 | + mc->gtt_end = mc->gtt_start + mc->gtt_size - 1; | |
| 603 | + dev_info(adev->dev, "GTT: %lluM 0x%016llX - 0x%016llX\n", | |
| 604 | + mc->gtt_size >> 20, mc->gtt_start, mc->gtt_end); | |
| 605 | +} | |
| 606 | + | |
| 607 | +/* | |
| 608 | + * GPU helpers function. | |
| 609 | + */ | |
| 610 | +/** | |
| 611 | + * amdgpu_card_posted - check if the hw has already been initialized | |
| 612 | + * | |
| 613 | + * @adev: amdgpu_device pointer | |
| 614 | + * | |
| 615 | + * Check if the asic has been initialized (all asics). | |
| 616 | + * Used at driver startup. | |
| 617 | + * Returns true if initialized or false if not. | |
| 618 | + */ | |
| 619 | +bool amdgpu_card_posted(struct amdgpu_device *adev) | |
| 620 | +{ | |
| 621 | + uint32_t reg; | |
| 622 | + | |
| 623 | + /* then check MEM_SIZE, in case the crtcs are off */ | |
| 624 | + reg = RREG32(mmCONFIG_MEMSIZE); | |
| 625 | + | |
| 626 | + if (reg) | |
| 627 | + return true; | |
| 628 | + | |
| 629 | + return false; | |
| 630 | + | |
| 631 | +} | |
| 632 | + | |
| 633 | +/** | |
| 634 | + * amdgpu_boot_test_post_card - check and possibly initialize the hw | |
| 635 | + * | |
| 636 | + * @adev: amdgpu_device pointer | |
| 637 | + * | |
| 638 | + * Check if the asic is initialized and if not, attempt to initialize | |
| 639 | + * it (all asics). | |
| 640 | + * Returns true if initialized or false if not. | |
| 641 | + */ | |
| 642 | +bool amdgpu_boot_test_post_card(struct amdgpu_device *adev) | |
| 643 | +{ | |
| 644 | + if (amdgpu_card_posted(adev)) | |
| 645 | + return true; | |
| 646 | + | |
| 647 | + if (adev->bios) { | |
| 648 | + DRM_INFO("GPU not posted. posting now...\n"); | |
| 649 | + if (adev->is_atom_bios) | |
| 650 | + amdgpu_atom_asic_init(adev->mode_info.atom_context); | |
| 651 | + return true; | |
| 652 | + } else { | |
| 653 | + dev_err(adev->dev, "Card not posted and no BIOS - ignoring\n"); | |
| 654 | + return false; | |
| 655 | + } | |
| 656 | +} | |
| 657 | + | |
| 658 | +/** | |
| 659 | + * amdgpu_dummy_page_init - init dummy page used by the driver | |
| 660 | + * | |
| 661 | + * @adev: amdgpu_device pointer | |
| 662 | + * | |
| 663 | + * Allocate the dummy page used by the driver (all asics). | |
| 664 | + * This dummy page is used by the driver as a filler for gart entries | |
| 665 | + * when pages are taken out of the GART | |
| 666 | + * Returns 0 on sucess, -ENOMEM on failure. | |
| 667 | + */ | |
| 668 | +int amdgpu_dummy_page_init(struct amdgpu_device *adev) | |
| 669 | +{ | |
| 670 | + if (adev->dummy_page.page) | |
| 671 | + return 0; | |
| 672 | + adev->dummy_page.page = alloc_page(GFP_DMA32 | GFP_KERNEL | __GFP_ZERO); | |
| 673 | + if (adev->dummy_page.page == NULL) | |
| 674 | + return -ENOMEM; | |
| 675 | + adev->dummy_page.addr = pci_map_page(adev->pdev, adev->dummy_page.page, | |
| 676 | + 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | |
| 677 | + if (pci_dma_mapping_error(adev->pdev, adev->dummy_page.addr)) { | |
| 678 | + dev_err(&adev->pdev->dev, "Failed to DMA MAP the dummy page\n"); | |
| 679 | + __free_page(adev->dummy_page.page); | |
| 680 | + adev->dummy_page.page = NULL; | |
| 681 | + return -ENOMEM; | |
| 682 | + } | |
| 683 | + return 0; | |
| 684 | +} | |
| 685 | + | |
| 686 | +/** | |
| 687 | + * amdgpu_dummy_page_fini - free dummy page used by the driver | |
| 688 | + * | |
| 689 | + * @adev: amdgpu_device pointer | |
| 690 | + * | |
| 691 | + * Frees the dummy page used by the driver (all asics). | |
| 692 | + */ | |
| 693 | +void amdgpu_dummy_page_fini(struct amdgpu_device *adev) | |
| 694 | +{ | |
| 695 | + if (adev->dummy_page.page == NULL) | |
| 696 | + return; | |
| 697 | + pci_unmap_page(adev->pdev, adev->dummy_page.addr, | |
| 698 | + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | |
| 699 | + __free_page(adev->dummy_page.page); | |
| 700 | + adev->dummy_page.page = NULL; | |
| 701 | +} | |
| 702 | + | |
| 703 | + | |
| 704 | +/* ATOM accessor methods */ | |
| 705 | +/* | |
| 706 | + * ATOM is an interpreted byte code stored in tables in the vbios. The | |
| 707 | + * driver registers callbacks to access registers and the interpreter | |
| 708 | + * in the driver parses the tables and executes then to program specific | |
| 709 | + * actions (set display modes, asic init, etc.). See amdgpu_atombios.c, | |
| 710 | + * atombios.h, and atom.c | |
| 711 | + */ | |
| 712 | + | |
| 713 | +/** | |
| 714 | + * cail_pll_read - read PLL register | |
| 715 | + * | |
| 716 | + * @info: atom card_info pointer | |
| 717 | + * @reg: PLL register offset | |
| 718 | + * | |
| 719 | + * Provides a PLL register accessor for the atom interpreter (r4xx+). | |
| 720 | + * Returns the value of the PLL register. | |
| 721 | + */ | |
| 722 | +static uint32_t cail_pll_read(struct card_info *info, uint32_t reg) | |
| 723 | +{ | |
| 724 | + return 0; | |
| 725 | +} | |
| 726 | + | |
| 727 | +/** | |
| 728 | + * cail_pll_write - write PLL register | |
| 729 | + * | |
| 730 | + * @info: atom card_info pointer | |
| 731 | + * @reg: PLL register offset | |
| 732 | + * @val: value to write to the pll register | |
| 733 | + * | |
| 734 | + * Provides a PLL register accessor for the atom interpreter (r4xx+). | |
| 735 | + */ | |
| 736 | +static void cail_pll_write(struct card_info *info, uint32_t reg, uint32_t val) | |
| 737 | +{ | |
| 738 | + | |
| 739 | +} | |
| 740 | + | |
| 741 | +/** | |
| 742 | + * cail_mc_read - read MC (Memory Controller) register | |
| 743 | + * | |
| 744 | + * @info: atom card_info pointer | |
| 745 | + * @reg: MC register offset | |
| 746 | + * | |
| 747 | + * Provides an MC register accessor for the atom interpreter (r4xx+). | |
| 748 | + * Returns the value of the MC register. | |
| 749 | + */ | |
| 750 | +static uint32_t cail_mc_read(struct card_info *info, uint32_t reg) | |
| 751 | +{ | |
| 752 | + return 0; | |
| 753 | +} | |
| 754 | + | |
| 755 | +/** | |
| 756 | + * cail_mc_write - write MC (Memory Controller) register | |
| 757 | + * | |
| 758 | + * @info: atom card_info pointer | |
| 759 | + * @reg: MC register offset | |
| 760 | + * @val: value to write to the pll register | |
| 761 | + * | |
| 762 | + * Provides a MC register accessor for the atom interpreter (r4xx+). | |
| 763 | + */ | |
| 764 | +static void cail_mc_write(struct card_info *info, uint32_t reg, uint32_t val) | |
| 765 | +{ | |
| 766 | + | |
| 767 | +} | |
| 768 | + | |
| 769 | +/** | |
| 770 | + * cail_reg_write - write MMIO register | |
| 771 | + * | |
| 772 | + * @info: atom card_info pointer | |
| 773 | + * @reg: MMIO register offset | |
| 774 | + * @val: value to write to the pll register | |
| 775 | + * | |
| 776 | + * Provides a MMIO register accessor for the atom interpreter (r4xx+). | |
| 777 | + */ | |
| 778 | +static void cail_reg_write(struct card_info *info, uint32_t reg, uint32_t val) | |
| 779 | +{ | |
| 780 | + struct amdgpu_device *adev = info->dev->dev_private; | |
| 781 | + | |
| 782 | + WREG32(reg, val); | |
| 783 | +} | |
| 784 | + | |
| 785 | +/** | |
| 786 | + * cail_reg_read - read MMIO register | |
| 787 | + * | |
| 788 | + * @info: atom card_info pointer | |
| 789 | + * @reg: MMIO register offset | |
| 790 | + * | |
| 791 | + * Provides an MMIO register accessor for the atom interpreter (r4xx+). | |
| 792 | + * Returns the value of the MMIO register. | |
| 793 | + */ | |
| 794 | +static uint32_t cail_reg_read(struct card_info *info, uint32_t reg) | |
| 795 | +{ | |
| 796 | + struct amdgpu_device *adev = info->dev->dev_private; | |
| 797 | + uint32_t r; | |
| 798 | + | |
| 799 | + r = RREG32(reg); | |
| 800 | + return r; | |
| 801 | +} | |
| 802 | + | |
| 803 | +/** | |
| 804 | + * cail_ioreg_write - write IO register | |
| 805 | + * | |
| 806 | + * @info: atom card_info pointer | |
| 807 | + * @reg: IO register offset | |
| 808 | + * @val: value to write to the pll register | |
| 809 | + * | |
| 810 | + * Provides a IO register accessor for the atom interpreter (r4xx+). | |
| 811 | + */ | |
| 812 | +static void cail_ioreg_write(struct card_info *info, uint32_t reg, uint32_t val) | |
| 813 | +{ | |
| 814 | + struct amdgpu_device *adev = info->dev->dev_private; | |
| 815 | + | |
| 816 | + WREG32_IO(reg, val); | |
| 817 | +} | |
| 818 | + | |
| 819 | +/** | |
| 820 | + * cail_ioreg_read - read IO register | |
| 821 | + * | |
| 822 | + * @info: atom card_info pointer | |
| 823 | + * @reg: IO register offset | |
| 824 | + * | |
| 825 | + * Provides an IO register accessor for the atom interpreter (r4xx+). | |
| 826 | + * Returns the value of the IO register. | |
| 827 | + */ | |
| 828 | +static uint32_t cail_ioreg_read(struct card_info *info, uint32_t reg) | |
| 829 | +{ | |
| 830 | + struct amdgpu_device *adev = info->dev->dev_private; | |
| 831 | + uint32_t r; | |
| 832 | + | |
| 833 | + r = RREG32_IO(reg); | |
| 834 | + return r; | |
| 835 | +} | |
| 836 | + | |
| 837 | +/** | |
| 838 | + * amdgpu_atombios_fini - free the driver info and callbacks for atombios | |
| 839 | + * | |
| 840 | + * @adev: amdgpu_device pointer | |
| 841 | + * | |
| 842 | + * Frees the driver info and register access callbacks for the ATOM | |
| 843 | + * interpreter (r4xx+). | |
| 844 | + * Called at driver shutdown. | |
| 845 | + */ | |
| 846 | +static void amdgpu_atombios_fini(struct amdgpu_device *adev) | |
| 847 | +{ | |
| 848 | + if (adev->mode_info.atom_context) | |
| 849 | + kfree(adev->mode_info.atom_context->scratch); | |
| 850 | + kfree(adev->mode_info.atom_context); | |
| 851 | + adev->mode_info.atom_context = NULL; | |
| 852 | + kfree(adev->mode_info.atom_card_info); | |
| 853 | + adev->mode_info.atom_card_info = NULL; | |
| 854 | +} | |
| 855 | + | |
| 856 | +/** | |
| 857 | + * amdgpu_atombios_init - init the driver info and callbacks for atombios | |
| 858 | + * | |
| 859 | + * @adev: amdgpu_device pointer | |
| 860 | + * | |
| 861 | + * Initializes the driver info and register access callbacks for the | |
| 862 | + * ATOM interpreter (r4xx+). | |
| 863 | + * Returns 0 on sucess, -ENOMEM on failure. | |
| 864 | + * Called at driver startup. | |
| 865 | + */ | |
| 866 | +static int amdgpu_atombios_init(struct amdgpu_device *adev) | |
| 867 | +{ | |
| 868 | + struct card_info *atom_card_info = | |
| 869 | + kzalloc(sizeof(struct card_info), GFP_KERNEL); | |
| 870 | + | |
| 871 | + if (!atom_card_info) | |
| 872 | + return -ENOMEM; | |
| 873 | + | |
| 874 | + adev->mode_info.atom_card_info = atom_card_info; | |
| 875 | + atom_card_info->dev = adev->ddev; | |
| 876 | + atom_card_info->reg_read = cail_reg_read; | |
| 877 | + atom_card_info->reg_write = cail_reg_write; | |
| 878 | + /* needed for iio ops */ | |
| 879 | + if (adev->rio_mem) { | |
| 880 | + atom_card_info->ioreg_read = cail_ioreg_read; | |
| 881 | + atom_card_info->ioreg_write = cail_ioreg_write; | |
| 882 | + } else { | |
| 883 | + DRM_ERROR("Unable to find PCI I/O BAR; using MMIO for ATOM IIO\n"); | |
| 884 | + atom_card_info->ioreg_read = cail_reg_read; | |
| 885 | + atom_card_info->ioreg_write = cail_reg_write; | |
| 886 | + } | |
| 887 | + atom_card_info->mc_read = cail_mc_read; | |
| 888 | + atom_card_info->mc_write = cail_mc_write; | |
| 889 | + atom_card_info->pll_read = cail_pll_read; | |
| 890 | + atom_card_info->pll_write = cail_pll_write; | |
| 891 | + | |
| 892 | + adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios); | |
| 893 | + if (!adev->mode_info.atom_context) { | |
| 894 | + amdgpu_atombios_fini(adev); | |
| 895 | + return -ENOMEM; | |
| 896 | + } | |
| 897 | + | |
| 898 | + mutex_init(&adev->mode_info.atom_context->mutex); | |
| 899 | + amdgpu_atombios_scratch_regs_init(adev); | |
| 900 | + amdgpu_atom_allocate_fb_scratch(adev->mode_info.atom_context); | |
| 901 | + return 0; | |
| 902 | +} | |
| 903 | + | |
| 904 | +/* if we get transitioned to only one device, take VGA back */ | |
| 905 | +/** | |
| 906 | + * amdgpu_vga_set_decode - enable/disable vga decode | |
| 907 | + * | |
| 908 | + * @cookie: amdgpu_device pointer | |
| 909 | + * @state: enable/disable vga decode | |
| 910 | + * | |
| 911 | + * Enable/disable vga decode (all asics). | |
| 912 | + * Returns VGA resource flags. | |
| 913 | + */ | |
| 914 | +static unsigned int amdgpu_vga_set_decode(void *cookie, bool state) | |
| 915 | +{ | |
| 916 | + struct amdgpu_device *adev = cookie; | |
| 917 | + amdgpu_asic_set_vga_state(adev, state); | |
| 918 | + if (state) | |
| 919 | + return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | | |
| 920 | + VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | |
| 921 | + else | |
| 922 | + return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | |
| 923 | +} | |
| 924 | + | |
| 925 | +/** | |
| 926 | + * amdgpu_check_pot_argument - check that argument is a power of two | |
| 927 | + * | |
| 928 | + * @arg: value to check | |
| 929 | + * | |
| 930 | + * Validates that a certain argument is a power of two (all asics). | |
| 931 | + * Returns true if argument is valid. | |
| 932 | + */ | |
| 933 | +static bool amdgpu_check_pot_argument(int arg) | |
| 934 | +{ | |
| 935 | + return (arg & (arg - 1)) == 0; | |
| 936 | +} | |
| 937 | + | |
| 938 | +/** | |
| 939 | + * amdgpu_check_arguments - validate module params | |
| 940 | + * | |
| 941 | + * @adev: amdgpu_device pointer | |
| 942 | + * | |
| 943 | + * Validates certain module parameters and updates | |
| 944 | + * the associated values used by the driver (all asics). | |
| 945 | + */ | |
| 946 | +static void amdgpu_check_arguments(struct amdgpu_device *adev) | |
| 947 | +{ | |
| 948 | + /* vramlimit must be a power of two */ | |
| 949 | + if (!amdgpu_check_pot_argument(amdgpu_vram_limit)) { | |
| 950 | + dev_warn(adev->dev, "vram limit (%d) must be a power of 2\n", | |
| 951 | + amdgpu_vram_limit); | |
| 952 | + amdgpu_vram_limit = 0; | |
| 953 | + } | |
| 954 | + | |
| 955 | + if (amdgpu_gart_size != -1) { | |
| 956 | + /* gtt size must be power of two and greater or equal to 32M */ | |
| 957 | + if (amdgpu_gart_size < 32) { | |
| 958 | + dev_warn(adev->dev, "gart size (%d) too small\n", | |
| 959 | + amdgpu_gart_size); | |
| 960 | + amdgpu_gart_size = -1; | |
| 961 | + } else if (!amdgpu_check_pot_argument(amdgpu_gart_size)) { | |
| 962 | + dev_warn(adev->dev, "gart size (%d) must be a power of 2\n", | |
| 963 | + amdgpu_gart_size); | |
| 964 | + amdgpu_gart_size = -1; | |
| 965 | + } | |
| 966 | + } | |
| 967 | + | |
| 968 | + if (!amdgpu_check_pot_argument(amdgpu_vm_size)) { | |
| 969 | + dev_warn(adev->dev, "VM size (%d) must be a power of 2\n", | |
| 970 | + amdgpu_vm_size); | |
| 971 | + amdgpu_vm_size = 8; | |
| 972 | + } | |
| 973 | + | |
| 974 | + if (amdgpu_vm_size < 1) { | |
| 975 | + dev_warn(adev->dev, "VM size (%d) too small, min is 1GB\n", | |
| 976 | + amdgpu_vm_size); | |
| 977 | + amdgpu_vm_size = 8; | |
| 978 | + } | |
| 979 | + | |
| 980 | + /* | |
| 981 | + * Max GPUVM size for Cayman, SI and CI are 40 bits. | |
| 982 | + */ | |
| 983 | + if (amdgpu_vm_size > 1024) { | |
| 984 | + dev_warn(adev->dev, "VM size (%d) too large, max is 1TB\n", | |
| 985 | + amdgpu_vm_size); | |
| 986 | + amdgpu_vm_size = 8; | |
| 987 | + } | |
| 988 | + | |
| 989 | + /* defines number of bits in page table versus page directory, | |
| 990 | + * a page is 4KB so we have 12 bits offset, minimum 9 bits in the | |
| 991 | + * page table and the remaining bits are in the page directory */ | |
| 992 | + if (amdgpu_vm_block_size == -1) { | |
| 993 | + | |
| 994 | + /* Total bits covered by PD + PTs */ | |
| 995 | + unsigned bits = ilog2(amdgpu_vm_size) + 18; | |
| 996 | + | |
| 997 | + /* Make sure the PD is 4K in size up to 8GB address space. | |
| 998 | + Above that split equal between PD and PTs */ | |
| 999 | + if (amdgpu_vm_size <= 8) | |
| 1000 | + amdgpu_vm_block_size = bits - 9; | |
| 1001 | + else | |
| 1002 | + amdgpu_vm_block_size = (bits + 3) / 2; | |
| 1003 | + | |
| 1004 | + } else if (amdgpu_vm_block_size < 9) { | |
| 1005 | + dev_warn(adev->dev, "VM page table size (%d) too small\n", | |
| 1006 | + amdgpu_vm_block_size); | |
| 1007 | + amdgpu_vm_block_size = 9; | |
| 1008 | + } | |
| 1009 | + | |
| 1010 | + if (amdgpu_vm_block_size > 24 || | |
| 1011 | + (amdgpu_vm_size * 1024) < (1ull << amdgpu_vm_block_size)) { | |
| 1012 | + dev_warn(adev->dev, "VM page table size (%d) too large\n", | |
| 1013 | + amdgpu_vm_block_size); | |
| 1014 | + amdgpu_vm_block_size = 9; | |
| 1015 | + } | |
| 1016 | +} | |
| 1017 | + | |
| 1018 | +/** | |
| 1019 | + * amdgpu_switcheroo_set_state - set switcheroo state | |
| 1020 | + * | |
| 1021 | + * @pdev: pci dev pointer | |
| 1022 | + * @state: vga switcheroo state | |
| 1023 | + * | |
| 1024 | + * Callback for the switcheroo driver. Suspends or resumes the | |
| 1025 | + * the asics before or after it is powered up using ACPI methods. | |
| 1026 | + */ | |
| 1027 | +static void amdgpu_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) | |
| 1028 | +{ | |
| 1029 | + struct drm_device *dev = pci_get_drvdata(pdev); | |
| 1030 | + | |
| 1031 | + if (amdgpu_device_is_px(dev) && state == VGA_SWITCHEROO_OFF) | |
| 1032 | + return; | |
| 1033 | + | |
| 1034 | + if (state == VGA_SWITCHEROO_ON) { | |
| 1035 | + unsigned d3_delay = dev->pdev->d3_delay; | |
| 1036 | + | |
| 1037 | + printk(KERN_INFO "amdgpu: switched on\n"); | |
| 1038 | + /* don't suspend or resume card normally */ | |
| 1039 | + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | |
| 1040 | + | |
| 1041 | + amdgpu_resume_kms(dev, true, true); | |
| 1042 | + | |
| 1043 | + dev->pdev->d3_delay = d3_delay; | |
| 1044 | + | |
| 1045 | + dev->switch_power_state = DRM_SWITCH_POWER_ON; | |
| 1046 | + drm_kms_helper_poll_enable(dev); | |
| 1047 | + } else { | |
| 1048 | + printk(KERN_INFO "amdgpu: switched off\n"); | |
| 1049 | + drm_kms_helper_poll_disable(dev); | |
| 1050 | + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | |
| 1051 | + amdgpu_suspend_kms(dev, true, true); | |
| 1052 | + dev->switch_power_state = DRM_SWITCH_POWER_OFF; | |
| 1053 | + } | |
| 1054 | +} | |
| 1055 | + | |
| 1056 | +/** | |
| 1057 | + * amdgpu_switcheroo_can_switch - see if switcheroo state can change | |
| 1058 | + * | |
| 1059 | + * @pdev: pci dev pointer | |
| 1060 | + * | |
| 1061 | + * Callback for the switcheroo driver. Check of the switcheroo | |
| 1062 | + * state can be changed. | |
| 1063 | + * Returns true if the state can be changed, false if not. | |
| 1064 | + */ | |
| 1065 | +static bool amdgpu_switcheroo_can_switch(struct pci_dev *pdev) | |
| 1066 | +{ | |
| 1067 | + struct drm_device *dev = pci_get_drvdata(pdev); | |
| 1068 | + | |
| 1069 | + /* | |
| 1070 | + * FIXME: open_count is protected by drm_global_mutex but that would lead to | |
| 1071 | + * locking inversion with the driver load path. And the access here is | |
| 1072 | + * completely racy anyway. So don't bother with locking for now. | |
| 1073 | + */ | |
| 1074 | + return dev->open_count == 0; | |
| 1075 | +} | |
| 1076 | + | |
| 1077 | +static const struct vga_switcheroo_client_ops amdgpu_switcheroo_ops = { | |
| 1078 | + .set_gpu_state = amdgpu_switcheroo_set_state, | |
| 1079 | + .reprobe = NULL, | |
| 1080 | + .can_switch = amdgpu_switcheroo_can_switch, | |
| 1081 | +}; | |
| 1082 | + | |
| 1083 | +int amdgpu_set_clockgating_state(struct amdgpu_device *adev, | |
| 1084 | + enum amd_ip_block_type block_type, | |
| 1085 | + enum amd_clockgating_state state) | |
| 1086 | +{ | |
| 1087 | + int i, r = 0; | |
| 1088 | + | |
| 1089 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1090 | + if (adev->ip_blocks[i].type == block_type) { | |
| 1091 | + r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev, | |
| 1092 | + state); | |
| 1093 | + if (r) | |
| 1094 | + return r; | |
| 1095 | + } | |
| 1096 | + } | |
| 1097 | + return r; | |
| 1098 | +} | |
| 1099 | + | |
| 1100 | +int amdgpu_set_powergating_state(struct amdgpu_device *adev, | |
| 1101 | + enum amd_ip_block_type block_type, | |
| 1102 | + enum amd_powergating_state state) | |
| 1103 | +{ | |
| 1104 | + int i, r = 0; | |
| 1105 | + | |
| 1106 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1107 | + if (adev->ip_blocks[i].type == block_type) { | |
| 1108 | + r = adev->ip_blocks[i].funcs->set_powergating_state((void *)adev, | |
| 1109 | + state); | |
| 1110 | + if (r) | |
| 1111 | + return r; | |
| 1112 | + } | |
| 1113 | + } | |
| 1114 | + return r; | |
| 1115 | +} | |
| 1116 | + | |
| 1117 | +const struct amdgpu_ip_block_version * amdgpu_get_ip_block( | |
| 1118 | + struct amdgpu_device *adev, | |
| 1119 | + enum amd_ip_block_type type) | |
| 1120 | +{ | |
| 1121 | + int i; | |
| 1122 | + | |
| 1123 | + for (i = 0; i < adev->num_ip_blocks; i++) | |
| 1124 | + if (adev->ip_blocks[i].type == type) | |
| 1125 | + return &adev->ip_blocks[i]; | |
| 1126 | + | |
| 1127 | + return NULL; | |
| 1128 | +} | |
| 1129 | + | |
| 1130 | +/** | |
| 1131 | + * amdgpu_ip_block_version_cmp | |
| 1132 | + * | |
| 1133 | + * @adev: amdgpu_device pointer | |
| 1134 | + * @type: enum amd_ip_block_type | |
| 1135 | + * @major: major version | |
| 1136 | + * @minor: minor version | |
| 1137 | + * | |
| 1138 | + * return 0 if equal or greater | |
| 1139 | + * return 1 if smaller or the ip_block doesn't exist | |
| 1140 | + */ | |
| 1141 | +int amdgpu_ip_block_version_cmp(struct amdgpu_device *adev, | |
| 1142 | + enum amd_ip_block_type type, | |
| 1143 | + u32 major, u32 minor) | |
| 1144 | +{ | |
| 1145 | + const struct amdgpu_ip_block_version *ip_block; | |
| 1146 | + ip_block = amdgpu_get_ip_block(adev, type); | |
| 1147 | + | |
| 1148 | + if (ip_block && ((ip_block->major > major) || | |
| 1149 | + ((ip_block->major == major) && | |
| 1150 | + (ip_block->minor >= minor)))) | |
| 1151 | + return 0; | |
| 1152 | + | |
| 1153 | + return 1; | |
| 1154 | +} | |
| 1155 | + | |
| 1156 | +static int amdgpu_early_init(struct amdgpu_device *adev) | |
| 1157 | +{ | |
| 1158 | + int i, r; | |
| 1159 | + | |
| 1160 | + switch (adev->asic_type) { | |
| 1161 | + case CHIP_TOPAZ: | |
| 1162 | + case CHIP_TONGA: | |
| 1163 | + case CHIP_CARRIZO: | |
| 1164 | + if (adev->asic_type == CHIP_CARRIZO) | |
| 1165 | + adev->family = AMDGPU_FAMILY_CZ; | |
| 1166 | + else | |
| 1167 | + adev->family = AMDGPU_FAMILY_VI; | |
| 1168 | + | |
| 1169 | + r = vi_set_ip_blocks(adev); | |
| 1170 | + if (r) | |
| 1171 | + return r; | |
| 1172 | + break; | |
| 1173 | +#ifdef CONFIG_DRM_AMDGPU_CIK | |
| 1174 | + case CHIP_BONAIRE: | |
| 1175 | + case CHIP_HAWAII: | |
| 1176 | + case CHIP_KAVERI: | |
| 1177 | + case CHIP_KABINI: | |
| 1178 | + case CHIP_MULLINS: | |
| 1179 | + if ((adev->asic_type == CHIP_BONAIRE) || (adev->asic_type == CHIP_HAWAII)) | |
| 1180 | + adev->family = AMDGPU_FAMILY_CI; | |
| 1181 | + else | |
| 1182 | + adev->family = AMDGPU_FAMILY_KV; | |
| 1183 | + | |
| 1184 | + r = cik_set_ip_blocks(adev); | |
| 1185 | + if (r) | |
| 1186 | + return r; | |
| 1187 | + break; | |
| 1188 | +#endif | |
| 1189 | + default: | |
| 1190 | + /* FIXME: not supported yet */ | |
| 1191 | + return -EINVAL; | |
| 1192 | + } | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + if (adev->ip_blocks == NULL) { | |
| 1197 | + DRM_ERROR("No IP blocks found!\n"); | |
| 1198 | + return r; | |
| 1199 | + } | |
| 1200 | + | |
| 1201 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1202 | + if ((amdgpu_ip_block_mask & (1 << i)) == 0) { | |
| 1203 | + DRM_ERROR("disabled ip block: %d\n", i); | |
| 1204 | + adev->ip_block_enabled[i] = false; | |
| 1205 | + } else { | |
| 1206 | + if (adev->ip_blocks[i].funcs->early_init) { | |
| 1207 | + r = adev->ip_blocks[i].funcs->early_init((void *)adev); | |
| 1208 | + if (r) | |
| 1209 | + return r; | |
| 1210 | + } | |
| 1211 | + adev->ip_block_enabled[i] = true; | |
| 1212 | + } | |
| 1213 | + } | |
| 1214 | + | |
| 1215 | + return 0; | |
| 1216 | +} | |
| 1217 | + | |
| 1218 | +static int amdgpu_init(struct amdgpu_device *adev) | |
| 1219 | +{ | |
| 1220 | + int i, r; | |
| 1221 | + | |
| 1222 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1223 | + if (!adev->ip_block_enabled[i]) | |
| 1224 | + continue; | |
| 1225 | + r = adev->ip_blocks[i].funcs->sw_init((void *)adev); | |
| 1226 | + if (r) | |
| 1227 | + return r; | |
| 1228 | + /* need to do gmc hw init early so we can allocate gpu mem */ | |
| 1229 | + if (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_GMC) { | |
| 1230 | + r = amdgpu_vram_scratch_init(adev); | |
| 1231 | + if (r) | |
| 1232 | + return r; | |
| 1233 | + r = adev->ip_blocks[i].funcs->hw_init((void *)adev); | |
| 1234 | + if (r) | |
| 1235 | + return r; | |
| 1236 | + r = amdgpu_wb_init(adev); | |
| 1237 | + if (r) | |
| 1238 | + return r; | |
| 1239 | + } | |
| 1240 | + } | |
| 1241 | + | |
| 1242 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1243 | + if (!adev->ip_block_enabled[i]) | |
| 1244 | + continue; | |
| 1245 | + /* gmc hw init is done early */ | |
| 1246 | + if (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_GMC) | |
| 1247 | + continue; | |
| 1248 | + r = adev->ip_blocks[i].funcs->hw_init((void *)adev); | |
| 1249 | + if (r) | |
| 1250 | + return r; | |
| 1251 | + } | |
| 1252 | + | |
| 1253 | + return 0; | |
| 1254 | +} | |
| 1255 | + | |
| 1256 | +static int amdgpu_late_init(struct amdgpu_device *adev) | |
| 1257 | +{ | |
| 1258 | + int i = 0, r; | |
| 1259 | + | |
| 1260 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1261 | + if (!adev->ip_block_enabled[i]) | |
| 1262 | + continue; | |
| 1263 | + /* enable clockgating to save power */ | |
| 1264 | + r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev, | |
| 1265 | + AMD_CG_STATE_GATE); | |
| 1266 | + if (r) | |
| 1267 | + return r; | |
| 1268 | + if (adev->ip_blocks[i].funcs->late_init) { | |
| 1269 | + r = adev->ip_blocks[i].funcs->late_init((void *)adev); | |
| 1270 | + if (r) | |
| 1271 | + return r; | |
| 1272 | + } | |
| 1273 | + } | |
| 1274 | + | |
| 1275 | + return 0; | |
| 1276 | +} | |
| 1277 | + | |
| 1278 | +static int amdgpu_fini(struct amdgpu_device *adev) | |
| 1279 | +{ | |
| 1280 | + int i, r; | |
| 1281 | + | |
| 1282 | + for (i = adev->num_ip_blocks - 1; i >= 0; i--) { | |
| 1283 | + if (!adev->ip_block_enabled[i]) | |
| 1284 | + continue; | |
| 1285 | + if (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_GMC) { | |
| 1286 | + amdgpu_wb_fini(adev); | |
| 1287 | + amdgpu_vram_scratch_fini(adev); | |
| 1288 | + } | |
| 1289 | + /* ungate blocks before hw fini so that we can shutdown the blocks safely */ | |
| 1290 | + r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev, | |
| 1291 | + AMD_CG_STATE_UNGATE); | |
| 1292 | + if (r) | |
| 1293 | + return r; | |
| 1294 | + r = adev->ip_blocks[i].funcs->hw_fini((void *)adev); | |
| 1295 | + /* XXX handle errors */ | |
| 1296 | + } | |
| 1297 | + | |
| 1298 | + for (i = adev->num_ip_blocks - 1; i >= 0; i--) { | |
| 1299 | + if (!adev->ip_block_enabled[i]) | |
| 1300 | + continue; | |
| 1301 | + r = adev->ip_blocks[i].funcs->sw_fini((void *)adev); | |
| 1302 | + /* XXX handle errors */ | |
| 1303 | + adev->ip_block_enabled[i] = false; | |
| 1304 | + } | |
| 1305 | + | |
| 1306 | + return 0; | |
| 1307 | +} | |
| 1308 | + | |
| 1309 | +static int amdgpu_suspend(struct amdgpu_device *adev) | |
| 1310 | +{ | |
| 1311 | + int i, r; | |
| 1312 | + | |
| 1313 | + for (i = adev->num_ip_blocks - 1; i >= 0; i--) { | |
| 1314 | + if (!adev->ip_block_enabled[i]) | |
| 1315 | + continue; | |
| 1316 | + /* ungate blocks so that suspend can properly shut them down */ | |
| 1317 | + r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev, | |
| 1318 | + AMD_CG_STATE_UNGATE); | |
| 1319 | + /* XXX handle errors */ | |
| 1320 | + r = adev->ip_blocks[i].funcs->suspend(adev); | |
| 1321 | + /* XXX handle errors */ | |
| 1322 | + } | |
| 1323 | + | |
| 1324 | + return 0; | |
| 1325 | +} | |
| 1326 | + | |
| 1327 | +static int amdgpu_resume(struct amdgpu_device *adev) | |
| 1328 | +{ | |
| 1329 | + int i, r; | |
| 1330 | + | |
| 1331 | + for (i = 0; i < adev->num_ip_blocks; i++) { | |
| 1332 | + if (!adev->ip_block_enabled[i]) | |
| 1333 | + continue; | |
| 1334 | + r = adev->ip_blocks[i].funcs->resume(adev); | |
| 1335 | + if (r) | |
| 1336 | + return r; | |
| 1337 | + } | |
| 1338 | + | |
| 1339 | + return 0; | |
| 1340 | +} | |
| 1341 | + | |
| 1342 | +/** | |
| 1343 | + * amdgpu_device_init - initialize the driver | |
| 1344 | + * | |
| 1345 | + * @adev: amdgpu_device pointer | |
| 1346 | + * @pdev: drm dev pointer | |
| 1347 | + * @pdev: pci dev pointer | |
| 1348 | + * @flags: driver flags | |
| 1349 | + * | |
| 1350 | + * Initializes the driver info and hw (all asics). | |
| 1351 | + * Returns 0 for success or an error on failure. | |
| 1352 | + * Called at driver startup. | |
| 1353 | + */ | |
| 1354 | +int amdgpu_device_init(struct amdgpu_device *adev, | |
| 1355 | + struct drm_device *ddev, | |
| 1356 | + struct pci_dev *pdev, | |
| 1357 | + uint32_t flags) | |
| 1358 | +{ | |
| 1359 | + int r, i; | |
| 1360 | + bool runtime = false; | |
| 1361 | + | |
| 1362 | + adev->shutdown = false; | |
| 1363 | + adev->dev = &pdev->dev; | |
| 1364 | + adev->ddev = ddev; | |
| 1365 | + adev->pdev = pdev; | |
| 1366 | + adev->flags = flags; | |
| 1367 | + adev->asic_type = flags & AMDGPU_ASIC_MASK; | |
| 1368 | + adev->is_atom_bios = false; | |
| 1369 | + adev->usec_timeout = AMDGPU_MAX_USEC_TIMEOUT; | |
| 1370 | + adev->mc.gtt_size = 512 * 1024 * 1024; | |
| 1371 | + adev->accel_working = false; | |
| 1372 | + adev->num_rings = 0; | |
| 1373 | + adev->mman.buffer_funcs = NULL; | |
| 1374 | + adev->mman.buffer_funcs_ring = NULL; | |
| 1375 | + adev->vm_manager.vm_pte_funcs = NULL; | |
| 1376 | + adev->vm_manager.vm_pte_funcs_ring = NULL; | |
| 1377 | + adev->gart.gart_funcs = NULL; | |
| 1378 | + adev->fence_context = fence_context_alloc(AMDGPU_MAX_RINGS); | |
| 1379 | + | |
| 1380 | + adev->smc_rreg = &amdgpu_invalid_rreg; | |
| 1381 | + adev->smc_wreg = &amdgpu_invalid_wreg; | |
| 1382 | + adev->pcie_rreg = &amdgpu_invalid_rreg; | |
| 1383 | + adev->pcie_wreg = &amdgpu_invalid_wreg; | |
| 1384 | + adev->uvd_ctx_rreg = &amdgpu_invalid_rreg; | |
| 1385 | + adev->uvd_ctx_wreg = &amdgpu_invalid_wreg; | |
| 1386 | + adev->didt_rreg = &amdgpu_invalid_rreg; | |
| 1387 | + adev->didt_wreg = &amdgpu_invalid_wreg; | |
| 1388 | + adev->audio_endpt_rreg = &amdgpu_block_invalid_rreg; | |
| 1389 | + adev->audio_endpt_wreg = &amdgpu_block_invalid_wreg; | |
| 1390 | + | |
| 1391 | + DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X 0x%02X).\n", | |
| 1392 | + amdgpu_asic_name[adev->asic_type], pdev->vendor, pdev->device, | |
| 1393 | + pdev->subsystem_vendor, pdev->subsystem_device, pdev->revision); | |
| 1394 | + | |
| 1395 | + /* mutex initialization are all done here so we | |
| 1396 | + * can recall function without having locking issues */ | |
| 1397 | + mutex_init(&adev->ring_lock); | |
| 1398 | + atomic_set(&adev->irq.ih.lock, 0); | |
| 1399 | + mutex_init(&adev->gem.mutex); | |
| 1400 | + mutex_init(&adev->pm.mutex); | |
| 1401 | + mutex_init(&adev->gfx.gpu_clock_mutex); | |
| 1402 | + mutex_init(&adev->srbm_mutex); | |
| 1403 | + mutex_init(&adev->grbm_idx_mutex); | |
| 1404 | + init_rwsem(&adev->exclusive_lock); | |
| 1405 | + mutex_init(&adev->mn_lock); | |
| 1406 | + hash_init(adev->mn_hash); | |
| 1407 | + | |
| 1408 | + amdgpu_check_arguments(adev); | |
| 1409 | + | |
| 1410 | + /* Registers mapping */ | |
| 1411 | + /* TODO: block userspace mapping of io register */ | |
| 1412 | + spin_lock_init(&adev->mmio_idx_lock); | |
| 1413 | + spin_lock_init(&adev->smc_idx_lock); | |
| 1414 | + spin_lock_init(&adev->pcie_idx_lock); | |
| 1415 | + spin_lock_init(&adev->uvd_ctx_idx_lock); | |
| 1416 | + spin_lock_init(&adev->didt_idx_lock); | |
| 1417 | + spin_lock_init(&adev->audio_endpt_idx_lock); | |
| 1418 | + | |
| 1419 | + adev->rmmio_base = pci_resource_start(adev->pdev, 5); | |
| 1420 | + adev->rmmio_size = pci_resource_len(adev->pdev, 5); | |
| 1421 | + adev->rmmio = ioremap(adev->rmmio_base, adev->rmmio_size); | |
| 1422 | + if (adev->rmmio == NULL) { | |
| 1423 | + return -ENOMEM; | |
| 1424 | + } | |
| 1425 | + DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)adev->rmmio_base); | |
| 1426 | + DRM_INFO("register mmio size: %u\n", (unsigned)adev->rmmio_size); | |
| 1427 | + | |
| 1428 | + /* doorbell bar mapping */ | |
| 1429 | + amdgpu_doorbell_init(adev); | |
| 1430 | + | |
| 1431 | + /* io port mapping */ | |
| 1432 | + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | |
| 1433 | + if (pci_resource_flags(adev->pdev, i) & IORESOURCE_IO) { | |
| 1434 | + adev->rio_mem_size = pci_resource_len(adev->pdev, i); | |
| 1435 | + adev->rio_mem = pci_iomap(adev->pdev, i, adev->rio_mem_size); | |
| 1436 | + break; | |
| 1437 | + } | |
| 1438 | + } | |
| 1439 | + if (adev->rio_mem == NULL) | |
| 1440 | + DRM_ERROR("Unable to find PCI I/O BAR\n"); | |
| 1441 | + | |
| 1442 | + /* early init functions */ | |
| 1443 | + r = amdgpu_early_init(adev); | |
| 1444 | + if (r) | |
| 1445 | + return r; | |
| 1446 | + | |
| 1447 | + /* if we have > 1 VGA cards, then disable the amdgpu VGA resources */ | |
| 1448 | + /* this will fail for cards that aren't VGA class devices, just | |
| 1449 | + * ignore it */ | |
| 1450 | + vga_client_register(adev->pdev, adev, NULL, amdgpu_vga_set_decode); | |
| 1451 | + | |
| 1452 | + if (amdgpu_runtime_pm == 1) | |
| 1453 | + runtime = true; | |
| 1454 | + if (amdgpu_device_is_px(ddev)) | |
| 1455 | + runtime = true; | |
| 1456 | + vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime); | |
| 1457 | + if (runtime) | |
| 1458 | + vga_switcheroo_init_domain_pm_ops(adev->dev, &adev->vga_pm_domain); | |
| 1459 | + | |
| 1460 | + /* Read BIOS */ | |
| 1461 | + if (!amdgpu_get_bios(adev)) | |
| 1462 | + return -EINVAL; | |
| 1463 | + /* Must be an ATOMBIOS */ | |
| 1464 | + if (!adev->is_atom_bios) { | |
| 1465 | + dev_err(adev->dev, "Expecting atombios for GPU\n"); | |
| 1466 | + return -EINVAL; | |
| 1467 | + } | |
| 1468 | + r = amdgpu_atombios_init(adev); | |
| 1469 | + if (r) | |
| 1470 | + return r; | |
| 1471 | + | |
| 1472 | + /* Post card if necessary */ | |
| 1473 | + if (!amdgpu_card_posted(adev)) { | |
| 1474 | + if (!adev->bios) { | |
| 1475 | + dev_err(adev->dev, "Card not posted and no BIOS - ignoring\n"); | |
| 1476 | + return -EINVAL; | |
| 1477 | + } | |
| 1478 | + DRM_INFO("GPU not posted. posting now...\n"); | |
| 1479 | + amdgpu_atom_asic_init(adev->mode_info.atom_context); | |
| 1480 | + } | |
| 1481 | + | |
| 1482 | + /* Initialize clocks */ | |
| 1483 | + r = amdgpu_atombios_get_clock_info(adev); | |
| 1484 | + if (r) | |
| 1485 | + return r; | |
| 1486 | + /* init i2c buses */ | |
| 1487 | + amdgpu_atombios_i2c_init(adev); | |
| 1488 | + | |
| 1489 | + /* Fence driver */ | |
| 1490 | + r = amdgpu_fence_driver_init(adev); | |
| 1491 | + if (r) | |
| 1492 | + return r; | |
| 1493 | + | |
| 1494 | + /* init the mode config */ | |
| 1495 | + drm_mode_config_init(adev->ddev); | |
| 1496 | + | |
| 1497 | + r = amdgpu_init(adev); | |
| 1498 | + if (r) { | |
| 1499 | + amdgpu_fini(adev); | |
| 1500 | + return r; | |
| 1501 | + } | |
| 1502 | + | |
| 1503 | + adev->accel_working = true; | |
| 1504 | + | |
| 1505 | + amdgpu_fbdev_init(adev); | |
| 1506 | + | |
| 1507 | + r = amdgpu_ib_pool_init(adev); | |
| 1508 | + if (r) { | |
| 1509 | + dev_err(adev->dev, "IB initialization failed (%d).\n", r); | |
| 1510 | + return r; | |
| 1511 | + } | |
| 1512 | + | |
| 1513 | + r = amdgpu_ib_ring_tests(adev); | |
| 1514 | + if (r) | |
| 1515 | + DRM_ERROR("ib ring test failed (%d).\n", r); | |
| 1516 | + | |
| 1517 | + r = amdgpu_gem_debugfs_init(adev); | |
| 1518 | + if (r) { | |
| 1519 | + DRM_ERROR("registering gem debugfs failed (%d).\n", r); | |
| 1520 | + } | |
| 1521 | + | |
| 1522 | + r = amdgpu_debugfs_regs_init(adev); | |
| 1523 | + if (r) { | |
| 1524 | + DRM_ERROR("registering register debugfs failed (%d).\n", r); | |
| 1525 | + } | |
| 1526 | + | |
| 1527 | + if ((amdgpu_testing & 1)) { | |
| 1528 | + if (adev->accel_working) | |
| 1529 | + amdgpu_test_moves(adev); | |
| 1530 | + else | |
| 1531 | + DRM_INFO("amdgpu: acceleration disabled, skipping move tests\n"); | |
| 1532 | + } | |
| 1533 | + if ((amdgpu_testing & 2)) { | |
| 1534 | + if (adev->accel_working) | |
| 1535 | + amdgpu_test_syncing(adev); | |
| 1536 | + else | |
| 1537 | + DRM_INFO("amdgpu: acceleration disabled, skipping sync tests\n"); | |
| 1538 | + } | |
| 1539 | + if (amdgpu_benchmarking) { | |
| 1540 | + if (adev->accel_working) | |
| 1541 | + amdgpu_benchmark(adev, amdgpu_benchmarking); | |
| 1542 | + else | |
| 1543 | + DRM_INFO("amdgpu: acceleration disabled, skipping benchmarks\n"); | |
| 1544 | + } | |
| 1545 | + | |
| 1546 | + /* enable clockgating, etc. after ib tests, etc. since some blocks require | |
| 1547 | + * explicit gating rather than handling it automatically. | |
| 1548 | + */ | |
| 1549 | + r = amdgpu_late_init(adev); | |
| 1550 | + if (r) | |
| 1551 | + return r; | |
| 1552 | + | |
| 1553 | + return 0; | |
| 1554 | +} | |
| 1555 | + | |
| 1556 | +static void amdgpu_debugfs_remove_files(struct amdgpu_device *adev); | |
| 1557 | + | |
| 1558 | +/** | |
| 1559 | + * amdgpu_device_fini - tear down the driver | |
| 1560 | + * | |
| 1561 | + * @adev: amdgpu_device pointer | |
| 1562 | + * | |
| 1563 | + * Tear down the driver info (all asics). | |
| 1564 | + * Called at driver shutdown. | |
| 1565 | + */ | |
| 1566 | +void amdgpu_device_fini(struct amdgpu_device *adev) | |
| 1567 | +{ | |
| 1568 | + int r; | |
| 1569 | + | |
| 1570 | + DRM_INFO("amdgpu: finishing device.\n"); | |
| 1571 | + adev->shutdown = true; | |
| 1572 | + /* evict vram memory */ | |
| 1573 | + amdgpu_bo_evict_vram(adev); | |
| 1574 | + amdgpu_ib_pool_fini(adev); | |
| 1575 | + amdgpu_fence_driver_fini(adev); | |
| 1576 | + amdgpu_fbdev_fini(adev); | |
| 1577 | + r = amdgpu_fini(adev); | |
| 1578 | + if (adev->ip_block_enabled) | |
| 1579 | + kfree(adev->ip_block_enabled); | |
| 1580 | + adev->ip_block_enabled = NULL; | |
| 1581 | + adev->accel_working = false; | |
| 1582 | + /* free i2c buses */ | |
| 1583 | + amdgpu_i2c_fini(adev); | |
| 1584 | + amdgpu_atombios_fini(adev); | |
| 1585 | + kfree(adev->bios); | |
| 1586 | + adev->bios = NULL; | |
| 1587 | + vga_switcheroo_unregister_client(adev->pdev); | |
| 1588 | + vga_client_register(adev->pdev, NULL, NULL, NULL); | |
| 1589 | + if (adev->rio_mem) | |
| 1590 | + pci_iounmap(adev->pdev, adev->rio_mem); | |
| 1591 | + adev->rio_mem = NULL; | |
| 1592 | + iounmap(adev->rmmio); | |
| 1593 | + adev->rmmio = NULL; | |
| 1594 | + amdgpu_doorbell_fini(adev); | |
| 1595 | + amdgpu_debugfs_regs_cleanup(adev); | |
| 1596 | + amdgpu_debugfs_remove_files(adev); | |
| 1597 | +} | |
| 1598 | + | |
| 1599 | + | |
| 1600 | +/* | |
| 1601 | + * Suspend & resume. | |
| 1602 | + */ | |
| 1603 | +/** | |
| 1604 | + * amdgpu_suspend_kms - initiate device suspend | |
| 1605 | + * | |
| 1606 | + * @pdev: drm dev pointer | |
| 1607 | + * @state: suspend state | |
| 1608 | + * | |
| 1609 | + * Puts the hw in the suspend state (all asics). | |
| 1610 | + * Returns 0 for success or an error on failure. | |
| 1611 | + * Called at driver suspend. | |
| 1612 | + */ | |
| 1613 | +int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon) | |
| 1614 | +{ | |
| 1615 | + struct amdgpu_device *adev; | |
| 1616 | + struct drm_crtc *crtc; | |
| 1617 | + struct drm_connector *connector; | |
| 1618 | + int i, r; | |
| 1619 | + bool force_completion = false; | |
| 1620 | + | |
| 1621 | + if (dev == NULL || dev->dev_private == NULL) { | |
| 1622 | + return -ENODEV; | |
| 1623 | + } | |
| 1624 | + | |
| 1625 | + adev = dev->dev_private; | |
| 1626 | + | |
| 1627 | + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) | |
| 1628 | + return 0; | |
| 1629 | + | |
| 1630 | + drm_kms_helper_poll_disable(dev); | |
| 1631 | + | |
| 1632 | + /* turn off display hw */ | |
| 1633 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 1634 | + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); | |
| 1635 | + } | |
| 1636 | + | |
| 1637 | + /* unpin the front buffers */ | |
| 1638 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | |
| 1639 | + struct amdgpu_framebuffer *rfb = to_amdgpu_framebuffer(crtc->primary->fb); | |
| 1640 | + struct amdgpu_bo *robj; | |
| 1641 | + | |
| 1642 | + if (rfb == NULL || rfb->obj == NULL) { | |
| 1643 | + continue; | |
| 1644 | + } | |
| 1645 | + robj = gem_to_amdgpu_bo(rfb->obj); | |
| 1646 | + /* don't unpin kernel fb objects */ | |
| 1647 | + if (!amdgpu_fbdev_robj_is_fb(adev, robj)) { | |
| 1648 | + r = amdgpu_bo_reserve(robj, false); | |
| 1649 | + if (r == 0) { | |
| 1650 | + amdgpu_bo_unpin(robj); | |
| 1651 | + amdgpu_bo_unreserve(robj); | |
| 1652 | + } | |
| 1653 | + } | |
| 1654 | + } | |
| 1655 | + /* evict vram memory */ | |
| 1656 | + amdgpu_bo_evict_vram(adev); | |
| 1657 | + | |
| 1658 | + /* wait for gpu to finish processing current batch */ | |
| 1659 | + for (i = 0; i < AMDGPU_MAX_RINGS; i++) { | |
| 1660 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 1661 | + if (!ring) | |
| 1662 | + continue; | |
| 1663 | + | |
| 1664 | + r = amdgpu_fence_wait_empty(ring); | |
| 1665 | + if (r) { | |
| 1666 | + /* delay GPU reset to resume */ | |
| 1667 | + force_completion = true; | |
| 1668 | + } | |
| 1669 | + } | |
| 1670 | + if (force_completion) { | |
| 1671 | + amdgpu_fence_driver_force_completion(adev); | |
| 1672 | + } | |
| 1673 | + | |
| 1674 | + r = amdgpu_suspend(adev); | |
| 1675 | + | |
| 1676 | + /* evict remaining vram memory */ | |
| 1677 | + amdgpu_bo_evict_vram(adev); | |
| 1678 | + | |
| 1679 | + pci_save_state(dev->pdev); | |
| 1680 | + if (suspend) { | |
| 1681 | + /* Shut down the device */ | |
| 1682 | + pci_disable_device(dev->pdev); | |
| 1683 | + pci_set_power_state(dev->pdev, PCI_D3hot); | |
| 1684 | + } | |
| 1685 | + | |
| 1686 | + if (fbcon) { | |
| 1687 | + console_lock(); | |
| 1688 | + amdgpu_fbdev_set_suspend(adev, 1); | |
| 1689 | + console_unlock(); | |
| 1690 | + } | |
| 1691 | + return 0; | |
| 1692 | +} | |
| 1693 | + | |
| 1694 | +/** | |
| 1695 | + * amdgpu_resume_kms - initiate device resume | |
| 1696 | + * | |
| 1697 | + * @pdev: drm dev pointer | |
| 1698 | + * | |
| 1699 | + * Bring the hw back to operating state (all asics). | |
| 1700 | + * Returns 0 for success or an error on failure. | |
| 1701 | + * Called at driver resume. | |
| 1702 | + */ | |
| 1703 | +int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon) | |
| 1704 | +{ | |
| 1705 | + struct drm_connector *connector; | |
| 1706 | + struct amdgpu_device *adev = dev->dev_private; | |
| 1707 | + int r; | |
| 1708 | + | |
| 1709 | + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) | |
| 1710 | + return 0; | |
| 1711 | + | |
| 1712 | + if (fbcon) { | |
| 1713 | + console_lock(); | |
| 1714 | + } | |
| 1715 | + if (resume) { | |
| 1716 | + pci_set_power_state(dev->pdev, PCI_D0); | |
| 1717 | + pci_restore_state(dev->pdev); | |
| 1718 | + if (pci_enable_device(dev->pdev)) { | |
| 1719 | + if (fbcon) | |
| 1720 | + console_unlock(); | |
| 1721 | + return -1; | |
| 1722 | + } | |
| 1723 | + } | |
| 1724 | + | |
| 1725 | + /* post card */ | |
| 1726 | + amdgpu_atom_asic_init(adev->mode_info.atom_context); | |
| 1727 | + | |
| 1728 | + r = amdgpu_resume(adev); | |
| 1729 | + | |
| 1730 | + r = amdgpu_ib_ring_tests(adev); | |
| 1731 | + if (r) | |
| 1732 | + DRM_ERROR("ib ring test failed (%d).\n", r); | |
| 1733 | + | |
| 1734 | + r = amdgpu_late_init(adev); | |
| 1735 | + if (r) | |
| 1736 | + return r; | |
| 1737 | + | |
| 1738 | + /* blat the mode back in */ | |
| 1739 | + if (fbcon) { | |
| 1740 | + drm_helper_resume_force_mode(dev); | |
| 1741 | + /* turn on display hw */ | |
| 1742 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 1743 | + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); | |
| 1744 | + } | |
| 1745 | + } | |
| 1746 | + | |
| 1747 | + drm_kms_helper_poll_enable(dev); | |
| 1748 | + | |
| 1749 | + if (fbcon) { | |
| 1750 | + amdgpu_fbdev_set_suspend(adev, 0); | |
| 1751 | + console_unlock(); | |
| 1752 | + } | |
| 1753 | + | |
| 1754 | + return 0; | |
| 1755 | +} | |
| 1756 | + | |
| 1757 | +/** | |
| 1758 | + * amdgpu_gpu_reset - reset the asic | |
| 1759 | + * | |
| 1760 | + * @adev: amdgpu device pointer | |
| 1761 | + * | |
| 1762 | + * Attempt the reset the GPU if it has hung (all asics). | |
| 1763 | + * Returns 0 for success or an error on failure. | |
| 1764 | + */ | |
| 1765 | +int amdgpu_gpu_reset(struct amdgpu_device *adev) | |
| 1766 | +{ | |
| 1767 | + unsigned ring_sizes[AMDGPU_MAX_RINGS]; | |
| 1768 | + uint32_t *ring_data[AMDGPU_MAX_RINGS]; | |
| 1769 | + | |
| 1770 | + bool saved = false; | |
| 1771 | + | |
| 1772 | + int i, r; | |
| 1773 | + int resched; | |
| 1774 | + | |
| 1775 | + down_write(&adev->exclusive_lock); | |
| 1776 | + | |
| 1777 | + if (!adev->needs_reset) { | |
| 1778 | + up_write(&adev->exclusive_lock); | |
| 1779 | + return 0; | |
| 1780 | + } | |
| 1781 | + | |
| 1782 | + adev->needs_reset = false; | |
| 1783 | + atomic_inc(&adev->gpu_reset_counter); | |
| 1784 | + | |
| 1785 | + /* block TTM */ | |
| 1786 | + resched = ttm_bo_lock_delayed_workqueue(&adev->mman.bdev); | |
| 1787 | + | |
| 1788 | + r = amdgpu_suspend(adev); | |
| 1789 | + | |
| 1790 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 1791 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 1792 | + if (!ring) | |
| 1793 | + continue; | |
| 1794 | + | |
| 1795 | + ring_sizes[i] = amdgpu_ring_backup(ring, &ring_data[i]); | |
| 1796 | + if (ring_sizes[i]) { | |
| 1797 | + saved = true; | |
| 1798 | + dev_info(adev->dev, "Saved %d dwords of commands " | |
| 1799 | + "on ring %d.\n", ring_sizes[i], i); | |
| 1800 | + } | |
| 1801 | + } | |
| 1802 | + | |
| 1803 | +retry: | |
| 1804 | + r = amdgpu_asic_reset(adev); | |
| 1805 | + if (!r) { | |
| 1806 | + dev_info(adev->dev, "GPU reset succeeded, trying to resume\n"); | |
| 1807 | + r = amdgpu_resume(adev); | |
| 1808 | + } | |
| 1809 | + | |
| 1810 | + if (!r) { | |
| 1811 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 1812 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 1813 | + if (!ring) | |
| 1814 | + continue; | |
| 1815 | + | |
| 1816 | + amdgpu_ring_restore(ring, ring_sizes[i], ring_data[i]); | |
| 1817 | + ring_sizes[i] = 0; | |
| 1818 | + ring_data[i] = NULL; | |
| 1819 | + } | |
| 1820 | + | |
| 1821 | + r = amdgpu_ib_ring_tests(adev); | |
| 1822 | + if (r) { | |
| 1823 | + dev_err(adev->dev, "ib ring test failed (%d).\n", r); | |
| 1824 | + if (saved) { | |
| 1825 | + saved = false; | |
| 1826 | + r = amdgpu_suspend(adev); | |
| 1827 | + goto retry; | |
| 1828 | + } | |
| 1829 | + } | |
| 1830 | + } else { | |
| 1831 | + amdgpu_fence_driver_force_completion(adev); | |
| 1832 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 1833 | + if (adev->rings[i]) | |
| 1834 | + kfree(ring_data[i]); | |
| 1835 | + } | |
| 1836 | + } | |
| 1837 | + | |
| 1838 | + drm_helper_resume_force_mode(adev->ddev); | |
| 1839 | + | |
| 1840 | + ttm_bo_unlock_delayed_workqueue(&adev->mman.bdev, resched); | |
| 1841 | + if (r) { | |
| 1842 | + /* bad news, how to tell it to userspace ? */ | |
| 1843 | + dev_info(adev->dev, "GPU reset failed\n"); | |
| 1844 | + } | |
| 1845 | + | |
| 1846 | + up_write(&adev->exclusive_lock); | |
| 1847 | + return r; | |
| 1848 | +} | |
| 1849 | + | |
| 1850 | + | |
| 1851 | +/* | |
| 1852 | + * Debugfs | |
| 1853 | + */ | |
| 1854 | +int amdgpu_debugfs_add_files(struct amdgpu_device *adev, | |
| 1855 | + struct drm_info_list *files, | |
| 1856 | + unsigned nfiles) | |
| 1857 | +{ | |
| 1858 | + unsigned i; | |
| 1859 | + | |
| 1860 | + for (i = 0; i < adev->debugfs_count; i++) { | |
| 1861 | + if (adev->debugfs[i].files == files) { | |
| 1862 | + /* Already registered */ | |
| 1863 | + return 0; | |
| 1864 | + } | |
| 1865 | + } | |
| 1866 | + | |
| 1867 | + i = adev->debugfs_count + 1; | |
| 1868 | + if (i > AMDGPU_DEBUGFS_MAX_COMPONENTS) { | |
| 1869 | + DRM_ERROR("Reached maximum number of debugfs components.\n"); | |
| 1870 | + DRM_ERROR("Report so we increase " | |
| 1871 | + "AMDGPU_DEBUGFS_MAX_COMPONENTS.\n"); | |
| 1872 | + return -EINVAL; | |
| 1873 | + } | |
| 1874 | + adev->debugfs[adev->debugfs_count].files = files; | |
| 1875 | + adev->debugfs[adev->debugfs_count].num_files = nfiles; | |
| 1876 | + adev->debugfs_count = i; | |
| 1877 | +#if defined(CONFIG_DEBUG_FS) | |
| 1878 | + drm_debugfs_create_files(files, nfiles, | |
| 1879 | + adev->ddev->control->debugfs_root, | |
| 1880 | + adev->ddev->control); | |
| 1881 | + drm_debugfs_create_files(files, nfiles, | |
| 1882 | + adev->ddev->primary->debugfs_root, | |
| 1883 | + adev->ddev->primary); | |
| 1884 | +#endif | |
| 1885 | + return 0; | |
| 1886 | +} | |
| 1887 | + | |
| 1888 | +static void amdgpu_debugfs_remove_files(struct amdgpu_device *adev) | |
| 1889 | +{ | |
| 1890 | +#if defined(CONFIG_DEBUG_FS) | |
| 1891 | + unsigned i; | |
| 1892 | + | |
| 1893 | + for (i = 0; i < adev->debugfs_count; i++) { | |
| 1894 | + drm_debugfs_remove_files(adev->debugfs[i].files, | |
| 1895 | + adev->debugfs[i].num_files, | |
| 1896 | + adev->ddev->control); | |
| 1897 | + drm_debugfs_remove_files(adev->debugfs[i].files, | |
| 1898 | + adev->debugfs[i].num_files, | |
| 1899 | + adev->ddev->primary); | |
| 1900 | + } | |
| 1901 | +#endif | |
| 1902 | +} | |
| 1903 | + | |
| 1904 | +#if defined(CONFIG_DEBUG_FS) | |
| 1905 | + | |
| 1906 | +static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, | |
| 1907 | + size_t size, loff_t *pos) | |
| 1908 | +{ | |
| 1909 | + struct amdgpu_device *adev = f->f_inode->i_private; | |
| 1910 | + ssize_t result = 0; | |
| 1911 | + int r; | |
| 1912 | + | |
| 1913 | + if (size & 0x3 || *pos & 0x3) | |
| 1914 | + return -EINVAL; | |
| 1915 | + | |
| 1916 | + while (size) { | |
| 1917 | + uint32_t value; | |
| 1918 | + | |
| 1919 | + if (*pos > adev->rmmio_size) | |
| 1920 | + return result; | |
| 1921 | + | |
| 1922 | + value = RREG32(*pos >> 2); | |
| 1923 | + r = put_user(value, (uint32_t *)buf); | |
| 1924 | + if (r) | |
| 1925 | + return r; | |
| 1926 | + | |
| 1927 | + result += 4; | |
| 1928 | + buf += 4; | |
| 1929 | + *pos += 4; | |
| 1930 | + size -= 4; | |
| 1931 | + } | |
| 1932 | + | |
| 1933 | + return result; | |
| 1934 | +} | |
| 1935 | + | |
| 1936 | +static ssize_t amdgpu_debugfs_regs_write(struct file *f, const char __user *buf, | |
| 1937 | + size_t size, loff_t *pos) | |
| 1938 | +{ | |
| 1939 | + struct amdgpu_device *adev = f->f_inode->i_private; | |
| 1940 | + ssize_t result = 0; | |
| 1941 | + int r; | |
| 1942 | + | |
| 1943 | + if (size & 0x3 || *pos & 0x3) | |
| 1944 | + return -EINVAL; | |
| 1945 | + | |
| 1946 | + while (size) { | |
| 1947 | + uint32_t value; | |
| 1948 | + | |
| 1949 | + if (*pos > adev->rmmio_size) | |
| 1950 | + return result; | |
| 1951 | + | |
| 1952 | + r = get_user(value, (uint32_t *)buf); | |
| 1953 | + if (r) | |
| 1954 | + return r; | |
| 1955 | + | |
| 1956 | + WREG32(*pos >> 2, value); | |
| 1957 | + | |
| 1958 | + result += 4; | |
| 1959 | + buf += 4; | |
| 1960 | + *pos += 4; | |
| 1961 | + size -= 4; | |
| 1962 | + } | |
| 1963 | + | |
| 1964 | + return result; | |
| 1965 | +} | |
| 1966 | + | |
| 1967 | +static const struct file_operations amdgpu_debugfs_regs_fops = { | |
| 1968 | + .owner = THIS_MODULE, | |
| 1969 | + .read = amdgpu_debugfs_regs_read, | |
| 1970 | + .write = amdgpu_debugfs_regs_write, | |
| 1971 | + .llseek = default_llseek | |
| 1972 | +}; | |
| 1973 | + | |
| 1974 | +static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) | |
| 1975 | +{ | |
| 1976 | + struct drm_minor *minor = adev->ddev->primary; | |
| 1977 | + struct dentry *ent, *root = minor->debugfs_root; | |
| 1978 | + | |
| 1979 | + ent = debugfs_create_file("amdgpu_regs", S_IFREG | S_IRUGO, root, | |
| 1980 | + adev, &amdgpu_debugfs_regs_fops); | |
| 1981 | + if (IS_ERR(ent)) | |
| 1982 | + return PTR_ERR(ent); | |
| 1983 | + i_size_write(ent->d_inode, adev->rmmio_size); | |
| 1984 | + adev->debugfs_regs = ent; | |
| 1985 | + | |
| 1986 | + return 0; | |
| 1987 | +} | |
| 1988 | + | |
| 1989 | +static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev) | |
| 1990 | +{ | |
| 1991 | + debugfs_remove(adev->debugfs_regs); | |
| 1992 | + adev->debugfs_regs = NULL; | |
| 1993 | +} | |
| 1994 | + | |
| 1995 | +int amdgpu_debugfs_init(struct drm_minor *minor) | |
| 1996 | +{ | |
| 1997 | + return 0; | |
| 1998 | +} | |
| 1999 | + | |
| 2000 | +void amdgpu_debugfs_cleanup(struct drm_minor *minor) | |
| 2001 | +{ | |
| 2002 | +} | |
| 2003 | +#endif | |
| @@ -0,0 +1,832 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2007-8 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the "Software"), | |
| 7 | + * to deal in the Software without restriction, including without limitation | |
| 8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 10 | + * Software is furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in | |
| 13 | + * all copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 18 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 19 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 20 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 21 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 22 | + * | |
| 23 | + * Authors: Dave Airlie | |
| 24 | + * Alex Deucher | |
| 25 | + */ | |
| 26 | +#include <drm/drmP.h> | |
| 27 | +#include <drm/amdgpu_drm.h> | |
| 28 | +#include "amdgpu.h" | |
| 29 | +#include "amdgpu_i2c.h" | |
| 30 | +#include "atom.h" | |
| 31 | +#include "amdgpu_connectors.h" | |
| 32 | +#include <asm/div64.h> | |
| 33 | + | |
| 34 | +#include <linux/pm_runtime.h> | |
| 35 | +#include <drm/drm_crtc_helper.h> | |
| 36 | +#include <drm/drm_edid.h> | |
| 37 | + | |
| 38 | + | |
| 39 | +static void amdgpu_flip_work_func(struct work_struct *__work) | |
| 40 | +{ | |
| 41 | + struct amdgpu_flip_work *work = | |
| 42 | + container_of(__work, struct amdgpu_flip_work, flip_work); | |
| 43 | + struct amdgpu_device *adev = work->adev; | |
| 44 | + struct amdgpu_crtc *amdgpuCrtc = adev->mode_info.crtcs[work->crtc_id]; | |
| 45 | + | |
| 46 | + struct drm_crtc *crtc = &amdgpuCrtc->base; | |
| 47 | + struct amdgpu_fence *fence; | |
| 48 | + unsigned long flags; | |
| 49 | + int r; | |
| 50 | + | |
| 51 | + down_read(&adev->exclusive_lock); | |
| 52 | + if (work->fence) { | |
| 53 | + fence = to_amdgpu_fence(work->fence); | |
| 54 | + if (fence) { | |
| 55 | + r = amdgpu_fence_wait(fence, false); | |
| 56 | + if (r == -EDEADLK) { | |
| 57 | + up_read(&adev->exclusive_lock); | |
| 58 | + r = amdgpu_gpu_reset(adev); | |
| 59 | + down_read(&adev->exclusive_lock); | |
| 60 | + } | |
| 61 | + } else | |
| 62 | + r = fence_wait(work->fence, false); | |
| 63 | + | |
| 64 | + if (r) | |
| 65 | + DRM_ERROR("failed to wait on page flip fence (%d)!\n", r); | |
| 66 | + | |
| 67 | + /* We continue with the page flip even if we failed to wait on | |
| 68 | + * the fence, otherwise the DRM core and userspace will be | |
| 69 | + * confused about which BO the CRTC is scanning out | |
| 70 | + */ | |
| 71 | + | |
| 72 | + fence_put(work->fence); | |
| 73 | + work->fence = NULL; | |
| 74 | + } | |
| 75 | + | |
| 76 | + /* We borrow the event spin lock for protecting flip_status */ | |
| 77 | + spin_lock_irqsave(&crtc->dev->event_lock, flags); | |
| 78 | + | |
| 79 | + /* set the proper interrupt */ | |
| 80 | + amdgpu_irq_get(adev, &adev->pageflip_irq, work->crtc_id); | |
| 81 | + /* do the flip (mmio) */ | |
| 82 | + adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base); | |
| 83 | + /* set the flip status */ | |
| 84 | + amdgpuCrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; | |
| 85 | + | |
| 86 | + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
| 87 | + up_read(&adev->exclusive_lock); | |
| 88 | +} | |
| 89 | + | |
| 90 | +/* | |
| 91 | + * Handle unpin events outside the interrupt handler proper. | |
| 92 | + */ | |
| 93 | +static void amdgpu_unpin_work_func(struct work_struct *__work) | |
| 94 | +{ | |
| 95 | + struct amdgpu_flip_work *work = | |
| 96 | + container_of(__work, struct amdgpu_flip_work, unpin_work); | |
| 97 | + int r; | |
| 98 | + | |
| 99 | + /* unpin of the old buffer */ | |
| 100 | + r = amdgpu_bo_reserve(work->old_rbo, false); | |
| 101 | + if (likely(r == 0)) { | |
| 102 | + r = amdgpu_bo_unpin(work->old_rbo); | |
| 103 | + if (unlikely(r != 0)) { | |
| 104 | + DRM_ERROR("failed to unpin buffer after flip\n"); | |
| 105 | + } | |
| 106 | + amdgpu_bo_unreserve(work->old_rbo); | |
| 107 | + } else | |
| 108 | + DRM_ERROR("failed to reserve buffer after flip\n"); | |
| 109 | + | |
| 110 | + drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); | |
| 111 | + kfree(work); | |
| 112 | +} | |
| 113 | + | |
| 114 | +int amdgpu_crtc_page_flip(struct drm_crtc *crtc, | |
| 115 | + struct drm_framebuffer *fb, | |
| 116 | + struct drm_pending_vblank_event *event, | |
| 117 | + uint32_t page_flip_flags) | |
| 118 | +{ | |
| 119 | + struct drm_device *dev = crtc->dev; | |
| 120 | + struct amdgpu_device *adev = dev->dev_private; | |
| 121 | + struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); | |
| 122 | + struct amdgpu_framebuffer *old_amdgpu_fb; | |
| 123 | + struct amdgpu_framebuffer *new_amdgpu_fb; | |
| 124 | + struct drm_gem_object *obj; | |
| 125 | + struct amdgpu_flip_work *work; | |
| 126 | + struct amdgpu_bo *new_rbo; | |
| 127 | + unsigned long flags; | |
| 128 | + u64 tiling_flags; | |
| 129 | + u64 base; | |
| 130 | + int r; | |
| 131 | + | |
| 132 | + work = kzalloc(sizeof *work, GFP_KERNEL); | |
| 133 | + if (work == NULL) | |
| 134 | + return -ENOMEM; | |
| 135 | + | |
| 136 | + INIT_WORK(&work->flip_work, amdgpu_flip_work_func); | |
| 137 | + INIT_WORK(&work->unpin_work, amdgpu_unpin_work_func); | |
| 138 | + | |
| 139 | + work->event = event; | |
| 140 | + work->adev = adev; | |
| 141 | + work->crtc_id = amdgpu_crtc->crtc_id; | |
| 142 | + | |
| 143 | + /* schedule unpin of the old buffer */ | |
| 144 | + old_amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb); | |
| 145 | + obj = old_amdgpu_fb->obj; | |
| 146 | + | |
| 147 | + /* take a reference to the old object */ | |
| 148 | + drm_gem_object_reference(obj); | |
| 149 | + work->old_rbo = gem_to_amdgpu_bo(obj); | |
| 150 | + | |
| 151 | + new_amdgpu_fb = to_amdgpu_framebuffer(fb); | |
| 152 | + obj = new_amdgpu_fb->obj; | |
| 153 | + new_rbo = gem_to_amdgpu_bo(obj); | |
| 154 | + | |
| 155 | + /* pin the new buffer */ | |
| 156 | + r = amdgpu_bo_reserve(new_rbo, false); | |
| 157 | + if (unlikely(r != 0)) { | |
| 158 | + DRM_ERROR("failed to reserve new rbo buffer before flip\n"); | |
| 159 | + goto cleanup; | |
| 160 | + } | |
| 161 | + | |
| 162 | + r = amdgpu_bo_pin_restricted(new_rbo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, &base); | |
| 163 | + if (unlikely(r != 0)) { | |
| 164 | + amdgpu_bo_unreserve(new_rbo); | |
| 165 | + r = -EINVAL; | |
| 166 | + DRM_ERROR("failed to pin new rbo buffer before flip\n"); | |
| 167 | + goto cleanup; | |
| 168 | + } | |
| 169 | + | |
| 170 | + work->fence = fence_get(reservation_object_get_excl(new_rbo->tbo.resv)); | |
| 171 | + amdgpu_bo_get_tiling_flags(new_rbo, &tiling_flags); | |
| 172 | + amdgpu_bo_unreserve(new_rbo); | |
| 173 | + | |
| 174 | + work->base = base; | |
| 175 | + | |
| 176 | + r = drm_vblank_get(crtc->dev, amdgpu_crtc->crtc_id); | |
| 177 | + if (r) { | |
| 178 | + DRM_ERROR("failed to get vblank before flip\n"); | |
| 179 | + goto pflip_cleanup; | |
| 180 | + } | |
| 181 | + | |
| 182 | + /* we borrow the event spin lock for protecting flip_wrok */ | |
| 183 | + spin_lock_irqsave(&crtc->dev->event_lock, flags); | |
| 184 | + if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_NONE) { | |
| 185 | + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); | |
| 186 | + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
| 187 | + r = -EBUSY; | |
| 188 | + goto vblank_cleanup; | |
| 189 | + } | |
| 190 | + | |
| 191 | + amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING; | |
| 192 | + amdgpu_crtc->pflip_works = work; | |
| 193 | + | |
| 194 | + /* update crtc fb */ | |
| 195 | + crtc->primary->fb = fb; | |
| 196 | + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
| 197 | + queue_work(amdgpu_crtc->pflip_queue, &work->flip_work); | |
| 198 | + return 0; | |
| 199 | + | |
| 200 | +vblank_cleanup: | |
| 201 | + drm_vblank_put(crtc->dev, amdgpu_crtc->crtc_id); | |
| 202 | + | |
| 203 | +pflip_cleanup: | |
| 204 | + if (unlikely(amdgpu_bo_reserve(new_rbo, false) != 0)) { | |
| 205 | + DRM_ERROR("failed to reserve new rbo in error path\n"); | |
| 206 | + goto cleanup; | |
| 207 | + } | |
| 208 | + if (unlikely(amdgpu_bo_unpin(new_rbo) != 0)) { | |
| 209 | + DRM_ERROR("failed to unpin new rbo in error path\n"); | |
| 210 | + } | |
| 211 | + amdgpu_bo_unreserve(new_rbo); | |
| 212 | + | |
| 213 | +cleanup: | |
| 214 | + drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); | |
| 215 | + fence_put(work->fence); | |
| 216 | + kfree(work); | |
| 217 | + | |
| 218 | + return r; | |
| 219 | +} | |
| 220 | + | |
| 221 | +int amdgpu_crtc_set_config(struct drm_mode_set *set) | |
| 222 | +{ | |
| 223 | + struct drm_device *dev; | |
| 224 | + struct amdgpu_device *adev; | |
| 225 | + struct drm_crtc *crtc; | |
| 226 | + bool active = false; | |
| 227 | + int ret; | |
| 228 | + | |
| 229 | + if (!set || !set->crtc) | |
| 230 | + return -EINVAL; | |
| 231 | + | |
| 232 | + dev = set->crtc->dev; | |
| 233 | + | |
| 234 | + ret = pm_runtime_get_sync(dev->dev); | |
| 235 | + if (ret < 0) | |
| 236 | + return ret; | |
| 237 | + | |
| 238 | + ret = drm_crtc_helper_set_config(set); | |
| 239 | + | |
| 240 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | |
| 241 | + if (crtc->enabled) | |
| 242 | + active = true; | |
| 243 | + | |
| 244 | + pm_runtime_mark_last_busy(dev->dev); | |
| 245 | + | |
| 246 | + adev = dev->dev_private; | |
| 247 | + /* if we have active crtcs and we don't have a power ref, | |
| 248 | + take the current one */ | |
| 249 | + if (active && !adev->have_disp_power_ref) { | |
| 250 | + adev->have_disp_power_ref = true; | |
| 251 | + return ret; | |
| 252 | + } | |
| 253 | + /* if we have no active crtcs, then drop the power ref | |
| 254 | + we got before */ | |
| 255 | + if (!active && adev->have_disp_power_ref) { | |
| 256 | + pm_runtime_put_autosuspend(dev->dev); | |
| 257 | + adev->have_disp_power_ref = false; | |
| 258 | + } | |
| 259 | + | |
| 260 | + /* drop the power reference we got coming in here */ | |
| 261 | + pm_runtime_put_autosuspend(dev->dev); | |
| 262 | + return ret; | |
| 263 | +} | |
| 264 | + | |
| 265 | +static const char *encoder_names[38] = { | |
| 266 | + "NONE", | |
| 267 | + "INTERNAL_LVDS", | |
| 268 | + "INTERNAL_TMDS1", | |
| 269 | + "INTERNAL_TMDS2", | |
| 270 | + "INTERNAL_DAC1", | |
| 271 | + "INTERNAL_DAC2", | |
| 272 | + "INTERNAL_SDVOA", | |
| 273 | + "INTERNAL_SDVOB", | |
| 274 | + "SI170B", | |
| 275 | + "CH7303", | |
| 276 | + "CH7301", | |
| 277 | + "INTERNAL_DVO1", | |
| 278 | + "EXTERNAL_SDVOA", | |
| 279 | + "EXTERNAL_SDVOB", | |
| 280 | + "TITFP513", | |
| 281 | + "INTERNAL_LVTM1", | |
| 282 | + "VT1623", | |
| 283 | + "HDMI_SI1930", | |
| 284 | + "HDMI_INTERNAL", | |
| 285 | + "INTERNAL_KLDSCP_TMDS1", | |
| 286 | + "INTERNAL_KLDSCP_DVO1", | |
| 287 | + "INTERNAL_KLDSCP_DAC1", | |
| 288 | + "INTERNAL_KLDSCP_DAC2", | |
| 289 | + "SI178", | |
| 290 | + "MVPU_FPGA", | |
| 291 | + "INTERNAL_DDI", | |
| 292 | + "VT1625", | |
| 293 | + "HDMI_SI1932", | |
| 294 | + "DP_AN9801", | |
| 295 | + "DP_DP501", | |
| 296 | + "INTERNAL_UNIPHY", | |
| 297 | + "INTERNAL_KLDSCP_LVTMA", | |
| 298 | + "INTERNAL_UNIPHY1", | |
| 299 | + "INTERNAL_UNIPHY2", | |
| 300 | + "NUTMEG", | |
| 301 | + "TRAVIS", | |
| 302 | + "INTERNAL_VCE", | |
| 303 | + "INTERNAL_UNIPHY3", | |
| 304 | +}; | |
| 305 | + | |
| 306 | +static const char *hpd_names[6] = { | |
| 307 | + "HPD1", | |
| 308 | + "HPD2", | |
| 309 | + "HPD3", | |
| 310 | + "HPD4", | |
| 311 | + "HPD5", | |
| 312 | + "HPD6", | |
| 313 | +}; | |
| 314 | + | |
| 315 | +void amdgpu_print_display_setup(struct drm_device *dev) | |
| 316 | +{ | |
| 317 | + struct drm_connector *connector; | |
| 318 | + struct amdgpu_connector *amdgpu_connector; | |
| 319 | + struct drm_encoder *encoder; | |
| 320 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 321 | + uint32_t devices; | |
| 322 | + int i = 0; | |
| 323 | + | |
| 324 | + DRM_INFO("AMDGPU Display Connectors\n"); | |
| 325 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 326 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 327 | + DRM_INFO("Connector %d:\n", i); | |
| 328 | + DRM_INFO(" %s\n", connector->name); | |
| 329 | + if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE) | |
| 330 | + DRM_INFO(" %s\n", hpd_names[amdgpu_connector->hpd.hpd]); | |
| 331 | + if (amdgpu_connector->ddc_bus) { | |
| 332 | + DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", | |
| 333 | + amdgpu_connector->ddc_bus->rec.mask_clk_reg, | |
| 334 | + amdgpu_connector->ddc_bus->rec.mask_data_reg, | |
| 335 | + amdgpu_connector->ddc_bus->rec.a_clk_reg, | |
| 336 | + amdgpu_connector->ddc_bus->rec.a_data_reg, | |
| 337 | + amdgpu_connector->ddc_bus->rec.en_clk_reg, | |
| 338 | + amdgpu_connector->ddc_bus->rec.en_data_reg, | |
| 339 | + amdgpu_connector->ddc_bus->rec.y_clk_reg, | |
| 340 | + amdgpu_connector->ddc_bus->rec.y_data_reg); | |
| 341 | + if (amdgpu_connector->router.ddc_valid) | |
| 342 | + DRM_INFO(" DDC Router 0x%x/0x%x\n", | |
| 343 | + amdgpu_connector->router.ddc_mux_control_pin, | |
| 344 | + amdgpu_connector->router.ddc_mux_state); | |
| 345 | + if (amdgpu_connector->router.cd_valid) | |
| 346 | + DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", | |
| 347 | + amdgpu_connector->router.cd_mux_control_pin, | |
| 348 | + amdgpu_connector->router.cd_mux_state); | |
| 349 | + } else { | |
| 350 | + if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || | |
| 351 | + connector->connector_type == DRM_MODE_CONNECTOR_DVII || | |
| 352 | + connector->connector_type == DRM_MODE_CONNECTOR_DVID || | |
| 353 | + connector->connector_type == DRM_MODE_CONNECTOR_DVIA || | |
| 354 | + connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || | |
| 355 | + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) | |
| 356 | + DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n"); | |
| 357 | + } | |
| 358 | + DRM_INFO(" Encoders:\n"); | |
| 359 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
| 360 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 361 | + devices = amdgpu_encoder->devices & amdgpu_connector->devices; | |
| 362 | + if (devices) { | |
| 363 | + if (devices & ATOM_DEVICE_CRT1_SUPPORT) | |
| 364 | + DRM_INFO(" CRT1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 365 | + if (devices & ATOM_DEVICE_CRT2_SUPPORT) | |
| 366 | + DRM_INFO(" CRT2: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 367 | + if (devices & ATOM_DEVICE_LCD1_SUPPORT) | |
| 368 | + DRM_INFO(" LCD1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 369 | + if (devices & ATOM_DEVICE_DFP1_SUPPORT) | |
| 370 | + DRM_INFO(" DFP1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 371 | + if (devices & ATOM_DEVICE_DFP2_SUPPORT) | |
| 372 | + DRM_INFO(" DFP2: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 373 | + if (devices & ATOM_DEVICE_DFP3_SUPPORT) | |
| 374 | + DRM_INFO(" DFP3: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 375 | + if (devices & ATOM_DEVICE_DFP4_SUPPORT) | |
| 376 | + DRM_INFO(" DFP4: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 377 | + if (devices & ATOM_DEVICE_DFP5_SUPPORT) | |
| 378 | + DRM_INFO(" DFP5: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 379 | + if (devices & ATOM_DEVICE_DFP6_SUPPORT) | |
| 380 | + DRM_INFO(" DFP6: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 381 | + if (devices & ATOM_DEVICE_TV1_SUPPORT) | |
| 382 | + DRM_INFO(" TV1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 383 | + if (devices & ATOM_DEVICE_CV_SUPPORT) | |
| 384 | + DRM_INFO(" CV: %s\n", encoder_names[amdgpu_encoder->encoder_id]); | |
| 385 | + } | |
| 386 | + } | |
| 387 | + i++; | |
| 388 | + } | |
| 389 | +} | |
| 390 | + | |
| 391 | +/** | |
| 392 | + * amdgpu_ddc_probe | |
| 393 | + * | |
| 394 | + */ | |
| 395 | +bool amdgpu_ddc_probe(struct amdgpu_connector *amdgpu_connector, | |
| 396 | + bool use_aux) | |
| 397 | +{ | |
| 398 | + u8 out = 0x0; | |
| 399 | + u8 buf[8]; | |
| 400 | + int ret; | |
| 401 | + struct i2c_msg msgs[] = { | |
| 402 | + { | |
| 403 | + .addr = DDC_ADDR, | |
| 404 | + .flags = 0, | |
| 405 | + .len = 1, | |
| 406 | + .buf = &out, | |
| 407 | + }, | |
| 408 | + { | |
| 409 | + .addr = DDC_ADDR, | |
| 410 | + .flags = I2C_M_RD, | |
| 411 | + .len = 8, | |
| 412 | + .buf = buf, | |
| 413 | + } | |
| 414 | + }; | |
| 415 | + | |
| 416 | + /* on hw with routers, select right port */ | |
| 417 | + if (amdgpu_connector->router.ddc_valid) | |
| 418 | + amdgpu_i2c_router_select_ddc_port(amdgpu_connector); | |
| 419 | + | |
| 420 | + if (use_aux) { | |
| 421 | + ret = i2c_transfer(&amdgpu_connector->ddc_bus->aux.ddc, msgs, 2); | |
| 422 | + } else { | |
| 423 | + ret = i2c_transfer(&amdgpu_connector->ddc_bus->adapter, msgs, 2); | |
| 424 | + } | |
| 425 | + | |
| 426 | + if (ret != 2) | |
| 427 | + /* Couldn't find an accessible DDC on this connector */ | |
| 428 | + return false; | |
| 429 | + /* Probe also for valid EDID header | |
| 430 | + * EDID header starts with: | |
| 431 | + * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. | |
| 432 | + * Only the first 6 bytes must be valid as | |
| 433 | + * drm_edid_block_valid() can fix the last 2 bytes */ | |
| 434 | + if (drm_edid_header_is_valid(buf) < 6) { | |
| 435 | + /* Couldn't find an accessible EDID on this | |
| 436 | + * connector */ | |
| 437 | + return false; | |
| 438 | + } | |
| 439 | + return true; | |
| 440 | +} | |
| 441 | + | |
| 442 | +static void amdgpu_user_framebuffer_destroy(struct drm_framebuffer *fb) | |
| 443 | +{ | |
| 444 | + struct amdgpu_framebuffer *amdgpu_fb = to_amdgpu_framebuffer(fb); | |
| 445 | + | |
| 446 | + if (amdgpu_fb->obj) { | |
| 447 | + drm_gem_object_unreference_unlocked(amdgpu_fb->obj); | |
| 448 | + } | |
| 449 | + drm_framebuffer_cleanup(fb); | |
| 450 | + kfree(amdgpu_fb); | |
| 451 | +} | |
| 452 | + | |
| 453 | +static int amdgpu_user_framebuffer_create_handle(struct drm_framebuffer *fb, | |
| 454 | + struct drm_file *file_priv, | |
| 455 | + unsigned int *handle) | |
| 456 | +{ | |
| 457 | + struct amdgpu_framebuffer *amdgpu_fb = to_amdgpu_framebuffer(fb); | |
| 458 | + | |
| 459 | + return drm_gem_handle_create(file_priv, amdgpu_fb->obj, handle); | |
| 460 | +} | |
| 461 | + | |
| 462 | +static const struct drm_framebuffer_funcs amdgpu_fb_funcs = { | |
| 463 | + .destroy = amdgpu_user_framebuffer_destroy, | |
| 464 | + .create_handle = amdgpu_user_framebuffer_create_handle, | |
| 465 | +}; | |
| 466 | + | |
| 467 | +int | |
| 468 | +amdgpu_framebuffer_init(struct drm_device *dev, | |
| 469 | + struct amdgpu_framebuffer *rfb, | |
| 470 | + struct drm_mode_fb_cmd2 *mode_cmd, | |
| 471 | + struct drm_gem_object *obj) | |
| 472 | +{ | |
| 473 | + int ret; | |
| 474 | + rfb->obj = obj; | |
| 475 | + drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); | |
| 476 | + ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs); | |
| 477 | + if (ret) { | |
| 478 | + rfb->obj = NULL; | |
| 479 | + return ret; | |
| 480 | + } | |
| 481 | + return 0; | |
| 482 | +} | |
| 483 | + | |
| 484 | +static struct drm_framebuffer * | |
| 485 | +amdgpu_user_framebuffer_create(struct drm_device *dev, | |
| 486 | + struct drm_file *file_priv, | |
| 487 | + struct drm_mode_fb_cmd2 *mode_cmd) | |
| 488 | +{ | |
| 489 | + struct drm_gem_object *obj; | |
| 490 | + struct amdgpu_framebuffer *amdgpu_fb; | |
| 491 | + int ret; | |
| 492 | + | |
| 493 | + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); | |
| 494 | + if (obj == NULL) { | |
| 495 | + dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, " | |
| 496 | + "can't create framebuffer\n", mode_cmd->handles[0]); | |
| 497 | + return ERR_PTR(-ENOENT); | |
| 498 | + } | |
| 499 | + | |
| 500 | + amdgpu_fb = kzalloc(sizeof(*amdgpu_fb), GFP_KERNEL); | |
| 501 | + if (amdgpu_fb == NULL) { | |
| 502 | + drm_gem_object_unreference_unlocked(obj); | |
| 503 | + return ERR_PTR(-ENOMEM); | |
| 504 | + } | |
| 505 | + | |
| 506 | + ret = amdgpu_framebuffer_init(dev, amdgpu_fb, mode_cmd, obj); | |
| 507 | + if (ret) { | |
| 508 | + kfree(amdgpu_fb); | |
| 509 | + drm_gem_object_unreference_unlocked(obj); | |
| 510 | + return ERR_PTR(ret); | |
| 511 | + } | |
| 512 | + | |
| 513 | + return &amdgpu_fb->base; | |
| 514 | +} | |
| 515 | + | |
| 516 | +static void amdgpu_output_poll_changed(struct drm_device *dev) | |
| 517 | +{ | |
| 518 | + struct amdgpu_device *adev = dev->dev_private; | |
| 519 | + amdgpu_fb_output_poll_changed(adev); | |
| 520 | +} | |
| 521 | + | |
| 522 | +const struct drm_mode_config_funcs amdgpu_mode_funcs = { | |
| 523 | + .fb_create = amdgpu_user_framebuffer_create, | |
| 524 | + .output_poll_changed = amdgpu_output_poll_changed | |
| 525 | +}; | |
| 526 | + | |
| 527 | +static struct drm_prop_enum_list amdgpu_underscan_enum_list[] = | |
| 528 | +{ { UNDERSCAN_OFF, "off" }, | |
| 529 | + { UNDERSCAN_ON, "on" }, | |
| 530 | + { UNDERSCAN_AUTO, "auto" }, | |
| 531 | +}; | |
| 532 | + | |
| 533 | +static struct drm_prop_enum_list amdgpu_audio_enum_list[] = | |
| 534 | +{ { AMDGPU_AUDIO_DISABLE, "off" }, | |
| 535 | + { AMDGPU_AUDIO_ENABLE, "on" }, | |
| 536 | + { AMDGPU_AUDIO_AUTO, "auto" }, | |
| 537 | +}; | |
| 538 | + | |
| 539 | +/* XXX support different dither options? spatial, temporal, both, etc. */ | |
| 540 | +static struct drm_prop_enum_list amdgpu_dither_enum_list[] = | |
| 541 | +{ { AMDGPU_FMT_DITHER_DISABLE, "off" }, | |
| 542 | + { AMDGPU_FMT_DITHER_ENABLE, "on" }, | |
| 543 | +}; | |
| 544 | + | |
| 545 | +int amdgpu_modeset_create_props(struct amdgpu_device *adev) | |
| 546 | +{ | |
| 547 | + int sz; | |
| 548 | + | |
| 549 | + if (adev->is_atom_bios) { | |
| 550 | + adev->mode_info.coherent_mode_property = | |
| 551 | + drm_property_create_range(adev->ddev, 0 , "coherent", 0, 1); | |
| 552 | + if (!adev->mode_info.coherent_mode_property) | |
| 553 | + return -ENOMEM; | |
| 554 | + } | |
| 555 | + | |
| 556 | + adev->mode_info.load_detect_property = | |
| 557 | + drm_property_create_range(adev->ddev, 0, "load detection", 0, 1); | |
| 558 | + if (!adev->mode_info.load_detect_property) | |
| 559 | + return -ENOMEM; | |
| 560 | + | |
| 561 | + drm_mode_create_scaling_mode_property(adev->ddev); | |
| 562 | + | |
| 563 | + sz = ARRAY_SIZE(amdgpu_underscan_enum_list); | |
| 564 | + adev->mode_info.underscan_property = | |
| 565 | + drm_property_create_enum(adev->ddev, 0, | |
| 566 | + "underscan", | |
| 567 | + amdgpu_underscan_enum_list, sz); | |
| 568 | + | |
| 569 | + adev->mode_info.underscan_hborder_property = | |
| 570 | + drm_property_create_range(adev->ddev, 0, | |
| 571 | + "underscan hborder", 0, 128); | |
| 572 | + if (!adev->mode_info.underscan_hborder_property) | |
| 573 | + return -ENOMEM; | |
| 574 | + | |
| 575 | + adev->mode_info.underscan_vborder_property = | |
| 576 | + drm_property_create_range(adev->ddev, 0, | |
| 577 | + "underscan vborder", 0, 128); | |
| 578 | + if (!adev->mode_info.underscan_vborder_property) | |
| 579 | + return -ENOMEM; | |
| 580 | + | |
| 581 | + sz = ARRAY_SIZE(amdgpu_audio_enum_list); | |
| 582 | + adev->mode_info.audio_property = | |
| 583 | + drm_property_create_enum(adev->ddev, 0, | |
| 584 | + "audio", | |
| 585 | + amdgpu_audio_enum_list, sz); | |
| 586 | + | |
| 587 | + sz = ARRAY_SIZE(amdgpu_dither_enum_list); | |
| 588 | + adev->mode_info.dither_property = | |
| 589 | + drm_property_create_enum(adev->ddev, 0, | |
| 590 | + "dither", | |
| 591 | + amdgpu_dither_enum_list, sz); | |
| 592 | + | |
| 593 | + return 0; | |
| 594 | +} | |
| 595 | + | |
| 596 | +void amdgpu_update_display_priority(struct amdgpu_device *adev) | |
| 597 | +{ | |
| 598 | + /* adjustment options for the display watermarks */ | |
| 599 | + if ((amdgpu_disp_priority == 0) || (amdgpu_disp_priority > 2)) | |
| 600 | + adev->mode_info.disp_priority = 0; | |
| 601 | + else | |
| 602 | + adev->mode_info.disp_priority = amdgpu_disp_priority; | |
| 603 | + | |
| 604 | +} | |
| 605 | + | |
| 606 | +static bool is_hdtv_mode(const struct drm_display_mode *mode) | |
| 607 | +{ | |
| 608 | + /* try and guess if this is a tv or a monitor */ | |
| 609 | + if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ | |
| 610 | + (mode->vdisplay == 576) || /* 576p */ | |
| 611 | + (mode->vdisplay == 720) || /* 720p */ | |
| 612 | + (mode->vdisplay == 1080)) /* 1080p */ | |
| 613 | + return true; | |
| 614 | + else | |
| 615 | + return false; | |
| 616 | +} | |
| 617 | + | |
| 618 | +bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc, | |
| 619 | + const struct drm_display_mode *mode, | |
| 620 | + struct drm_display_mode *adjusted_mode) | |
| 621 | +{ | |
| 622 | + struct drm_device *dev = crtc->dev; | |
| 623 | + struct drm_encoder *encoder; | |
| 624 | + struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); | |
| 625 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 626 | + struct drm_connector *connector; | |
| 627 | + struct amdgpu_connector *amdgpu_connector; | |
| 628 | + u32 src_v = 1, dst_v = 1; | |
| 629 | + u32 src_h = 1, dst_h = 1; | |
| 630 | + | |
| 631 | + amdgpu_crtc->h_border = 0; | |
| 632 | + amdgpu_crtc->v_border = 0; | |
| 633 | + | |
| 634 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
| 635 | + if (encoder->crtc != crtc) | |
| 636 | + continue; | |
| 637 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 638 | + connector = amdgpu_get_connector_for_encoder(encoder); | |
| 639 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 640 | + | |
| 641 | + /* set scaling */ | |
| 642 | + if (amdgpu_encoder->rmx_type == RMX_OFF) | |
| 643 | + amdgpu_crtc->rmx_type = RMX_OFF; | |
| 644 | + else if (mode->hdisplay < amdgpu_encoder->native_mode.hdisplay || | |
| 645 | + mode->vdisplay < amdgpu_encoder->native_mode.vdisplay) | |
| 646 | + amdgpu_crtc->rmx_type = amdgpu_encoder->rmx_type; | |
| 647 | + else | |
| 648 | + amdgpu_crtc->rmx_type = RMX_OFF; | |
| 649 | + /* copy native mode */ | |
| 650 | + memcpy(&amdgpu_crtc->native_mode, | |
| 651 | + &amdgpu_encoder->native_mode, | |
| 652 | + sizeof(struct drm_display_mode)); | |
| 653 | + src_v = crtc->mode.vdisplay; | |
| 654 | + dst_v = amdgpu_crtc->native_mode.vdisplay; | |
| 655 | + src_h = crtc->mode.hdisplay; | |
| 656 | + dst_h = amdgpu_crtc->native_mode.hdisplay; | |
| 657 | + | |
| 658 | + /* fix up for overscan on hdmi */ | |
| 659 | + if ((!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && | |
| 660 | + ((amdgpu_encoder->underscan_type == UNDERSCAN_ON) || | |
| 661 | + ((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) && | |
| 662 | + drm_detect_hdmi_monitor(amdgpu_connector_edid(connector)) && | |
| 663 | + is_hdtv_mode(mode)))) { | |
| 664 | + if (amdgpu_encoder->underscan_hborder != 0) | |
| 665 | + amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder; | |
| 666 | + else | |
| 667 | + amdgpu_crtc->h_border = (mode->hdisplay >> 5) + 16; | |
| 668 | + if (amdgpu_encoder->underscan_vborder != 0) | |
| 669 | + amdgpu_crtc->v_border = amdgpu_encoder->underscan_vborder; | |
| 670 | + else | |
| 671 | + amdgpu_crtc->v_border = (mode->vdisplay >> 5) + 16; | |
| 672 | + amdgpu_crtc->rmx_type = RMX_FULL; | |
| 673 | + src_v = crtc->mode.vdisplay; | |
| 674 | + dst_v = crtc->mode.vdisplay - (amdgpu_crtc->v_border * 2); | |
| 675 | + src_h = crtc->mode.hdisplay; | |
| 676 | + dst_h = crtc->mode.hdisplay - (amdgpu_crtc->h_border * 2); | |
| 677 | + } | |
| 678 | + } | |
| 679 | + if (amdgpu_crtc->rmx_type != RMX_OFF) { | |
| 680 | + fixed20_12 a, b; | |
| 681 | + a.full = dfixed_const(src_v); | |
| 682 | + b.full = dfixed_const(dst_v); | |
| 683 | + amdgpu_crtc->vsc.full = dfixed_div(a, b); | |
| 684 | + a.full = dfixed_const(src_h); | |
| 685 | + b.full = dfixed_const(dst_h); | |
| 686 | + amdgpu_crtc->hsc.full = dfixed_div(a, b); | |
| 687 | + } else { | |
| 688 | + amdgpu_crtc->vsc.full = dfixed_const(1); | |
| 689 | + amdgpu_crtc->hsc.full = dfixed_const(1); | |
| 690 | + } | |
| 691 | + return true; | |
| 692 | +} | |
| 693 | + | |
| 694 | +/* | |
| 695 | + * Retrieve current video scanout position of crtc on a given gpu, and | |
| 696 | + * an optional accurate timestamp of when query happened. | |
| 697 | + * | |
| 698 | + * \param dev Device to query. | |
| 699 | + * \param crtc Crtc to query. | |
| 700 | + * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). | |
| 701 | + * \param *vpos Location where vertical scanout position should be stored. | |
| 702 | + * \param *hpos Location where horizontal scanout position should go. | |
| 703 | + * \param *stime Target location for timestamp taken immediately before | |
| 704 | + * scanout position query. Can be NULL to skip timestamp. | |
| 705 | + * \param *etime Target location for timestamp taken immediately after | |
| 706 | + * scanout position query. Can be NULL to skip timestamp. | |
| 707 | + * | |
| 708 | + * Returns vpos as a positive number while in active scanout area. | |
| 709 | + * Returns vpos as a negative number inside vblank, counting the number | |
| 710 | + * of scanlines to go until end of vblank, e.g., -1 means "one scanline | |
| 711 | + * until start of active scanout / end of vblank." | |
| 712 | + * | |
| 713 | + * \return Flags, or'ed together as follows: | |
| 714 | + * | |
| 715 | + * DRM_SCANOUTPOS_VALID = Query successful. | |
| 716 | + * DRM_SCANOUTPOS_INVBL = Inside vblank. | |
| 717 | + * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of | |
| 718 | + * this flag means that returned position may be offset by a constant but | |
| 719 | + * unknown small number of scanlines wrt. real scanout position. | |
| 720 | + * | |
| 721 | + */ | |
| 722 | +int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, | |
| 723 | + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) | |
| 724 | +{ | |
| 725 | + u32 vbl = 0, position = 0; | |
| 726 | + int vbl_start, vbl_end, vtotal, ret = 0; | |
| 727 | + bool in_vbl = true; | |
| 728 | + | |
| 729 | + struct amdgpu_device *adev = dev->dev_private; | |
| 730 | + | |
| 731 | + /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ | |
| 732 | + | |
| 733 | + /* Get optional system timestamp before query. */ | |
| 734 | + if (stime) | |
| 735 | + *stime = ktime_get(); | |
| 736 | + | |
| 737 | + if (amdgpu_display_page_flip_get_scanoutpos(adev, crtc, &vbl, &position) == 0) | |
| 738 | + ret |= DRM_SCANOUTPOS_VALID; | |
| 739 | + | |
| 740 | + /* Get optional system timestamp after query. */ | |
| 741 | + if (etime) | |
| 742 | + *etime = ktime_get(); | |
| 743 | + | |
| 744 | + /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ | |
| 745 | + | |
| 746 | + /* Decode into vertical and horizontal scanout position. */ | |
| 747 | + *vpos = position & 0x1fff; | |
| 748 | + *hpos = (position >> 16) & 0x1fff; | |
| 749 | + | |
| 750 | + /* Valid vblank area boundaries from gpu retrieved? */ | |
| 751 | + if (vbl > 0) { | |
| 752 | + /* Yes: Decode. */ | |
| 753 | + ret |= DRM_SCANOUTPOS_ACCURATE; | |
| 754 | + vbl_start = vbl & 0x1fff; | |
| 755 | + vbl_end = (vbl >> 16) & 0x1fff; | |
| 756 | + } | |
| 757 | + else { | |
| 758 | + /* No: Fake something reasonable which gives at least ok results. */ | |
| 759 | + vbl_start = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; | |
| 760 | + vbl_end = 0; | |
| 761 | + } | |
| 762 | + | |
| 763 | + /* Test scanout position against vblank region. */ | |
| 764 | + if ((*vpos < vbl_start) && (*vpos >= vbl_end)) | |
| 765 | + in_vbl = false; | |
| 766 | + | |
| 767 | + /* Check if inside vblank area and apply corrective offsets: | |
| 768 | + * vpos will then be >=0 in video scanout area, but negative | |
| 769 | + * within vblank area, counting down the number of lines until | |
| 770 | + * start of scanout. | |
| 771 | + */ | |
| 772 | + | |
| 773 | + /* Inside "upper part" of vblank area? Apply corrective offset if so: */ | |
| 774 | + if (in_vbl && (*vpos >= vbl_start)) { | |
| 775 | + vtotal = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; | |
| 776 | + *vpos = *vpos - vtotal; | |
| 777 | + } | |
| 778 | + | |
| 779 | + /* Correct for shifted end of vbl at vbl_end. */ | |
| 780 | + *vpos = *vpos - vbl_end; | |
| 781 | + | |
| 782 | + /* In vblank? */ | |
| 783 | + if (in_vbl) | |
| 784 | + ret |= DRM_SCANOUTPOS_IN_VBLANK; | |
| 785 | + | |
| 786 | + /* Is vpos outside nominal vblank area, but less than | |
| 787 | + * 1/100 of a frame height away from start of vblank? | |
| 788 | + * If so, assume this isn't a massively delayed vblank | |
| 789 | + * interrupt, but a vblank interrupt that fired a few | |
| 790 | + * microseconds before true start of vblank. Compensate | |
| 791 | + * by adding a full frame duration to the final timestamp. | |
| 792 | + * Happens, e.g., on ATI R500, R600. | |
| 793 | + * | |
| 794 | + * We only do this if DRM_CALLED_FROM_VBLIRQ. | |
| 795 | + */ | |
| 796 | + if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { | |
| 797 | + vbl_start = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; | |
| 798 | + vtotal = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; | |
| 799 | + | |
| 800 | + if (vbl_start - *vpos < vtotal / 100) { | |
| 801 | + *vpos -= vtotal; | |
| 802 | + | |
| 803 | + /* Signal this correction as "applied". */ | |
| 804 | + ret |= 0x8; | |
| 805 | + } | |
| 806 | + } | |
| 807 | + | |
| 808 | + return ret; | |
| 809 | +} | |
| 810 | + | |
| 811 | +int amdgpu_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc) | |
| 812 | +{ | |
| 813 | + if (crtc < 0 || crtc >= adev->mode_info.num_crtc) | |
| 814 | + return AMDGPU_CRTC_IRQ_NONE; | |
| 815 | + | |
| 816 | + switch (crtc) { | |
| 817 | + case 0: | |
| 818 | + return AMDGPU_CRTC_IRQ_VBLANK1; | |
| 819 | + case 1: | |
| 820 | + return AMDGPU_CRTC_IRQ_VBLANK2; | |
| 821 | + case 2: | |
| 822 | + return AMDGPU_CRTC_IRQ_VBLANK3; | |
| 823 | + case 3: | |
| 824 | + return AMDGPU_CRTC_IRQ_VBLANK4; | |
| 825 | + case 4: | |
| 826 | + return AMDGPU_CRTC_IRQ_VBLANK5; | |
| 827 | + case 5: | |
| 828 | + return AMDGPU_CRTC_IRQ_VBLANK6; | |
| 829 | + default: | |
| 830 | + return AMDGPU_CRTC_IRQ_NONE; | |
| 831 | + } | |
| 832 | +} | |
| @@ -0,0 +1,955 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2011 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + * Authors: Alex Deucher | |
| 23 | + */ | |
| 24 | + | |
| 25 | +#include "drmP.h" | |
| 26 | +#include "amdgpu.h" | |
| 27 | +#include "amdgpu_atombios.h" | |
| 28 | +#include "amdgpu_i2c.h" | |
| 29 | +#include "amdgpu_dpm.h" | |
| 30 | +#include "atom.h" | |
| 31 | + | |
| 32 | +void amdgpu_dpm_print_class_info(u32 class, u32 class2) | |
| 33 | +{ | |
| 34 | + printk("\tui class: "); | |
| 35 | + switch (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) { | |
| 36 | + case ATOM_PPLIB_CLASSIFICATION_UI_NONE: | |
| 37 | + default: | |
| 38 | + printk("none\n"); | |
| 39 | + break; | |
| 40 | + case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY: | |
| 41 | + printk("battery\n"); | |
| 42 | + break; | |
| 43 | + case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED: | |
| 44 | + printk("balanced\n"); | |
| 45 | + break; | |
| 46 | + case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE: | |
| 47 | + printk("performance\n"); | |
| 48 | + break; | |
| 49 | + } | |
| 50 | + printk("\tinternal class: "); | |
| 51 | + if (((class & ~ATOM_PPLIB_CLASSIFICATION_UI_MASK) == 0) && | |
| 52 | + (class2 == 0)) | |
| 53 | + printk("none"); | |
| 54 | + else { | |
| 55 | + if (class & ATOM_PPLIB_CLASSIFICATION_BOOT) | |
| 56 | + printk("boot "); | |
| 57 | + if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL) | |
| 58 | + printk("thermal "); | |
| 59 | + if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE) | |
| 60 | + printk("limited_pwr "); | |
| 61 | + if (class & ATOM_PPLIB_CLASSIFICATION_REST) | |
| 62 | + printk("rest "); | |
| 63 | + if (class & ATOM_PPLIB_CLASSIFICATION_FORCED) | |
| 64 | + printk("forced "); | |
| 65 | + if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) | |
| 66 | + printk("3d_perf "); | |
| 67 | + if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE) | |
| 68 | + printk("ovrdrv "); | |
| 69 | + if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) | |
| 70 | + printk("uvd "); | |
| 71 | + if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW) | |
| 72 | + printk("3d_low "); | |
| 73 | + if (class & ATOM_PPLIB_CLASSIFICATION_ACPI) | |
| 74 | + printk("acpi "); | |
| 75 | + if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) | |
| 76 | + printk("uvd_hd2 "); | |
| 77 | + if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) | |
| 78 | + printk("uvd_hd "); | |
| 79 | + if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) | |
| 80 | + printk("uvd_sd "); | |
| 81 | + if (class2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2) | |
| 82 | + printk("limited_pwr2 "); | |
| 83 | + if (class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) | |
| 84 | + printk("ulv "); | |
| 85 | + if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) | |
| 86 | + printk("uvd_mvc "); | |
| 87 | + } | |
| 88 | + printk("\n"); | |
| 89 | +} | |
| 90 | + | |
| 91 | +void amdgpu_dpm_print_cap_info(u32 caps) | |
| 92 | +{ | |
| 93 | + printk("\tcaps: "); | |
| 94 | + if (caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) | |
| 95 | + printk("single_disp "); | |
| 96 | + if (caps & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK) | |
| 97 | + printk("video "); | |
| 98 | + if (caps & ATOM_PPLIB_DISALLOW_ON_DC) | |
| 99 | + printk("no_dc "); | |
| 100 | + printk("\n"); | |
| 101 | +} | |
| 102 | + | |
| 103 | +void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev, | |
| 104 | + struct amdgpu_ps *rps) | |
| 105 | +{ | |
| 106 | + printk("\tstatus: "); | |
| 107 | + if (rps == adev->pm.dpm.current_ps) | |
| 108 | + printk("c "); | |
| 109 | + if (rps == adev->pm.dpm.requested_ps) | |
| 110 | + printk("r "); | |
| 111 | + if (rps == adev->pm.dpm.boot_ps) | |
| 112 | + printk("b "); | |
| 113 | + printk("\n"); | |
| 114 | +} | |
| 115 | + | |
| 116 | +u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev) | |
| 117 | +{ | |
| 118 | + struct drm_device *dev = adev->ddev; | |
| 119 | + struct drm_crtc *crtc; | |
| 120 | + struct amdgpu_crtc *amdgpu_crtc; | |
| 121 | + u32 line_time_us, vblank_lines; | |
| 122 | + u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */ | |
| 123 | + | |
| 124 | + if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { | |
| 125 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | |
| 126 | + amdgpu_crtc = to_amdgpu_crtc(crtc); | |
| 127 | + if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) { | |
| 128 | + line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) / | |
| 129 | + amdgpu_crtc->hw_mode.clock; | |
| 130 | + vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end - | |
| 131 | + amdgpu_crtc->hw_mode.crtc_vdisplay + | |
| 132 | + (amdgpu_crtc->v_border * 2); | |
| 133 | + vblank_time_us = vblank_lines * line_time_us; | |
| 134 | + break; | |
| 135 | + } | |
| 136 | + } | |
| 137 | + } | |
| 138 | + | |
| 139 | + return vblank_time_us; | |
| 140 | +} | |
| 141 | + | |
| 142 | +u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev) | |
| 143 | +{ | |
| 144 | + struct drm_device *dev = adev->ddev; | |
| 145 | + struct drm_crtc *crtc; | |
| 146 | + struct amdgpu_crtc *amdgpu_crtc; | |
| 147 | + u32 vrefresh = 0; | |
| 148 | + | |
| 149 | + if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { | |
| 150 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | |
| 151 | + amdgpu_crtc = to_amdgpu_crtc(crtc); | |
| 152 | + if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) { | |
| 153 | + vrefresh = amdgpu_crtc->hw_mode.vrefresh; | |
| 154 | + break; | |
| 155 | + } | |
| 156 | + } | |
| 157 | + } | |
| 158 | + | |
| 159 | + return vrefresh; | |
| 160 | +} | |
| 161 | + | |
| 162 | +void amdgpu_calculate_u_and_p(u32 i, u32 r_c, u32 p_b, | |
| 163 | + u32 *p, u32 *u) | |
| 164 | +{ | |
| 165 | + u32 b_c = 0; | |
| 166 | + u32 i_c; | |
| 167 | + u32 tmp; | |
| 168 | + | |
| 169 | + i_c = (i * r_c) / 100; | |
| 170 | + tmp = i_c >> p_b; | |
| 171 | + | |
| 172 | + while (tmp) { | |
| 173 | + b_c++; | |
| 174 | + tmp >>= 1; | |
| 175 | + } | |
| 176 | + | |
| 177 | + *u = (b_c + 1) / 2; | |
| 178 | + *p = i_c / (1 << (2 * (*u))); | |
| 179 | +} | |
| 180 | + | |
| 181 | +int amdgpu_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th) | |
| 182 | +{ | |
| 183 | + u32 k, a, ah, al; | |
| 184 | + u32 t1; | |
| 185 | + | |
| 186 | + if ((fl == 0) || (fh == 0) || (fl > fh)) | |
| 187 | + return -EINVAL; | |
| 188 | + | |
| 189 | + k = (100 * fh) / fl; | |
| 190 | + t1 = (t * (k - 100)); | |
| 191 | + a = (1000 * (100 * h + t1)) / (10000 + (t1 / 100)); | |
| 192 | + a = (a + 5) / 10; | |
| 193 | + ah = ((a * t) + 5000) / 10000; | |
| 194 | + al = a - ah; | |
| 195 | + | |
| 196 | + *th = t - ah; | |
| 197 | + *tl = t + al; | |
| 198 | + | |
| 199 | + return 0; | |
| 200 | +} | |
| 201 | + | |
| 202 | +bool amdgpu_is_uvd_state(u32 class, u32 class2) | |
| 203 | +{ | |
| 204 | + if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) | |
| 205 | + return true; | |
| 206 | + if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) | |
| 207 | + return true; | |
| 208 | + if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) | |
| 209 | + return true; | |
| 210 | + if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) | |
| 211 | + return true; | |
| 212 | + if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) | |
| 213 | + return true; | |
| 214 | + return false; | |
| 215 | +} | |
| 216 | + | |
| 217 | +bool amdgpu_is_internal_thermal_sensor(enum amdgpu_int_thermal_type sensor) | |
| 218 | +{ | |
| 219 | + switch (sensor) { | |
| 220 | + case THERMAL_TYPE_RV6XX: | |
| 221 | + case THERMAL_TYPE_RV770: | |
| 222 | + case THERMAL_TYPE_EVERGREEN: | |
| 223 | + case THERMAL_TYPE_SUMO: | |
| 224 | + case THERMAL_TYPE_NI: | |
| 225 | + case THERMAL_TYPE_SI: | |
| 226 | + case THERMAL_TYPE_CI: | |
| 227 | + case THERMAL_TYPE_KV: | |
| 228 | + return true; | |
| 229 | + case THERMAL_TYPE_ADT7473_WITH_INTERNAL: | |
| 230 | + case THERMAL_TYPE_EMC2103_WITH_INTERNAL: | |
| 231 | + return false; /* need special handling */ | |
| 232 | + case THERMAL_TYPE_NONE: | |
| 233 | + case THERMAL_TYPE_EXTERNAL: | |
| 234 | + case THERMAL_TYPE_EXTERNAL_GPIO: | |
| 235 | + default: | |
| 236 | + return false; | |
| 237 | + } | |
| 238 | +} | |
| 239 | + | |
| 240 | +union power_info { | |
| 241 | + struct _ATOM_POWERPLAY_INFO info; | |
| 242 | + struct _ATOM_POWERPLAY_INFO_V2 info_2; | |
| 243 | + struct _ATOM_POWERPLAY_INFO_V3 info_3; | |
| 244 | + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; | |
| 245 | + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; | |
| 246 | + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; | |
| 247 | + struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4; | |
| 248 | + struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5; | |
| 249 | +}; | |
| 250 | + | |
| 251 | +union fan_info { | |
| 252 | + struct _ATOM_PPLIB_FANTABLE fan; | |
| 253 | + struct _ATOM_PPLIB_FANTABLE2 fan2; | |
| 254 | + struct _ATOM_PPLIB_FANTABLE3 fan3; | |
| 255 | +}; | |
| 256 | + | |
| 257 | +static int amdgpu_parse_clk_voltage_dep_table(struct amdgpu_clock_voltage_dependency_table *amdgpu_table, | |
| 258 | + ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table) | |
| 259 | +{ | |
| 260 | + u32 size = atom_table->ucNumEntries * | |
| 261 | + sizeof(struct amdgpu_clock_voltage_dependency_entry); | |
| 262 | + int i; | |
| 263 | + ATOM_PPLIB_Clock_Voltage_Dependency_Record *entry; | |
| 264 | + | |
| 265 | + amdgpu_table->entries = kzalloc(size, GFP_KERNEL); | |
| 266 | + if (!amdgpu_table->entries) | |
| 267 | + return -ENOMEM; | |
| 268 | + | |
| 269 | + entry = &atom_table->entries[0]; | |
| 270 | + for (i = 0; i < atom_table->ucNumEntries; i++) { | |
| 271 | + amdgpu_table->entries[i].clk = le16_to_cpu(entry->usClockLow) | | |
| 272 | + (entry->ucClockHigh << 16); | |
| 273 | + amdgpu_table->entries[i].v = le16_to_cpu(entry->usVoltage); | |
| 274 | + entry = (ATOM_PPLIB_Clock_Voltage_Dependency_Record *) | |
| 275 | + ((u8 *)entry + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Record)); | |
| 276 | + } | |
| 277 | + amdgpu_table->count = atom_table->ucNumEntries; | |
| 278 | + | |
| 279 | + return 0; | |
| 280 | +} | |
| 281 | + | |
| 282 | +int amdgpu_get_platform_caps(struct amdgpu_device *adev) | |
| 283 | +{ | |
| 284 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 285 | + union power_info *power_info; | |
| 286 | + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); | |
| 287 | + u16 data_offset; | |
| 288 | + u8 frev, crev; | |
| 289 | + | |
| 290 | + if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, | |
| 291 | + &frev, &crev, &data_offset)) | |
| 292 | + return -EINVAL; | |
| 293 | + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); | |
| 294 | + | |
| 295 | + adev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); | |
| 296 | + adev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); | |
| 297 | + adev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); | |
| 298 | + | |
| 299 | + return 0; | |
| 300 | +} | |
| 301 | + | |
| 302 | +/* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */ | |
| 303 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12 | |
| 304 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14 | |
| 305 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16 | |
| 306 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18 | |
| 307 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20 | |
| 308 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22 | |
| 309 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8 24 | |
| 310 | +#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V9 26 | |
| 311 | + | |
| 312 | +int amdgpu_parse_extended_power_table(struct amdgpu_device *adev) | |
| 313 | +{ | |
| 314 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 315 | + union power_info *power_info; | |
| 316 | + union fan_info *fan_info; | |
| 317 | + ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table; | |
| 318 | + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); | |
| 319 | + u16 data_offset; | |
| 320 | + u8 frev, crev; | |
| 321 | + int ret, i; | |
| 322 | + | |
| 323 | + if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, | |
| 324 | + &frev, &crev, &data_offset)) | |
| 325 | + return -EINVAL; | |
| 326 | + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); | |
| 327 | + | |
| 328 | + /* fan table */ | |
| 329 | + if (le16_to_cpu(power_info->pplib.usTableSize) >= | |
| 330 | + sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { | |
| 331 | + if (power_info->pplib3.usFanTableOffset) { | |
| 332 | + fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset + | |
| 333 | + le16_to_cpu(power_info->pplib3.usFanTableOffset)); | |
| 334 | + adev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst; | |
| 335 | + adev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin); | |
| 336 | + adev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed); | |
| 337 | + adev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh); | |
| 338 | + adev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin); | |
| 339 | + adev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed); | |
| 340 | + adev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh); | |
| 341 | + if (fan_info->fan.ucFanTableFormat >= 2) | |
| 342 | + adev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax); | |
| 343 | + else | |
| 344 | + adev->pm.dpm.fan.t_max = 10900; | |
| 345 | + adev->pm.dpm.fan.cycle_delay = 100000; | |
| 346 | + if (fan_info->fan.ucFanTableFormat >= 3) { | |
| 347 | + adev->pm.dpm.fan.control_mode = fan_info->fan3.ucFanControlMode; | |
| 348 | + adev->pm.dpm.fan.default_max_fan_pwm = | |
| 349 | + le16_to_cpu(fan_info->fan3.usFanPWMMax); | |
| 350 | + adev->pm.dpm.fan.default_fan_output_sensitivity = 4836; | |
| 351 | + adev->pm.dpm.fan.fan_output_sensitivity = | |
| 352 | + le16_to_cpu(fan_info->fan3.usFanOutputSensitivity); | |
| 353 | + } | |
| 354 | + adev->pm.dpm.fan.ucode_fan_control = true; | |
| 355 | + } | |
| 356 | + } | |
| 357 | + | |
| 358 | + /* clock dependancy tables, shedding tables */ | |
| 359 | + if (le16_to_cpu(power_info->pplib.usTableSize) >= | |
| 360 | + sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) { | |
| 361 | + if (power_info->pplib4.usVddcDependencyOnSCLKOffset) { | |
| 362 | + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) | |
| 363 | + (mode_info->atom_context->bios + data_offset + | |
| 364 | + le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset)); | |
| 365 | + ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk, | |
| 366 | + dep_table); | |
| 367 | + if (ret) { | |
| 368 | + amdgpu_free_extended_power_table(adev); | |
| 369 | + return ret; | |
| 370 | + } | |
| 371 | + } | |
| 372 | + if (power_info->pplib4.usVddciDependencyOnMCLKOffset) { | |
| 373 | + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) | |
| 374 | + (mode_info->atom_context->bios + data_offset + | |
| 375 | + le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset)); | |
| 376 | + ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddci_dependency_on_mclk, | |
| 377 | + dep_table); | |
| 378 | + if (ret) { | |
| 379 | + amdgpu_free_extended_power_table(adev); | |
| 380 | + return ret; | |
| 381 | + } | |
| 382 | + } | |
| 383 | + if (power_info->pplib4.usVddcDependencyOnMCLKOffset) { | |
| 384 | + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) | |
| 385 | + (mode_info->atom_context->bios + data_offset + | |
| 386 | + le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset)); | |
| 387 | + ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk, | |
| 388 | + dep_table); | |
| 389 | + if (ret) { | |
| 390 | + amdgpu_free_extended_power_table(adev); | |
| 391 | + return ret; | |
| 392 | + } | |
| 393 | + } | |
| 394 | + if (power_info->pplib4.usMvddDependencyOnMCLKOffset) { | |
| 395 | + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) | |
| 396 | + (mode_info->atom_context->bios + data_offset + | |
| 397 | + le16_to_cpu(power_info->pplib4.usMvddDependencyOnMCLKOffset)); | |
| 398 | + ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.mvdd_dependency_on_mclk, | |
| 399 | + dep_table); | |
| 400 | + if (ret) { | |
| 401 | + amdgpu_free_extended_power_table(adev); | |
| 402 | + return ret; | |
| 403 | + } | |
| 404 | + } | |
| 405 | + if (power_info->pplib4.usMaxClockVoltageOnDCOffset) { | |
| 406 | + ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v = | |
| 407 | + (ATOM_PPLIB_Clock_Voltage_Limit_Table *) | |
| 408 | + (mode_info->atom_context->bios + data_offset + | |
| 409 | + le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset)); | |
| 410 | + if (clk_v->ucNumEntries) { | |
| 411 | + adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk = | |
| 412 | + le16_to_cpu(clk_v->entries[0].usSclkLow) | | |
| 413 | + (clk_v->entries[0].ucSclkHigh << 16); | |
| 414 | + adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk = | |
| 415 | + le16_to_cpu(clk_v->entries[0].usMclkLow) | | |
| 416 | + (clk_v->entries[0].ucMclkHigh << 16); | |
| 417 | + adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc = | |
| 418 | + le16_to_cpu(clk_v->entries[0].usVddc); | |
| 419 | + adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci = | |
| 420 | + le16_to_cpu(clk_v->entries[0].usVddci); | |
| 421 | + } | |
| 422 | + } | |
| 423 | + if (power_info->pplib4.usVddcPhaseShedLimitsTableOffset) { | |
| 424 | + ATOM_PPLIB_PhaseSheddingLimits_Table *psl = | |
| 425 | + (ATOM_PPLIB_PhaseSheddingLimits_Table *) | |
| 426 | + (mode_info->atom_context->bios + data_offset + | |
| 427 | + le16_to_cpu(power_info->pplib4.usVddcPhaseShedLimitsTableOffset)); | |
| 428 | + ATOM_PPLIB_PhaseSheddingLimits_Record *entry; | |
| 429 | + | |
| 430 | + adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries = | |
| 431 | + kzalloc(psl->ucNumEntries * | |
| 432 | + sizeof(struct amdgpu_phase_shedding_limits_entry), | |
| 433 | + GFP_KERNEL); | |
| 434 | + if (!adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) { | |
| 435 | + amdgpu_free_extended_power_table(adev); | |
| 436 | + return -ENOMEM; | |
| 437 | + } | |
| 438 | + | |
| 439 | + entry = &psl->entries[0]; | |
| 440 | + for (i = 0; i < psl->ucNumEntries; i++) { | |
| 441 | + adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].sclk = | |
| 442 | + le16_to_cpu(entry->usSclkLow) | (entry->ucSclkHigh << 16); | |
| 443 | + adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].mclk = | |
| 444 | + le16_to_cpu(entry->usMclkLow) | (entry->ucMclkHigh << 16); | |
| 445 | + adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].voltage = | |
| 446 | + le16_to_cpu(entry->usVoltage); | |
| 447 | + entry = (ATOM_PPLIB_PhaseSheddingLimits_Record *) | |
| 448 | + ((u8 *)entry + sizeof(ATOM_PPLIB_PhaseSheddingLimits_Record)); | |
| 449 | + } | |
| 450 | + adev->pm.dpm.dyn_state.phase_shedding_limits_table.count = | |
| 451 | + psl->ucNumEntries; | |
| 452 | + } | |
| 453 | + } | |
| 454 | + | |
| 455 | + /* cac data */ | |
| 456 | + if (le16_to_cpu(power_info->pplib.usTableSize) >= | |
| 457 | + sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) { | |
| 458 | + adev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit); | |
| 459 | + adev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit); | |
| 460 | + adev->pm.dpm.near_tdp_limit_adjusted = adev->pm.dpm.near_tdp_limit; | |
| 461 | + adev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit); | |
| 462 | + if (adev->pm.dpm.tdp_od_limit) | |
| 463 | + adev->pm.dpm.power_control = true; | |
| 464 | + else | |
| 465 | + adev->pm.dpm.power_control = false; | |
| 466 | + adev->pm.dpm.tdp_adjustment = 0; | |
| 467 | + adev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold); | |
| 468 | + adev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage); | |
| 469 | + adev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope); | |
| 470 | + if (power_info->pplib5.usCACLeakageTableOffset) { | |
| 471 | + ATOM_PPLIB_CAC_Leakage_Table *cac_table = | |
| 472 | + (ATOM_PPLIB_CAC_Leakage_Table *) | |
| 473 | + (mode_info->atom_context->bios + data_offset + | |
| 474 | + le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset)); | |
| 475 | + ATOM_PPLIB_CAC_Leakage_Record *entry; | |
| 476 | + u32 size = cac_table->ucNumEntries * sizeof(struct amdgpu_cac_leakage_table); | |
| 477 | + adev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL); | |
| 478 | + if (!adev->pm.dpm.dyn_state.cac_leakage_table.entries) { | |
| 479 | + amdgpu_free_extended_power_table(adev); | |
| 480 | + return -ENOMEM; | |
| 481 | + } | |
| 482 | + entry = &cac_table->entries[0]; | |
| 483 | + for (i = 0; i < cac_table->ucNumEntries; i++) { | |
| 484 | + if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) { | |
| 485 | + adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc1 = | |
| 486 | + le16_to_cpu(entry->usVddc1); | |
| 487 | + adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc2 = | |
| 488 | + le16_to_cpu(entry->usVddc2); | |
| 489 | + adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc3 = | |
| 490 | + le16_to_cpu(entry->usVddc3); | |
| 491 | + } else { | |
| 492 | + adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc = | |
| 493 | + le16_to_cpu(entry->usVddc); | |
| 494 | + adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage = | |
| 495 | + le32_to_cpu(entry->ulLeakageValue); | |
| 496 | + } | |
| 497 | + entry = (ATOM_PPLIB_CAC_Leakage_Record *) | |
| 498 | + ((u8 *)entry + sizeof(ATOM_PPLIB_CAC_Leakage_Record)); | |
| 499 | + } | |
| 500 | + adev->pm.dpm.dyn_state.cac_leakage_table.count = cac_table->ucNumEntries; | |
| 501 | + } | |
| 502 | + } | |
| 503 | + | |
| 504 | + /* ext tables */ | |
| 505 | + if (le16_to_cpu(power_info->pplib.usTableSize) >= | |
| 506 | + sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { | |
| 507 | + ATOM_PPLIB_EXTENDEDHEADER *ext_hdr = (ATOM_PPLIB_EXTENDEDHEADER *) | |
| 508 | + (mode_info->atom_context->bios + data_offset + | |
| 509 | + le16_to_cpu(power_info->pplib3.usExtendendedHeaderOffset)); | |
| 510 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2) && | |
| 511 | + ext_hdr->usVCETableOffset) { | |
| 512 | + VCEClockInfoArray *array = (VCEClockInfoArray *) | |
| 513 | + (mode_info->atom_context->bios + data_offset + | |
| 514 | + le16_to_cpu(ext_hdr->usVCETableOffset) + 1); | |
| 515 | + ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *limits = | |
| 516 | + (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *) | |
| 517 | + (mode_info->atom_context->bios + data_offset + | |
| 518 | + le16_to_cpu(ext_hdr->usVCETableOffset) + 1 + | |
| 519 | + 1 + array->ucNumEntries * sizeof(VCEClockInfo)); | |
| 520 | + ATOM_PPLIB_VCE_State_Table *states = | |
| 521 | + (ATOM_PPLIB_VCE_State_Table *) | |
| 522 | + (mode_info->atom_context->bios + data_offset + | |
| 523 | + le16_to_cpu(ext_hdr->usVCETableOffset) + 1 + | |
| 524 | + 1 + (array->ucNumEntries * sizeof (VCEClockInfo)) + | |
| 525 | + 1 + (limits->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record))); | |
| 526 | + ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *entry; | |
| 527 | + ATOM_PPLIB_VCE_State_Record *state_entry; | |
| 528 | + VCEClockInfo *vce_clk; | |
| 529 | + u32 size = limits->numEntries * | |
| 530 | + sizeof(struct amdgpu_vce_clock_voltage_dependency_entry); | |
| 531 | + adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries = | |
| 532 | + kzalloc(size, GFP_KERNEL); | |
| 533 | + if (!adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries) { | |
| 534 | + amdgpu_free_extended_power_table(adev); | |
| 535 | + return -ENOMEM; | |
| 536 | + } | |
| 537 | + adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.count = | |
| 538 | + limits->numEntries; | |
| 539 | + entry = &limits->entries[0]; | |
| 540 | + state_entry = &states->entries[0]; | |
| 541 | + for (i = 0; i < limits->numEntries; i++) { | |
| 542 | + vce_clk = (VCEClockInfo *) | |
| 543 | + ((u8 *)&array->entries[0] + | |
| 544 | + (entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo))); | |
| 545 | + adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].evclk = | |
| 546 | + le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16); | |
| 547 | + adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].ecclk = | |
| 548 | + le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16); | |
| 549 | + adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].v = | |
| 550 | + le16_to_cpu(entry->usVoltage); | |
| 551 | + entry = (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *) | |
| 552 | + ((u8 *)entry + sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record)); | |
| 553 | + } | |
| 554 | + for (i = 0; i < states->numEntries; i++) { | |
| 555 | + if (i >= AMDGPU_MAX_VCE_LEVELS) | |
| 556 | + break; | |
| 557 | + vce_clk = (VCEClockInfo *) | |
| 558 | + ((u8 *)&array->entries[0] + | |
| 559 | + (state_entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo))); | |
| 560 | + adev->pm.dpm.vce_states[i].evclk = | |
| 561 | + le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16); | |
| 562 | + adev->pm.dpm.vce_states[i].ecclk = | |
| 563 | + le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16); | |
| 564 | + adev->pm.dpm.vce_states[i].clk_idx = | |
| 565 | + state_entry->ucClockInfoIndex & 0x3f; | |
| 566 | + adev->pm.dpm.vce_states[i].pstate = | |
| 567 | + (state_entry->ucClockInfoIndex & 0xc0) >> 6; | |
| 568 | + state_entry = (ATOM_PPLIB_VCE_State_Record *) | |
| 569 | + ((u8 *)state_entry + sizeof(ATOM_PPLIB_VCE_State_Record)); | |
| 570 | + } | |
| 571 | + } | |
| 572 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) && | |
| 573 | + ext_hdr->usUVDTableOffset) { | |
| 574 | + UVDClockInfoArray *array = (UVDClockInfoArray *) | |
| 575 | + (mode_info->atom_context->bios + data_offset + | |
| 576 | + le16_to_cpu(ext_hdr->usUVDTableOffset) + 1); | |
| 577 | + ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *limits = | |
| 578 | + (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *) | |
| 579 | + (mode_info->atom_context->bios + data_offset + | |
| 580 | + le16_to_cpu(ext_hdr->usUVDTableOffset) + 1 + | |
| 581 | + 1 + (array->ucNumEntries * sizeof (UVDClockInfo))); | |
| 582 | + ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *entry; | |
| 583 | + u32 size = limits->numEntries * | |
| 584 | + sizeof(struct amdgpu_uvd_clock_voltage_dependency_entry); | |
| 585 | + adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries = | |
| 586 | + kzalloc(size, GFP_KERNEL); | |
| 587 | + if (!adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries) { | |
| 588 | + amdgpu_free_extended_power_table(adev); | |
| 589 | + return -ENOMEM; | |
| 590 | + } | |
| 591 | + adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count = | |
| 592 | + limits->numEntries; | |
| 593 | + entry = &limits->entries[0]; | |
| 594 | + for (i = 0; i < limits->numEntries; i++) { | |
| 595 | + UVDClockInfo *uvd_clk = (UVDClockInfo *) | |
| 596 | + ((u8 *)&array->entries[0] + | |
| 597 | + (entry->ucUVDClockInfoIndex * sizeof(UVDClockInfo))); | |
| 598 | + adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].vclk = | |
| 599 | + le16_to_cpu(uvd_clk->usVClkLow) | (uvd_clk->ucVClkHigh << 16); | |
| 600 | + adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].dclk = | |
| 601 | + le16_to_cpu(uvd_clk->usDClkLow) | (uvd_clk->ucDClkHigh << 16); | |
| 602 | + adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].v = | |
| 603 | + le16_to_cpu(entry->usVoltage); | |
| 604 | + entry = (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *) | |
| 605 | + ((u8 *)entry + sizeof(ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record)); | |
| 606 | + } | |
| 607 | + } | |
| 608 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4) && | |
| 609 | + ext_hdr->usSAMUTableOffset) { | |
| 610 | + ATOM_PPLIB_SAMClk_Voltage_Limit_Table *limits = | |
| 611 | + (ATOM_PPLIB_SAMClk_Voltage_Limit_Table *) | |
| 612 | + (mode_info->atom_context->bios + data_offset + | |
| 613 | + le16_to_cpu(ext_hdr->usSAMUTableOffset) + 1); | |
| 614 | + ATOM_PPLIB_SAMClk_Voltage_Limit_Record *entry; | |
| 615 | + u32 size = limits->numEntries * | |
| 616 | + sizeof(struct amdgpu_clock_voltage_dependency_entry); | |
| 617 | + adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries = | |
| 618 | + kzalloc(size, GFP_KERNEL); | |
| 619 | + if (!adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries) { | |
| 620 | + amdgpu_free_extended_power_table(adev); | |
| 621 | + return -ENOMEM; | |
| 622 | + } | |
| 623 | + adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.count = | |
| 624 | + limits->numEntries; | |
| 625 | + entry = &limits->entries[0]; | |
| 626 | + for (i = 0; i < limits->numEntries; i++) { | |
| 627 | + adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].clk = | |
| 628 | + le16_to_cpu(entry->usSAMClockLow) | (entry->ucSAMClockHigh << 16); | |
| 629 | + adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].v = | |
| 630 | + le16_to_cpu(entry->usVoltage); | |
| 631 | + entry = (ATOM_PPLIB_SAMClk_Voltage_Limit_Record *) | |
| 632 | + ((u8 *)entry + sizeof(ATOM_PPLIB_SAMClk_Voltage_Limit_Record)); | |
| 633 | + } | |
| 634 | + } | |
| 635 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5) && | |
| 636 | + ext_hdr->usPPMTableOffset) { | |
| 637 | + ATOM_PPLIB_PPM_Table *ppm = (ATOM_PPLIB_PPM_Table *) | |
| 638 | + (mode_info->atom_context->bios + data_offset + | |
| 639 | + le16_to_cpu(ext_hdr->usPPMTableOffset)); | |
| 640 | + adev->pm.dpm.dyn_state.ppm_table = | |
| 641 | + kzalloc(sizeof(struct amdgpu_ppm_table), GFP_KERNEL); | |
| 642 | + if (!adev->pm.dpm.dyn_state.ppm_table) { | |
| 643 | + amdgpu_free_extended_power_table(adev); | |
| 644 | + return -ENOMEM; | |
| 645 | + } | |
| 646 | + adev->pm.dpm.dyn_state.ppm_table->ppm_design = ppm->ucPpmDesign; | |
| 647 | + adev->pm.dpm.dyn_state.ppm_table->cpu_core_number = | |
| 648 | + le16_to_cpu(ppm->usCpuCoreNumber); | |
| 649 | + adev->pm.dpm.dyn_state.ppm_table->platform_tdp = | |
| 650 | + le32_to_cpu(ppm->ulPlatformTDP); | |
| 651 | + adev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdp = | |
| 652 | + le32_to_cpu(ppm->ulSmallACPlatformTDP); | |
| 653 | + adev->pm.dpm.dyn_state.ppm_table->platform_tdc = | |
| 654 | + le32_to_cpu(ppm->ulPlatformTDC); | |
| 655 | + adev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdc = | |
| 656 | + le32_to_cpu(ppm->ulSmallACPlatformTDC); | |
| 657 | + adev->pm.dpm.dyn_state.ppm_table->apu_tdp = | |
| 658 | + le32_to_cpu(ppm->ulApuTDP); | |
| 659 | + adev->pm.dpm.dyn_state.ppm_table->dgpu_tdp = | |
| 660 | + le32_to_cpu(ppm->ulDGpuTDP); | |
| 661 | + adev->pm.dpm.dyn_state.ppm_table->dgpu_ulv_power = | |
| 662 | + le32_to_cpu(ppm->ulDGpuUlvPower); | |
| 663 | + adev->pm.dpm.dyn_state.ppm_table->tj_max = | |
| 664 | + le32_to_cpu(ppm->ulTjmax); | |
| 665 | + } | |
| 666 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6) && | |
| 667 | + ext_hdr->usACPTableOffset) { | |
| 668 | + ATOM_PPLIB_ACPClk_Voltage_Limit_Table *limits = | |
| 669 | + (ATOM_PPLIB_ACPClk_Voltage_Limit_Table *) | |
| 670 | + (mode_info->atom_context->bios + data_offset + | |
| 671 | + le16_to_cpu(ext_hdr->usACPTableOffset) + 1); | |
| 672 | + ATOM_PPLIB_ACPClk_Voltage_Limit_Record *entry; | |
| 673 | + u32 size = limits->numEntries * | |
| 674 | + sizeof(struct amdgpu_clock_voltage_dependency_entry); | |
| 675 | + adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries = | |
| 676 | + kzalloc(size, GFP_KERNEL); | |
| 677 | + if (!adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries) { | |
| 678 | + amdgpu_free_extended_power_table(adev); | |
| 679 | + return -ENOMEM; | |
| 680 | + } | |
| 681 | + adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.count = | |
| 682 | + limits->numEntries; | |
| 683 | + entry = &limits->entries[0]; | |
| 684 | + for (i = 0; i < limits->numEntries; i++) { | |
| 685 | + adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].clk = | |
| 686 | + le16_to_cpu(entry->usACPClockLow) | (entry->ucACPClockHigh << 16); | |
| 687 | + adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].v = | |
| 688 | + le16_to_cpu(entry->usVoltage); | |
| 689 | + entry = (ATOM_PPLIB_ACPClk_Voltage_Limit_Record *) | |
| 690 | + ((u8 *)entry + sizeof(ATOM_PPLIB_ACPClk_Voltage_Limit_Record)); | |
| 691 | + } | |
| 692 | + } | |
| 693 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7) && | |
| 694 | + ext_hdr->usPowerTuneTableOffset) { | |
| 695 | + u8 rev = *(u8 *)(mode_info->atom_context->bios + data_offset + | |
| 696 | + le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); | |
| 697 | + ATOM_PowerTune_Table *pt; | |
| 698 | + adev->pm.dpm.dyn_state.cac_tdp_table = | |
| 699 | + kzalloc(sizeof(struct amdgpu_cac_tdp_table), GFP_KERNEL); | |
| 700 | + if (!adev->pm.dpm.dyn_state.cac_tdp_table) { | |
| 701 | + amdgpu_free_extended_power_table(adev); | |
| 702 | + return -ENOMEM; | |
| 703 | + } | |
| 704 | + if (rev > 0) { | |
| 705 | + ATOM_PPLIB_POWERTUNE_Table_V1 *ppt = (ATOM_PPLIB_POWERTUNE_Table_V1 *) | |
| 706 | + (mode_info->atom_context->bios + data_offset + | |
| 707 | + le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); | |
| 708 | + adev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = | |
| 709 | + ppt->usMaximumPowerDeliveryLimit; | |
| 710 | + pt = &ppt->power_tune_table; | |
| 711 | + } else { | |
| 712 | + ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *) | |
| 713 | + (mode_info->atom_context->bios + data_offset + | |
| 714 | + le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); | |
| 715 | + adev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = 255; | |
| 716 | + pt = &ppt->power_tune_table; | |
| 717 | + } | |
| 718 | + adev->pm.dpm.dyn_state.cac_tdp_table->tdp = le16_to_cpu(pt->usTDP); | |
| 719 | + adev->pm.dpm.dyn_state.cac_tdp_table->configurable_tdp = | |
| 720 | + le16_to_cpu(pt->usConfigurableTDP); | |
| 721 | + adev->pm.dpm.dyn_state.cac_tdp_table->tdc = le16_to_cpu(pt->usTDC); | |
| 722 | + adev->pm.dpm.dyn_state.cac_tdp_table->battery_power_limit = | |
| 723 | + le16_to_cpu(pt->usBatteryPowerLimit); | |
| 724 | + adev->pm.dpm.dyn_state.cac_tdp_table->small_power_limit = | |
| 725 | + le16_to_cpu(pt->usSmallPowerLimit); | |
| 726 | + adev->pm.dpm.dyn_state.cac_tdp_table->low_cac_leakage = | |
| 727 | + le16_to_cpu(pt->usLowCACLeakage); | |
| 728 | + adev->pm.dpm.dyn_state.cac_tdp_table->high_cac_leakage = | |
| 729 | + le16_to_cpu(pt->usHighCACLeakage); | |
| 730 | + } | |
| 731 | + if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8) && | |
| 732 | + ext_hdr->usSclkVddgfxTableOffset) { | |
| 733 | + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) | |
| 734 | + (mode_info->atom_context->bios + data_offset + | |
| 735 | + le16_to_cpu(ext_hdr->usSclkVddgfxTableOffset)); | |
| 736 | + ret = amdgpu_parse_clk_voltage_dep_table( | |
| 737 | + &adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk, | |
| 738 | + dep_table); | |
| 739 | + if (ret) { | |
| 740 | + kfree(adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk.entries); | |
| 741 | + return ret; | |
| 742 | + } | |
| 743 | + } | |
| 744 | + } | |
| 745 | + | |
| 746 | + return 0; | |
| 747 | +} | |
| 748 | + | |
| 749 | +void amdgpu_free_extended_power_table(struct amdgpu_device *adev) | |
| 750 | +{ | |
| 751 | + struct amdgpu_dpm_dynamic_state *dyn_state = &adev->pm.dpm.dyn_state; | |
| 752 | + | |
| 753 | + kfree(dyn_state->vddc_dependency_on_sclk.entries); | |
| 754 | + kfree(dyn_state->vddci_dependency_on_mclk.entries); | |
| 755 | + kfree(dyn_state->vddc_dependency_on_mclk.entries); | |
| 756 | + kfree(dyn_state->mvdd_dependency_on_mclk.entries); | |
| 757 | + kfree(dyn_state->cac_leakage_table.entries); | |
| 758 | + kfree(dyn_state->phase_shedding_limits_table.entries); | |
| 759 | + kfree(dyn_state->ppm_table); | |
| 760 | + kfree(dyn_state->cac_tdp_table); | |
| 761 | + kfree(dyn_state->vce_clock_voltage_dependency_table.entries); | |
| 762 | + kfree(dyn_state->uvd_clock_voltage_dependency_table.entries); | |
| 763 | + kfree(dyn_state->samu_clock_voltage_dependency_table.entries); | |
| 764 | + kfree(dyn_state->acp_clock_voltage_dependency_table.entries); | |
| 765 | + kfree(dyn_state->vddgfx_dependency_on_sclk.entries); | |
| 766 | +} | |
| 767 | + | |
| 768 | +static const char *pp_lib_thermal_controller_names[] = { | |
| 769 | + "NONE", | |
| 770 | + "lm63", | |
| 771 | + "adm1032", | |
| 772 | + "adm1030", | |
| 773 | + "max6649", | |
| 774 | + "lm64", | |
| 775 | + "f75375", | |
| 776 | + "RV6xx", | |
| 777 | + "RV770", | |
| 778 | + "adt7473", | |
| 779 | + "NONE", | |
| 780 | + "External GPIO", | |
| 781 | + "Evergreen", | |
| 782 | + "emc2103", | |
| 783 | + "Sumo", | |
| 784 | + "Northern Islands", | |
| 785 | + "Southern Islands", | |
| 786 | + "lm96163", | |
| 787 | + "Sea Islands", | |
| 788 | + "Kaveri/Kabini", | |
| 789 | +}; | |
| 790 | + | |
| 791 | +void amdgpu_add_thermal_controller(struct amdgpu_device *adev) | |
| 792 | +{ | |
| 793 | + struct amdgpu_mode_info *mode_info = &adev->mode_info; | |
| 794 | + ATOM_PPLIB_POWERPLAYTABLE *power_table; | |
| 795 | + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); | |
| 796 | + ATOM_PPLIB_THERMALCONTROLLER *controller; | |
| 797 | + struct amdgpu_i2c_bus_rec i2c_bus; | |
| 798 | + u16 data_offset; | |
| 799 | + u8 frev, crev; | |
| 800 | + | |
| 801 | + if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, | |
| 802 | + &frev, &crev, &data_offset)) | |
| 803 | + return; | |
| 804 | + power_table = (ATOM_PPLIB_POWERPLAYTABLE *) | |
| 805 | + (mode_info->atom_context->bios + data_offset); | |
| 806 | + controller = &power_table->sThermalController; | |
| 807 | + | |
| 808 | + /* add the i2c bus for thermal/fan chip */ | |
| 809 | + if (controller->ucType > 0) { | |
| 810 | + if (controller->ucFanParameters & ATOM_PP_FANPARAMETERS_NOFAN) | |
| 811 | + adev->pm.no_fan = true; | |
| 812 | + adev->pm.fan_pulses_per_revolution = | |
| 813 | + controller->ucFanParameters & ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK; | |
| 814 | + if (adev->pm.fan_pulses_per_revolution) { | |
| 815 | + adev->pm.fan_min_rpm = controller->ucFanMinRPM; | |
| 816 | + adev->pm.fan_max_rpm = controller->ucFanMaxRPM; | |
| 817 | + } | |
| 818 | + if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV6xx) { | |
| 819 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 820 | + (controller->ucFanParameters & | |
| 821 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 822 | + adev->pm.int_thermal_type = THERMAL_TYPE_RV6XX; | |
| 823 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV770) { | |
| 824 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 825 | + (controller->ucFanParameters & | |
| 826 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 827 | + adev->pm.int_thermal_type = THERMAL_TYPE_RV770; | |
| 828 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EVERGREEN) { | |
| 829 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 830 | + (controller->ucFanParameters & | |
| 831 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 832 | + adev->pm.int_thermal_type = THERMAL_TYPE_EVERGREEN; | |
| 833 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SUMO) { | |
| 834 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 835 | + (controller->ucFanParameters & | |
| 836 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 837 | + adev->pm.int_thermal_type = THERMAL_TYPE_SUMO; | |
| 838 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_NISLANDS) { | |
| 839 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 840 | + (controller->ucFanParameters & | |
| 841 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 842 | + adev->pm.int_thermal_type = THERMAL_TYPE_NI; | |
| 843 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SISLANDS) { | |
| 844 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 845 | + (controller->ucFanParameters & | |
| 846 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 847 | + adev->pm.int_thermal_type = THERMAL_TYPE_SI; | |
| 848 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_CISLANDS) { | |
| 849 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 850 | + (controller->ucFanParameters & | |
| 851 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 852 | + adev->pm.int_thermal_type = THERMAL_TYPE_CI; | |
| 853 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_KAVERI) { | |
| 854 | + DRM_INFO("Internal thermal controller %s fan control\n", | |
| 855 | + (controller->ucFanParameters & | |
| 856 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 857 | + adev->pm.int_thermal_type = THERMAL_TYPE_KV; | |
| 858 | + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) { | |
| 859 | + DRM_INFO("External GPIO thermal controller %s fan control\n", | |
| 860 | + (controller->ucFanParameters & | |
| 861 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 862 | + adev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL_GPIO; | |
| 863 | + } else if (controller->ucType == | |
| 864 | + ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL) { | |
| 865 | + DRM_INFO("ADT7473 with internal thermal controller %s fan control\n", | |
| 866 | + (controller->ucFanParameters & | |
| 867 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 868 | + adev->pm.int_thermal_type = THERMAL_TYPE_ADT7473_WITH_INTERNAL; | |
| 869 | + } else if (controller->ucType == | |
| 870 | + ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL) { | |
| 871 | + DRM_INFO("EMC2103 with internal thermal controller %s fan control\n", | |
| 872 | + (controller->ucFanParameters & | |
| 873 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 874 | + adev->pm.int_thermal_type = THERMAL_TYPE_EMC2103_WITH_INTERNAL; | |
| 875 | + } else if (controller->ucType < ARRAY_SIZE(pp_lib_thermal_controller_names)) { | |
| 876 | + DRM_INFO("Possible %s thermal controller at 0x%02x %s fan control\n", | |
| 877 | + pp_lib_thermal_controller_names[controller->ucType], | |
| 878 | + controller->ucI2cAddress >> 1, | |
| 879 | + (controller->ucFanParameters & | |
| 880 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 881 | + adev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL; | |
| 882 | + i2c_bus = amdgpu_atombios_lookup_i2c_gpio(adev, controller->ucI2cLine); | |
| 883 | + adev->pm.i2c_bus = amdgpu_i2c_lookup(adev, &i2c_bus); | |
| 884 | + if (adev->pm.i2c_bus) { | |
| 885 | + struct i2c_board_info info = { }; | |
| 886 | + const char *name = pp_lib_thermal_controller_names[controller->ucType]; | |
| 887 | + info.addr = controller->ucI2cAddress >> 1; | |
| 888 | + strlcpy(info.type, name, sizeof(info.type)); | |
| 889 | + i2c_new_device(&adev->pm.i2c_bus->adapter, &info); | |
| 890 | + } | |
| 891 | + } else { | |
| 892 | + DRM_INFO("Unknown thermal controller type %d at 0x%02x %s fan control\n", | |
| 893 | + controller->ucType, | |
| 894 | + controller->ucI2cAddress >> 1, | |
| 895 | + (controller->ucFanParameters & | |
| 896 | + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); | |
| 897 | + } | |
| 898 | + } | |
| 899 | +} | |
| 900 | + | |
| 901 | +enum amdgpu_pcie_gen amdgpu_get_pcie_gen_support(struct amdgpu_device *adev, | |
| 902 | + u32 sys_mask, | |
| 903 | + enum amdgpu_pcie_gen asic_gen, | |
| 904 | + enum amdgpu_pcie_gen default_gen) | |
| 905 | +{ | |
| 906 | + switch (asic_gen) { | |
| 907 | + case AMDGPU_PCIE_GEN1: | |
| 908 | + return AMDGPU_PCIE_GEN1; | |
| 909 | + case AMDGPU_PCIE_GEN2: | |
| 910 | + return AMDGPU_PCIE_GEN2; | |
| 911 | + case AMDGPU_PCIE_GEN3: | |
| 912 | + return AMDGPU_PCIE_GEN3; | |
| 913 | + default: | |
| 914 | + if ((sys_mask & DRM_PCIE_SPEED_80) && (default_gen == AMDGPU_PCIE_GEN3)) | |
| 915 | + return AMDGPU_PCIE_GEN3; | |
| 916 | + else if ((sys_mask & DRM_PCIE_SPEED_50) && (default_gen == AMDGPU_PCIE_GEN2)) | |
| 917 | + return AMDGPU_PCIE_GEN2; | |
| 918 | + else | |
| 919 | + return AMDGPU_PCIE_GEN1; | |
| 920 | + } | |
| 921 | + return AMDGPU_PCIE_GEN1; | |
| 922 | +} | |
| 923 | + | |
| 924 | +u16 amdgpu_get_pcie_lane_support(struct amdgpu_device *adev, | |
| 925 | + u16 asic_lanes, | |
| 926 | + u16 default_lanes) | |
| 927 | +{ | |
| 928 | + switch (asic_lanes) { | |
| 929 | + case 0: | |
| 930 | + default: | |
| 931 | + return default_lanes; | |
| 932 | + case 1: | |
| 933 | + return 1; | |
| 934 | + case 2: | |
| 935 | + return 2; | |
| 936 | + case 4: | |
| 937 | + return 4; | |
| 938 | + case 8: | |
| 939 | + return 8; | |
| 940 | + case 12: | |
| 941 | + return 12; | |
| 942 | + case 16: | |
| 943 | + return 16; | |
| 944 | + } | |
| 945 | +} | |
| 946 | + | |
| 947 | +u8 amdgpu_encode_pci_lane_width(u32 lanes) | |
| 948 | +{ | |
| 949 | + u8 encoded_lanes[] = { 0, 1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6 }; | |
| 950 | + | |
| 951 | + if (lanes > 16) | |
| 952 | + return 0; | |
| 953 | + | |
| 954 | + return encoded_lanes[lanes]; | |
| 955 | +} | |
| @@ -0,0 +1,85 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | +#ifndef __AMDGPU_DPM_H__ | |
| 24 | +#define __AMDGPU_DPM_H__ | |
| 25 | + | |
| 26 | +#define R600_SSTU_DFLT 0 | |
| 27 | +#define R600_SST_DFLT 0x00C8 | |
| 28 | + | |
| 29 | +/* XXX are these ok? */ | |
| 30 | +#define R600_TEMP_RANGE_MIN (90 * 1000) | |
| 31 | +#define R600_TEMP_RANGE_MAX (120 * 1000) | |
| 32 | + | |
| 33 | +#define FDO_PWM_MODE_STATIC 1 | |
| 34 | +#define FDO_PWM_MODE_STATIC_RPM 5 | |
| 35 | + | |
| 36 | +enum amdgpu_td { | |
| 37 | + AMDGPU_TD_AUTO, | |
| 38 | + AMDGPU_TD_UP, | |
| 39 | + AMDGPU_TD_DOWN, | |
| 40 | +}; | |
| 41 | + | |
| 42 | +enum amdgpu_display_watermark { | |
| 43 | + AMDGPU_DISPLAY_WATERMARK_LOW = 0, | |
| 44 | + AMDGPU_DISPLAY_WATERMARK_HIGH = 1, | |
| 45 | +}; | |
| 46 | + | |
| 47 | +enum amdgpu_display_gap | |
| 48 | +{ | |
| 49 | + AMDGPU_PM_DISPLAY_GAP_VBLANK_OR_WM = 0, | |
| 50 | + AMDGPU_PM_DISPLAY_GAP_VBLANK = 1, | |
| 51 | + AMDGPU_PM_DISPLAY_GAP_WATERMARK = 2, | |
| 52 | + AMDGPU_PM_DISPLAY_GAP_IGNORE = 3, | |
| 53 | +}; | |
| 54 | + | |
| 55 | +void amdgpu_dpm_print_class_info(u32 class, u32 class2); | |
| 56 | +void amdgpu_dpm_print_cap_info(u32 caps); | |
| 57 | +void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev, | |
| 58 | + struct amdgpu_ps *rps); | |
| 59 | +u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev); | |
| 60 | +u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev); | |
| 61 | +bool amdgpu_is_uvd_state(u32 class, u32 class2); | |
| 62 | +void amdgpu_calculate_u_and_p(u32 i, u32 r_c, u32 p_b, | |
| 63 | + u32 *p, u32 *u); | |
| 64 | +int amdgpu_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th); | |
| 65 | + | |
| 66 | +bool amdgpu_is_internal_thermal_sensor(enum amdgpu_int_thermal_type sensor); | |
| 67 | + | |
| 68 | +int amdgpu_get_platform_caps(struct amdgpu_device *adev); | |
| 69 | + | |
| 70 | +int amdgpu_parse_extended_power_table(struct amdgpu_device *adev); | |
| 71 | +void amdgpu_free_extended_power_table(struct amdgpu_device *adev); | |
| 72 | + | |
| 73 | +void amdgpu_add_thermal_controller(struct amdgpu_device *adev); | |
| 74 | + | |
| 75 | +enum amdgpu_pcie_gen amdgpu_get_pcie_gen_support(struct amdgpu_device *adev, | |
| 76 | + u32 sys_mask, | |
| 77 | + enum amdgpu_pcie_gen asic_gen, | |
| 78 | + enum amdgpu_pcie_gen default_gen); | |
| 79 | + | |
| 80 | +u16 amdgpu_get_pcie_lane_support(struct amdgpu_device *adev, | |
| 81 | + u16 asic_lanes, | |
| 82 | + u16 default_lanes); | |
| 83 | +u8 amdgpu_encode_pci_lane_width(u32 lanes); | |
| 84 | + | |
| 85 | +#endif | |
| @@ -0,0 +1,545 @@ | ||
| 1 | +/** | |
| 2 | + * \file amdgpu_drv.c | |
| 3 | + * AMD Amdgpu driver | |
| 4 | + * | |
| 5 | + * \author Gareth Hughes <gareth@valinux.com> | |
| 6 | + */ | |
| 7 | + | |
| 8 | +/* | |
| 9 | + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. | |
| 10 | + * All Rights Reserved. | |
| 11 | + * | |
| 12 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 13 | + * copy of this software and associated documentation files (the "Software"), | |
| 14 | + * to deal in the Software without restriction, including without limitation | |
| 15 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 16 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 17 | + * Software is furnished to do so, subject to the following conditions: | |
| 18 | + * | |
| 19 | + * The above copyright notice and this permission notice (including the next | |
| 20 | + * paragraph) shall be included in all copies or substantial portions of the | |
| 21 | + * Software. | |
| 22 | + * | |
| 23 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 24 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 25 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 26 | + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 27 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 28 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 29 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 30 | + */ | |
| 31 | + | |
| 32 | +#include <drm/drmP.h> | |
| 33 | +#include <drm/amdgpu_drm.h> | |
| 34 | +#include <drm/drm_gem.h> | |
| 35 | +#include "amdgpu_drv.h" | |
| 36 | + | |
| 37 | +#include <drm/drm_pciids.h> | |
| 38 | +#include <linux/console.h> | |
| 39 | +#include <linux/module.h> | |
| 40 | +#include <linux/pm_runtime.h> | |
| 41 | +#include <linux/vga_switcheroo.h> | |
| 42 | +#include "drm_crtc_helper.h" | |
| 43 | + | |
| 44 | +#include "amdgpu.h" | |
| 45 | +#include "amdgpu_irq.h" | |
| 46 | + | |
| 47 | +/* | |
| 48 | + * KMS wrapper. | |
| 49 | + * - 3.0.0 - initial driver | |
| 50 | + */ | |
| 51 | +#define KMS_DRIVER_MAJOR 3 | |
| 52 | +#define KMS_DRIVER_MINOR 0 | |
| 53 | +#define KMS_DRIVER_PATCHLEVEL 0 | |
| 54 | + | |
| 55 | +int amdgpu_vram_limit = 0; | |
| 56 | +int amdgpu_gart_size = -1; /* auto */ | |
| 57 | +int amdgpu_benchmarking = 0; | |
| 58 | +int amdgpu_testing = 0; | |
| 59 | +int amdgpu_audio = -1; | |
| 60 | +int amdgpu_disp_priority = 0; | |
| 61 | +int amdgpu_hw_i2c = 0; | |
| 62 | +int amdgpu_pcie_gen2 = -1; | |
| 63 | +int amdgpu_msi = -1; | |
| 64 | +int amdgpu_lockup_timeout = 10000; | |
| 65 | +int amdgpu_dpm = -1; | |
| 66 | +int amdgpu_smc_load_fw = 1; | |
| 67 | +int amdgpu_aspm = -1; | |
| 68 | +int amdgpu_runtime_pm = -1; | |
| 69 | +int amdgpu_hard_reset = 0; | |
| 70 | +unsigned amdgpu_ip_block_mask = 0xffffffff; | |
| 71 | +int amdgpu_bapm = -1; | |
| 72 | +int amdgpu_deep_color = 0; | |
| 73 | +int amdgpu_vm_size = 8; | |
| 74 | +int amdgpu_vm_block_size = -1; | |
| 75 | +int amdgpu_exp_hw_support = 0; | |
| 76 | + | |
| 77 | +MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes"); | |
| 78 | +module_param_named(vramlimit, amdgpu_vram_limit, int, 0600); | |
| 79 | + | |
| 80 | +MODULE_PARM_DESC(gartsize, "Size of PCIE/IGP gart to setup in megabytes (32, 64, etc., -1 = auto)"); | |
| 81 | +module_param_named(gartsize, amdgpu_gart_size, int, 0600); | |
| 82 | + | |
| 83 | +MODULE_PARM_DESC(benchmark, "Run benchmark"); | |
| 84 | +module_param_named(benchmark, amdgpu_benchmarking, int, 0444); | |
| 85 | + | |
| 86 | +MODULE_PARM_DESC(test, "Run tests"); | |
| 87 | +module_param_named(test, amdgpu_testing, int, 0444); | |
| 88 | + | |
| 89 | +MODULE_PARM_DESC(audio, "Audio enable (-1 = auto, 0 = disable, 1 = enable)"); | |
| 90 | +module_param_named(audio, amdgpu_audio, int, 0444); | |
| 91 | + | |
| 92 | +MODULE_PARM_DESC(disp_priority, "Display Priority (0 = auto, 1 = normal, 2 = high)"); | |
| 93 | +module_param_named(disp_priority, amdgpu_disp_priority, int, 0444); | |
| 94 | + | |
| 95 | +MODULE_PARM_DESC(hw_i2c, "hw i2c engine enable (0 = disable)"); | |
| 96 | +module_param_named(hw_i2c, amdgpu_hw_i2c, int, 0444); | |
| 97 | + | |
| 98 | +MODULE_PARM_DESC(pcie_gen2, "PCIE Gen2 mode (-1 = auto, 0 = disable, 1 = enable)"); | |
| 99 | +module_param_named(pcie_gen2, amdgpu_pcie_gen2, int, 0444); | |
| 100 | + | |
| 101 | +MODULE_PARM_DESC(msi, "MSI support (1 = enable, 0 = disable, -1 = auto)"); | |
| 102 | +module_param_named(msi, amdgpu_msi, int, 0444); | |
| 103 | + | |
| 104 | +MODULE_PARM_DESC(lockup_timeout, "GPU lockup timeout in ms (defaul 10000 = 10 seconds, 0 = disable)"); | |
| 105 | +module_param_named(lockup_timeout, amdgpu_lockup_timeout, int, 0444); | |
| 106 | + | |
| 107 | +MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)"); | |
| 108 | +module_param_named(dpm, amdgpu_dpm, int, 0444); | |
| 109 | + | |
| 110 | +MODULE_PARM_DESC(smc_load_fw, "SMC firmware loading(1 = enable, 0 = disable)"); | |
| 111 | +module_param_named(smc_load_fw, amdgpu_smc_load_fw, int, 0444); | |
| 112 | + | |
| 113 | +MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 = auto)"); | |
| 114 | +module_param_named(aspm, amdgpu_aspm, int, 0444); | |
| 115 | + | |
| 116 | +MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1 = PX only default)"); | |
| 117 | +module_param_named(runpm, amdgpu_runtime_pm, int, 0444); | |
| 118 | + | |
| 119 | +MODULE_PARM_DESC(hard_reset, "PCI config reset (1 = force enable, 0 = disable (default))"); | |
| 120 | +module_param_named(hard_reset, amdgpu_hard_reset, int, 0444); | |
| 121 | + | |
| 122 | +MODULE_PARM_DESC(ip_block_mask, "IP Block Mask (all blocks enabled (default))"); | |
| 123 | +module_param_named(ip_block_mask, amdgpu_ip_block_mask, uint, 0444); | |
| 124 | + | |
| 125 | +MODULE_PARM_DESC(bapm, "BAPM support (1 = enable, 0 = disable, -1 = auto)"); | |
| 126 | +module_param_named(bapm, amdgpu_bapm, int, 0444); | |
| 127 | + | |
| 128 | +MODULE_PARM_DESC(deep_color, "Deep Color support (1 = enable, 0 = disable (default))"); | |
| 129 | +module_param_named(deep_color, amdgpu_deep_color, int, 0444); | |
| 130 | + | |
| 131 | +MODULE_PARM_DESC(vm_size, "VM address space size in gigabytes (default 8GB)"); | |
| 132 | +module_param_named(vm_size, amdgpu_vm_size, int, 0444); | |
| 133 | + | |
| 134 | +MODULE_PARM_DESC(vm_block_size, "VM page table size in bits (default depending on vm_size)"); | |
| 135 | +module_param_named(vm_block_size, amdgpu_vm_block_size, int, 0444); | |
| 136 | + | |
| 137 | +MODULE_PARM_DESC(exp_hw_support, "experimental hw support (1 = enable, 0 = disable (default))"); | |
| 138 | +module_param_named(exp_hw_support, amdgpu_exp_hw_support, int, 0444); | |
| 139 | + | |
| 140 | +static struct pci_device_id pciidlist[] = { | |
| 141 | +#ifdef CONFIG_DRM_AMDGPU_CIK | |
| 142 | + /* Kaveri */ | |
| 143 | + {0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 144 | + {0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 145 | + {0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 146 | + {0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 147 | + {0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 148 | + {0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 149 | + {0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 150 | + {0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 151 | + {0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 152 | + {0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 153 | + {0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 154 | + {0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 155 | + {0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 156 | + {0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 157 | + {0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 158 | + {0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 159 | + {0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 160 | + {0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 161 | + {0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 162 | + {0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 163 | + {0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 164 | + {0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU}, | |
| 165 | + /* Bonaire */ | |
| 166 | + {0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY}, | |
| 167 | + {0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY}, | |
| 168 | + {0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY}, | |
| 169 | + {0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY}, | |
| 170 | + {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 171 | + {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 172 | + {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 173 | + {0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 174 | + {0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 175 | + {0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 176 | + {0x1002, 0x665f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, | |
| 177 | + /* Hawaii */ | |
| 178 | + {0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 179 | + {0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 180 | + {0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 181 | + {0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 182 | + {0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 183 | + {0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 184 | + {0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 185 | + {0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 186 | + {0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 187 | + {0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 188 | + {0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 189 | + {0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, | |
| 190 | + /* Kabini */ | |
| 191 | + {0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 192 | + {0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 193 | + {0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 194 | + {0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 195 | + {0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 196 | + {0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 197 | + {0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 198 | + {0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 199 | + {0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 200 | + {0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 201 | + {0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 202 | + {0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 203 | + {0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 204 | + {0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 205 | + {0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 206 | + {0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU}, | |
| 207 | + /* mullins */ | |
| 208 | + {0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 209 | + {0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 210 | + {0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 211 | + {0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 212 | + {0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 213 | + {0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 214 | + {0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 215 | + {0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 216 | + {0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 217 | + {0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 218 | + {0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 219 | + {0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 220 | + {0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 221 | + {0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 222 | + {0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 223 | + {0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU}, | |
| 224 | +#endif | |
| 225 | + /* topaz */ | |
| 226 | + {0x1002, 0x6900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, | |
| 227 | + {0x1002, 0x6901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, | |
| 228 | + {0x1002, 0x6902, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, | |
| 229 | + {0x1002, 0x6903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, | |
| 230 | + {0x1002, 0x6907, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, | |
| 231 | + /* tonga */ | |
| 232 | + {0x1002, 0x6920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 233 | + {0x1002, 0x6921, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 234 | + {0x1002, 0x6928, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 235 | + {0x1002, 0x6929, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 236 | + {0x1002, 0x692B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 237 | + {0x1002, 0x692F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 238 | + {0x1002, 0x6930, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 239 | + {0x1002, 0x6938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 240 | + {0x1002, 0x6939, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, | |
| 241 | + /* carrizo */ | |
| 242 | + {0x1002, 0x9870, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU}, | |
| 243 | + {0x1002, 0x9874, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU}, | |
| 244 | + {0x1002, 0x9875, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU}, | |
| 245 | + {0x1002, 0x9876, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU}, | |
| 246 | + {0x1002, 0x9877, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU}, | |
| 247 | + | |
| 248 | + {0, 0, 0} | |
| 249 | +}; | |
| 250 | + | |
| 251 | +MODULE_DEVICE_TABLE(pci, pciidlist); | |
| 252 | + | |
| 253 | +static struct drm_driver kms_driver; | |
| 254 | + | |
| 255 | +static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev) | |
| 256 | +{ | |
| 257 | + struct apertures_struct *ap; | |
| 258 | + bool primary = false; | |
| 259 | + | |
| 260 | + ap = alloc_apertures(1); | |
| 261 | + if (!ap) | |
| 262 | + return -ENOMEM; | |
| 263 | + | |
| 264 | + ap->ranges[0].base = pci_resource_start(pdev, 0); | |
| 265 | + ap->ranges[0].size = pci_resource_len(pdev, 0); | |
| 266 | + | |
| 267 | +#ifdef CONFIG_X86 | |
| 268 | + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; | |
| 269 | +#endif | |
| 270 | + remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary); | |
| 271 | + kfree(ap); | |
| 272 | + | |
| 273 | + return 0; | |
| 274 | +} | |
| 275 | + | |
| 276 | +static int amdgpu_pci_probe(struct pci_dev *pdev, | |
| 277 | + const struct pci_device_id *ent) | |
| 278 | +{ | |
| 279 | + unsigned long flags = ent->driver_data; | |
| 280 | + int ret; | |
| 281 | + | |
| 282 | + if ((flags & AMDGPU_EXP_HW_SUPPORT) && !amdgpu_exp_hw_support) { | |
| 283 | + DRM_INFO("This hardware requires experimental hardware support.\n" | |
| 284 | + "See modparam exp_hw_support\n"); | |
| 285 | + return -ENODEV; | |
| 286 | + } | |
| 287 | + | |
| 288 | + /* Get rid of things like offb */ | |
| 289 | + ret = amdgpu_kick_out_firmware_fb(pdev); | |
| 290 | + if (ret) | |
| 291 | + return ret; | |
| 292 | + | |
| 293 | + return drm_get_pci_dev(pdev, ent, &kms_driver); | |
| 294 | +} | |
| 295 | + | |
| 296 | +static void | |
| 297 | +amdgpu_pci_remove(struct pci_dev *pdev) | |
| 298 | +{ | |
| 299 | + struct drm_device *dev = pci_get_drvdata(pdev); | |
| 300 | + | |
| 301 | + drm_put_dev(dev); | |
| 302 | +} | |
| 303 | + | |
| 304 | +static int amdgpu_pmops_suspend(struct device *dev) | |
| 305 | +{ | |
| 306 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 307 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 308 | + return amdgpu_suspend_kms(drm_dev, true, true); | |
| 309 | +} | |
| 310 | + | |
| 311 | +static int amdgpu_pmops_resume(struct device *dev) | |
| 312 | +{ | |
| 313 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 314 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 315 | + return amdgpu_resume_kms(drm_dev, true, true); | |
| 316 | +} | |
| 317 | + | |
| 318 | +static int amdgpu_pmops_freeze(struct device *dev) | |
| 319 | +{ | |
| 320 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 321 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 322 | + return amdgpu_suspend_kms(drm_dev, false, true); | |
| 323 | +} | |
| 324 | + | |
| 325 | +static int amdgpu_pmops_thaw(struct device *dev) | |
| 326 | +{ | |
| 327 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 328 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 329 | + return amdgpu_resume_kms(drm_dev, false, true); | |
| 330 | +} | |
| 331 | + | |
| 332 | +static int amdgpu_pmops_runtime_suspend(struct device *dev) | |
| 333 | +{ | |
| 334 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 335 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 336 | + int ret; | |
| 337 | + | |
| 338 | + if (!amdgpu_device_is_px(drm_dev)) { | |
| 339 | + pm_runtime_forbid(dev); | |
| 340 | + return -EBUSY; | |
| 341 | + } | |
| 342 | + | |
| 343 | + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | |
| 344 | + drm_kms_helper_poll_disable(drm_dev); | |
| 345 | + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); | |
| 346 | + | |
| 347 | + ret = amdgpu_suspend_kms(drm_dev, false, false); | |
| 348 | + pci_save_state(pdev); | |
| 349 | + pci_disable_device(pdev); | |
| 350 | + pci_ignore_hotplug(pdev); | |
| 351 | + pci_set_power_state(pdev, PCI_D3cold); | |
| 352 | + drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; | |
| 353 | + | |
| 354 | + return 0; | |
| 355 | +} | |
| 356 | + | |
| 357 | +static int amdgpu_pmops_runtime_resume(struct device *dev) | |
| 358 | +{ | |
| 359 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 360 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 361 | + int ret; | |
| 362 | + | |
| 363 | + if (!amdgpu_device_is_px(drm_dev)) | |
| 364 | + return -EINVAL; | |
| 365 | + | |
| 366 | + drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | |
| 367 | + | |
| 368 | + pci_set_power_state(pdev, PCI_D0); | |
| 369 | + pci_restore_state(pdev); | |
| 370 | + ret = pci_enable_device(pdev); | |
| 371 | + if (ret) | |
| 372 | + return ret; | |
| 373 | + pci_set_master(pdev); | |
| 374 | + | |
| 375 | + ret = amdgpu_resume_kms(drm_dev, false, false); | |
| 376 | + drm_kms_helper_poll_enable(drm_dev); | |
| 377 | + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); | |
| 378 | + drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; | |
| 379 | + return 0; | |
| 380 | +} | |
| 381 | + | |
| 382 | +static int amdgpu_pmops_runtime_idle(struct device *dev) | |
| 383 | +{ | |
| 384 | + struct pci_dev *pdev = to_pci_dev(dev); | |
| 385 | + struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
| 386 | + struct drm_crtc *crtc; | |
| 387 | + | |
| 388 | + if (!amdgpu_device_is_px(drm_dev)) { | |
| 389 | + pm_runtime_forbid(dev); | |
| 390 | + return -EBUSY; | |
| 391 | + } | |
| 392 | + | |
| 393 | + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { | |
| 394 | + if (crtc->enabled) { | |
| 395 | + DRM_DEBUG_DRIVER("failing to power off - crtc active\n"); | |
| 396 | + return -EBUSY; | |
| 397 | + } | |
| 398 | + } | |
| 399 | + | |
| 400 | + pm_runtime_mark_last_busy(dev); | |
| 401 | + pm_runtime_autosuspend(dev); | |
| 402 | + /* we don't want the main rpm_idle to call suspend - we want to autosuspend */ | |
| 403 | + return 1; | |
| 404 | +} | |
| 405 | + | |
| 406 | +long amdgpu_drm_ioctl(struct file *filp, | |
| 407 | + unsigned int cmd, unsigned long arg) | |
| 408 | +{ | |
| 409 | + struct drm_file *file_priv = filp->private_data; | |
| 410 | + struct drm_device *dev; | |
| 411 | + long ret; | |
| 412 | + dev = file_priv->minor->dev; | |
| 413 | + ret = pm_runtime_get_sync(dev->dev); | |
| 414 | + if (ret < 0) | |
| 415 | + return ret; | |
| 416 | + | |
| 417 | + ret = drm_ioctl(filp, cmd, arg); | |
| 418 | + | |
| 419 | + pm_runtime_mark_last_busy(dev->dev); | |
| 420 | + pm_runtime_put_autosuspend(dev->dev); | |
| 421 | + return ret; | |
| 422 | +} | |
| 423 | + | |
| 424 | +static const struct dev_pm_ops amdgpu_pm_ops = { | |
| 425 | + .suspend = amdgpu_pmops_suspend, | |
| 426 | + .resume = amdgpu_pmops_resume, | |
| 427 | + .freeze = amdgpu_pmops_freeze, | |
| 428 | + .thaw = amdgpu_pmops_thaw, | |
| 429 | + .poweroff = amdgpu_pmops_freeze, | |
| 430 | + .restore = amdgpu_pmops_resume, | |
| 431 | + .runtime_suspend = amdgpu_pmops_runtime_suspend, | |
| 432 | + .runtime_resume = amdgpu_pmops_runtime_resume, | |
| 433 | + .runtime_idle = amdgpu_pmops_runtime_idle, | |
| 434 | +}; | |
| 435 | + | |
| 436 | +static const struct file_operations amdgpu_driver_kms_fops = { | |
| 437 | + .owner = THIS_MODULE, | |
| 438 | + .open = drm_open, | |
| 439 | + .release = drm_release, | |
| 440 | + .unlocked_ioctl = amdgpu_drm_ioctl, | |
| 441 | + .mmap = amdgpu_mmap, | |
| 442 | + .poll = drm_poll, | |
| 443 | + .read = drm_read, | |
| 444 | +#ifdef CONFIG_COMPAT | |
| 445 | + .compat_ioctl = amdgpu_kms_compat_ioctl, | |
| 446 | +#endif | |
| 447 | +}; | |
| 448 | + | |
| 449 | +static struct drm_driver kms_driver = { | |
| 450 | + .driver_features = | |
| 451 | + DRIVER_USE_AGP | | |
| 452 | + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | | |
| 453 | + DRIVER_PRIME | DRIVER_RENDER, | |
| 454 | + .dev_priv_size = 0, | |
| 455 | + .load = amdgpu_driver_load_kms, | |
| 456 | + .open = amdgpu_driver_open_kms, | |
| 457 | + .preclose = amdgpu_driver_preclose_kms, | |
| 458 | + .postclose = amdgpu_driver_postclose_kms, | |
| 459 | + .lastclose = amdgpu_driver_lastclose_kms, | |
| 460 | + .set_busid = drm_pci_set_busid, | |
| 461 | + .unload = amdgpu_driver_unload_kms, | |
| 462 | + .get_vblank_counter = amdgpu_get_vblank_counter_kms, | |
| 463 | + .enable_vblank = amdgpu_enable_vblank_kms, | |
| 464 | + .disable_vblank = amdgpu_disable_vblank_kms, | |
| 465 | + .get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms, | |
| 466 | + .get_scanout_position = amdgpu_get_crtc_scanoutpos, | |
| 467 | +#if defined(CONFIG_DEBUG_FS) | |
| 468 | + .debugfs_init = amdgpu_debugfs_init, | |
| 469 | + .debugfs_cleanup = amdgpu_debugfs_cleanup, | |
| 470 | +#endif | |
| 471 | + .irq_preinstall = amdgpu_irq_preinstall, | |
| 472 | + .irq_postinstall = amdgpu_irq_postinstall, | |
| 473 | + .irq_uninstall = amdgpu_irq_uninstall, | |
| 474 | + .irq_handler = amdgpu_irq_handler, | |
| 475 | + .ioctls = amdgpu_ioctls_kms, | |
| 476 | + .gem_free_object = amdgpu_gem_object_free, | |
| 477 | + .gem_open_object = amdgpu_gem_object_open, | |
| 478 | + .gem_close_object = amdgpu_gem_object_close, | |
| 479 | + .dumb_create = amdgpu_mode_dumb_create, | |
| 480 | + .dumb_map_offset = amdgpu_mode_dumb_mmap, | |
| 481 | + .dumb_destroy = drm_gem_dumb_destroy, | |
| 482 | + .fops = &amdgpu_driver_kms_fops, | |
| 483 | + | |
| 484 | + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | |
| 485 | + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
| 486 | + .gem_prime_export = amdgpu_gem_prime_export, | |
| 487 | + .gem_prime_import = drm_gem_prime_import, | |
| 488 | + .gem_prime_pin = amdgpu_gem_prime_pin, | |
| 489 | + .gem_prime_unpin = amdgpu_gem_prime_unpin, | |
| 490 | + .gem_prime_res_obj = amdgpu_gem_prime_res_obj, | |
| 491 | + .gem_prime_get_sg_table = amdgpu_gem_prime_get_sg_table, | |
| 492 | + .gem_prime_import_sg_table = amdgpu_gem_prime_import_sg_table, | |
| 493 | + .gem_prime_vmap = amdgpu_gem_prime_vmap, | |
| 494 | + .gem_prime_vunmap = amdgpu_gem_prime_vunmap, | |
| 495 | + | |
| 496 | + .name = DRIVER_NAME, | |
| 497 | + .desc = DRIVER_DESC, | |
| 498 | + .date = DRIVER_DATE, | |
| 499 | + .major = KMS_DRIVER_MAJOR, | |
| 500 | + .minor = KMS_DRIVER_MINOR, | |
| 501 | + .patchlevel = KMS_DRIVER_PATCHLEVEL, | |
| 502 | +}; | |
| 503 | + | |
| 504 | +static struct drm_driver *driver; | |
| 505 | +static struct pci_driver *pdriver; | |
| 506 | + | |
| 507 | +static struct pci_driver amdgpu_kms_pci_driver = { | |
| 508 | + .name = DRIVER_NAME, | |
| 509 | + .id_table = pciidlist, | |
| 510 | + .probe = amdgpu_pci_probe, | |
| 511 | + .remove = amdgpu_pci_remove, | |
| 512 | + .driver.pm = &amdgpu_pm_ops, | |
| 513 | +}; | |
| 514 | + | |
| 515 | +static int __init amdgpu_init(void) | |
| 516 | +{ | |
| 517 | +#ifdef CONFIG_VGA_CONSOLE | |
| 518 | + if (vgacon_text_force()) { | |
| 519 | + DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); | |
| 520 | + return -EINVAL; | |
| 521 | + } | |
| 522 | +#endif | |
| 523 | + DRM_INFO("amdgpu kernel modesetting enabled.\n"); | |
| 524 | + driver = &kms_driver; | |
| 525 | + pdriver = &amdgpu_kms_pci_driver; | |
| 526 | + driver->driver_features |= DRIVER_MODESET; | |
| 527 | + driver->num_ioctls = amdgpu_max_kms_ioctl; | |
| 528 | + amdgpu_register_atpx_handler(); | |
| 529 | + | |
| 530 | + /* let modprobe override vga console setting */ | |
| 531 | + return drm_pci_init(driver, pdriver); | |
| 532 | +} | |
| 533 | + | |
| 534 | +static void __exit amdgpu_exit(void) | |
| 535 | +{ | |
| 536 | + drm_pci_exit(driver, pdriver); | |
| 537 | + amdgpu_unregister_atpx_handler(); | |
| 538 | +} | |
| 539 | + | |
| 540 | +module_init(amdgpu_init); | |
| 541 | +module_exit(amdgpu_exit); | |
| 542 | + | |
| 543 | +MODULE_AUTHOR(DRIVER_AUTHOR); | |
| 544 | +MODULE_DESCRIPTION(DRIVER_DESC); | |
| 545 | +MODULE_LICENSE("GPL and additional rights"); | |
| @@ -0,0 +1,48 @@ | ||
| 1 | +/* amdgpu_drv.h -- Private header for amdgpu driver -*- linux-c -*- | |
| 2 | + * | |
| 3 | + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. | |
| 4 | + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. | |
| 5 | + * All rights reserved. | |
| 6 | + * | |
| 7 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 8 | + * copy of this software and associated documentation files (the "Software"), | |
| 9 | + * to deal in the Software without restriction, including without limitation | |
| 10 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 11 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 12 | + * Software is furnished to do so, subject to the following conditions: | |
| 13 | + * | |
| 14 | + * The above copyright notice and this permission notice (including the next | |
| 15 | + * paragraph) shall be included in all copies or substantial portions of the | |
| 16 | + * Software. | |
| 17 | + * | |
| 18 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 19 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 20 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 21 | + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 22 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 23 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 24 | + * DEALINGS IN THE SOFTWARE. | |
| 25 | + * | |
| 26 | + */ | |
| 27 | + | |
| 28 | +#ifndef __AMDGPU_DRV_H__ | |
| 29 | +#define __AMDGPU_DRV_H__ | |
| 30 | + | |
| 31 | +#include <linux/firmware.h> | |
| 32 | +#include <linux/platform_device.h> | |
| 33 | + | |
| 34 | +#include "amdgpu_family.h" | |
| 35 | + | |
| 36 | +/* General customization: | |
| 37 | + */ | |
| 38 | + | |
| 39 | +#define DRIVER_AUTHOR "AMD linux driver team" | |
| 40 | + | |
| 41 | +#define DRIVER_NAME "amdgpu" | |
| 42 | +#define DRIVER_DESC "AMD GPU" | |
| 43 | +#define DRIVER_DATE "20150101" | |
| 44 | + | |
| 45 | +long amdgpu_drm_ioctl(struct file *filp, | |
| 46 | + unsigned int cmd, unsigned long arg); | |
| 47 | + | |
| 48 | +#endif | |
| @@ -0,0 +1,245 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2007-8 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the "Software"), | |
| 7 | + * to deal in the Software without restriction, including without limitation | |
| 8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 10 | + * Software is furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in | |
| 13 | + * all copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 18 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 19 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 20 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 21 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 22 | + * | |
| 23 | + * Authors: Dave Airlie | |
| 24 | + * Alex Deucher | |
| 25 | + */ | |
| 26 | +#include <drm/drmP.h> | |
| 27 | +#include <drm/drm_crtc_helper.h> | |
| 28 | +#include <drm/amdgpu_drm.h> | |
| 29 | +#include "amdgpu.h" | |
| 30 | +#include "amdgpu_connectors.h" | |
| 31 | +#include "atom.h" | |
| 32 | +#include "atombios_encoders.h" | |
| 33 | + | |
| 34 | +void | |
| 35 | +amdgpu_link_encoder_connector(struct drm_device *dev) | |
| 36 | +{ | |
| 37 | + struct amdgpu_device *adev = dev->dev_private; | |
| 38 | + struct drm_connector *connector; | |
| 39 | + struct amdgpu_connector *amdgpu_connector; | |
| 40 | + struct drm_encoder *encoder; | |
| 41 | + struct amdgpu_encoder *amdgpu_encoder; | |
| 42 | + | |
| 43 | + /* walk the list and link encoders to connectors */ | |
| 44 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 45 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 46 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
| 47 | + amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 48 | + if (amdgpu_encoder->devices & amdgpu_connector->devices) { | |
| 49 | + drm_mode_connector_attach_encoder(connector, encoder); | |
| 50 | + if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { | |
| 51 | + amdgpu_atombios_encoder_init_backlight(amdgpu_encoder, connector); | |
| 52 | + adev->mode_info.bl_encoder = amdgpu_encoder; | |
| 53 | + } | |
| 54 | + } | |
| 55 | + } | |
| 56 | + } | |
| 57 | +} | |
| 58 | + | |
| 59 | +void amdgpu_encoder_set_active_device(struct drm_encoder *encoder) | |
| 60 | +{ | |
| 61 | + struct drm_device *dev = encoder->dev; | |
| 62 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 63 | + struct drm_connector *connector; | |
| 64 | + | |
| 65 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 66 | + if (connector->encoder == encoder) { | |
| 67 | + struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); | |
| 68 | + amdgpu_encoder->active_device = amdgpu_encoder->devices & amdgpu_connector->devices; | |
| 69 | + DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n", | |
| 70 | + amdgpu_encoder->active_device, amdgpu_encoder->devices, | |
| 71 | + amdgpu_connector->devices, encoder->encoder_type); | |
| 72 | + } | |
| 73 | + } | |
| 74 | +} | |
| 75 | + | |
| 76 | +struct drm_connector * | |
| 77 | +amdgpu_get_connector_for_encoder(struct drm_encoder *encoder) | |
| 78 | +{ | |
| 79 | + struct drm_device *dev = encoder->dev; | |
| 80 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 81 | + struct drm_connector *connector; | |
| 82 | + struct amdgpu_connector *amdgpu_connector; | |
| 83 | + | |
| 84 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 85 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 86 | + if (amdgpu_encoder->active_device & amdgpu_connector->devices) | |
| 87 | + return connector; | |
| 88 | + } | |
| 89 | + return NULL; | |
| 90 | +} | |
| 91 | + | |
| 92 | +struct drm_connector * | |
| 93 | +amdgpu_get_connector_for_encoder_init(struct drm_encoder *encoder) | |
| 94 | +{ | |
| 95 | + struct drm_device *dev = encoder->dev; | |
| 96 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 97 | + struct drm_connector *connector; | |
| 98 | + struct amdgpu_connector *amdgpu_connector; | |
| 99 | + | |
| 100 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
| 101 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 102 | + if (amdgpu_encoder->devices & amdgpu_connector->devices) | |
| 103 | + return connector; | |
| 104 | + } | |
| 105 | + return NULL; | |
| 106 | +} | |
| 107 | + | |
| 108 | +struct drm_encoder *amdgpu_get_external_encoder(struct drm_encoder *encoder) | |
| 109 | +{ | |
| 110 | + struct drm_device *dev = encoder->dev; | |
| 111 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 112 | + struct drm_encoder *other_encoder; | |
| 113 | + struct amdgpu_encoder *other_amdgpu_encoder; | |
| 114 | + | |
| 115 | + if (amdgpu_encoder->is_ext_encoder) | |
| 116 | + return NULL; | |
| 117 | + | |
| 118 | + list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) { | |
| 119 | + if (other_encoder == encoder) | |
| 120 | + continue; | |
| 121 | + other_amdgpu_encoder = to_amdgpu_encoder(other_encoder); | |
| 122 | + if (other_amdgpu_encoder->is_ext_encoder && | |
| 123 | + (amdgpu_encoder->devices & other_amdgpu_encoder->devices)) | |
| 124 | + return other_encoder; | |
| 125 | + } | |
| 126 | + return NULL; | |
| 127 | +} | |
| 128 | + | |
| 129 | +u16 amdgpu_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder) | |
| 130 | +{ | |
| 131 | + struct drm_encoder *other_encoder = amdgpu_get_external_encoder(encoder); | |
| 132 | + | |
| 133 | + if (other_encoder) { | |
| 134 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(other_encoder); | |
| 135 | + | |
| 136 | + switch (amdgpu_encoder->encoder_id) { | |
| 137 | + case ENCODER_OBJECT_ID_TRAVIS: | |
| 138 | + case ENCODER_OBJECT_ID_NUTMEG: | |
| 139 | + return amdgpu_encoder->encoder_id; | |
| 140 | + default: | |
| 141 | + return ENCODER_OBJECT_ID_NONE; | |
| 142 | + } | |
| 143 | + } | |
| 144 | + return ENCODER_OBJECT_ID_NONE; | |
| 145 | +} | |
| 146 | + | |
| 147 | +void amdgpu_panel_mode_fixup(struct drm_encoder *encoder, | |
| 148 | + struct drm_display_mode *adjusted_mode) | |
| 149 | +{ | |
| 150 | + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); | |
| 151 | + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; | |
| 152 | + unsigned hblank = native_mode->htotal - native_mode->hdisplay; | |
| 153 | + unsigned vblank = native_mode->vtotal - native_mode->vdisplay; | |
| 154 | + unsigned hover = native_mode->hsync_start - native_mode->hdisplay; | |
| 155 | + unsigned vover = native_mode->vsync_start - native_mode->vdisplay; | |
| 156 | + unsigned hsync_width = native_mode->hsync_end - native_mode->hsync_start; | |
| 157 | + unsigned vsync_width = native_mode->vsync_end - native_mode->vsync_start; | |
| 158 | + | |
| 159 | + adjusted_mode->clock = native_mode->clock; | |
| 160 | + adjusted_mode->flags = native_mode->flags; | |
| 161 | + | |
| 162 | + adjusted_mode->hdisplay = native_mode->hdisplay; | |
| 163 | + adjusted_mode->vdisplay = native_mode->vdisplay; | |
| 164 | + | |
| 165 | + adjusted_mode->htotal = native_mode->hdisplay + hblank; | |
| 166 | + adjusted_mode->hsync_start = native_mode->hdisplay + hover; | |
| 167 | + adjusted_mode->hsync_end = adjusted_mode->hsync_start + hsync_width; | |
| 168 | + | |
| 169 | + adjusted_mode->vtotal = native_mode->vdisplay + vblank; | |
| 170 | + adjusted_mode->vsync_start = native_mode->vdisplay + vover; | |
| 171 | + adjusted_mode->vsync_end = adjusted_mode->vsync_start + vsync_width; | |
| 172 | + | |
| 173 | + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); | |
| 174 | + | |
| 175 | + adjusted_mode->crtc_hdisplay = native_mode->hdisplay; | |
| 176 | + adjusted_mode->crtc_vdisplay = native_mode->vdisplay; | |
| 177 | + | |
| 178 | + adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + hblank; | |
| 179 | + adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + hover; | |
| 180 | + adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + hsync_width; | |
| 181 | + | |
| 182 | + adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + vblank; | |
| 183 | + adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + vover; | |
| 184 | + adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + vsync_width; | |
| 185 | + | |
| 186 | +} | |
| 187 | + | |
| 188 | +bool amdgpu_dig_monitor_is_duallink(struct drm_encoder *encoder, | |
| 189 | + u32 pixel_clock) | |
| 190 | +{ | |
| 191 | + struct drm_connector *connector; | |
| 192 | + struct amdgpu_connector *amdgpu_connector; | |
| 193 | + struct amdgpu_connector_atom_dig *dig_connector; | |
| 194 | + | |
| 195 | + connector = amdgpu_get_connector_for_encoder(encoder); | |
| 196 | + /* if we don't have an active device yet, just use one of | |
| 197 | + * the connectors tied to the encoder. | |
| 198 | + */ | |
| 199 | + if (!connector) | |
| 200 | + connector = amdgpu_get_connector_for_encoder_init(encoder); | |
| 201 | + amdgpu_connector = to_amdgpu_connector(connector); | |
| 202 | + | |
| 203 | + switch (connector->connector_type) { | |
| 204 | + case DRM_MODE_CONNECTOR_DVII: | |
| 205 | + case DRM_MODE_CONNECTOR_HDMIB: | |
| 206 | + if (amdgpu_connector->use_digital) { | |
| 207 | + /* HDMI 1.3 supports up to 340 Mhz over single link */ | |
| 208 | + if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 209 | + if (pixel_clock > 340000) | |
| 210 | + return true; | |
| 211 | + else | |
| 212 | + return false; | |
| 213 | + } else { | |
| 214 | + if (pixel_clock > 165000) | |
| 215 | + return true; | |
| 216 | + else | |
| 217 | + return false; | |
| 218 | + } | |
| 219 | + } else | |
| 220 | + return false; | |
| 221 | + case DRM_MODE_CONNECTOR_DVID: | |
| 222 | + case DRM_MODE_CONNECTOR_HDMIA: | |
| 223 | + case DRM_MODE_CONNECTOR_DisplayPort: | |
| 224 | + dig_connector = amdgpu_connector->con_priv; | |
| 225 | + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || | |
| 226 | + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) | |
| 227 | + return false; | |
| 228 | + else { | |
| 229 | + /* HDMI 1.3 supports up to 340 Mhz over single link */ | |
| 230 | + if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { | |
| 231 | + if (pixel_clock > 340000) | |
| 232 | + return true; | |
| 233 | + else | |
| 234 | + return false; | |
| 235 | + } else { | |
| 236 | + if (pixel_clock > 165000) | |
| 237 | + return true; | |
| 238 | + else | |
| 239 | + return false; | |
| 240 | + } | |
| 241 | + } | |
| 242 | + default: | |
| 243 | + return false; | |
| 244 | + } | |
| 245 | +} | |
| @@ -0,0 +1,62 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + */ | |
| 28 | + | |
| 29 | +/* this file defines the CHIP_ and family flags used in the pciids, | |
| 30 | + * its is common between kms and non-kms because duplicating it and | |
| 31 | + * changing one place is fail. | |
| 32 | + */ | |
| 33 | +#ifndef AMDGPU_FAMILY_H | |
| 34 | +#define AMDGPU_FAMILY_H | |
| 35 | +/* | |
| 36 | + * Supported ASIC types | |
| 37 | + */ | |
| 38 | +enum amdgpu_asic_type { | |
| 39 | + CHIP_BONAIRE = 0, | |
| 40 | + CHIP_KAVERI, | |
| 41 | + CHIP_KABINI, | |
| 42 | + CHIP_HAWAII, | |
| 43 | + CHIP_MULLINS, | |
| 44 | + CHIP_TOPAZ, | |
| 45 | + CHIP_TONGA, | |
| 46 | + CHIP_CARRIZO, | |
| 47 | + CHIP_LAST, | |
| 48 | +}; | |
| 49 | + | |
| 50 | +/* | |
| 51 | + * Chip flags | |
| 52 | + */ | |
| 53 | +enum amdgpu_chip_flags { | |
| 54 | + AMDGPU_ASIC_MASK = 0x0000ffffUL, | |
| 55 | + AMDGPU_FLAGS_MASK = 0xffff0000UL, | |
| 56 | + AMDGPU_IS_MOBILITY = 0x00010000UL, | |
| 57 | + AMDGPU_IS_APU = 0x00020000UL, | |
| 58 | + AMDGPU_IS_PX = 0x00040000UL, | |
| 59 | + AMDGPU_EXP_HW_SUPPORT = 0x00080000UL, | |
| 60 | +}; | |
| 61 | + | |
| 62 | +#endif | |
| @@ -0,0 +1,421 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright © 2007 David Airlie | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice (including the next | |
| 12 | + * paragraph) shall be included in all copies or substantial portions of the | |
| 13 | + * Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 18 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 20 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 21 | + * DEALINGS IN THE SOFTWARE. | |
| 22 | + * | |
| 23 | + * Authors: | |
| 24 | + * David Airlie | |
| 25 | + */ | |
| 26 | +#include <linux/module.h> | |
| 27 | +#include <linux/slab.h> | |
| 28 | +#include <linux/fb.h> | |
| 29 | + | |
| 30 | +#include <drm/drmP.h> | |
| 31 | +#include <drm/drm_crtc.h> | |
| 32 | +#include <drm/drm_crtc_helper.h> | |
| 33 | +#include <drm/amdgpu_drm.h> | |
| 34 | +#include "amdgpu.h" | |
| 35 | +#include "cikd.h" | |
| 36 | + | |
| 37 | +#include <drm/drm_fb_helper.h> | |
| 38 | + | |
| 39 | +#include <linux/vga_switcheroo.h> | |
| 40 | + | |
| 41 | +/* object hierarchy - | |
| 42 | + this contains a helper + a amdgpu fb | |
| 43 | + the helper contains a pointer to amdgpu framebuffer baseclass. | |
| 44 | +*/ | |
| 45 | +struct amdgpu_fbdev { | |
| 46 | + struct drm_fb_helper helper; | |
| 47 | + struct amdgpu_framebuffer rfb; | |
| 48 | + struct list_head fbdev_list; | |
| 49 | + struct amdgpu_device *adev; | |
| 50 | +}; | |
| 51 | + | |
| 52 | +static struct fb_ops amdgpufb_ops = { | |
| 53 | + .owner = THIS_MODULE, | |
| 54 | + .fb_check_var = drm_fb_helper_check_var, | |
| 55 | + .fb_set_par = drm_fb_helper_set_par, | |
| 56 | + .fb_fillrect = cfb_fillrect, | |
| 57 | + .fb_copyarea = cfb_copyarea, | |
| 58 | + .fb_imageblit = cfb_imageblit, | |
| 59 | + .fb_pan_display = drm_fb_helper_pan_display, | |
| 60 | + .fb_blank = drm_fb_helper_blank, | |
| 61 | + .fb_setcmap = drm_fb_helper_setcmap, | |
| 62 | + .fb_debug_enter = drm_fb_helper_debug_enter, | |
| 63 | + .fb_debug_leave = drm_fb_helper_debug_leave, | |
| 64 | +}; | |
| 65 | + | |
| 66 | + | |
| 67 | +int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tiled) | |
| 68 | +{ | |
| 69 | + int aligned = width; | |
| 70 | + int pitch_mask = 0; | |
| 71 | + | |
| 72 | + switch (bpp / 8) { | |
| 73 | + case 1: | |
| 74 | + pitch_mask = 255; | |
| 75 | + break; | |
| 76 | + case 2: | |
| 77 | + pitch_mask = 127; | |
| 78 | + break; | |
| 79 | + case 3: | |
| 80 | + case 4: | |
| 81 | + pitch_mask = 63; | |
| 82 | + break; | |
| 83 | + } | |
| 84 | + | |
| 85 | + aligned += pitch_mask; | |
| 86 | + aligned &= ~pitch_mask; | |
| 87 | + return aligned; | |
| 88 | +} | |
| 89 | + | |
| 90 | +static void amdgpufb_destroy_pinned_object(struct drm_gem_object *gobj) | |
| 91 | +{ | |
| 92 | + struct amdgpu_bo *rbo = gem_to_amdgpu_bo(gobj); | |
| 93 | + int ret; | |
| 94 | + | |
| 95 | + ret = amdgpu_bo_reserve(rbo, false); | |
| 96 | + if (likely(ret == 0)) { | |
| 97 | + amdgpu_bo_kunmap(rbo); | |
| 98 | + amdgpu_bo_unpin(rbo); | |
| 99 | + amdgpu_bo_unreserve(rbo); | |
| 100 | + } | |
| 101 | + drm_gem_object_unreference_unlocked(gobj); | |
| 102 | +} | |
| 103 | + | |
| 104 | +static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, | |
| 105 | + struct drm_mode_fb_cmd2 *mode_cmd, | |
| 106 | + struct drm_gem_object **gobj_p) | |
| 107 | +{ | |
| 108 | + struct amdgpu_device *adev = rfbdev->adev; | |
| 109 | + struct drm_gem_object *gobj = NULL; | |
| 110 | + struct amdgpu_bo *rbo = NULL; | |
| 111 | + bool fb_tiled = false; /* useful for testing */ | |
| 112 | + u32 tiling_flags = 0; | |
| 113 | + int ret; | |
| 114 | + int aligned_size, size; | |
| 115 | + int height = mode_cmd->height; | |
| 116 | + u32 bpp, depth; | |
| 117 | + | |
| 118 | + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); | |
| 119 | + | |
| 120 | + /* need to align pitch with crtc limits */ | |
| 121 | + mode_cmd->pitches[0] = amdgpu_align_pitch(adev, mode_cmd->width, bpp, | |
| 122 | + fb_tiled) * ((bpp + 1) / 8); | |
| 123 | + | |
| 124 | + height = ALIGN(mode_cmd->height, 8); | |
| 125 | + size = mode_cmd->pitches[0] * height; | |
| 126 | + aligned_size = ALIGN(size, PAGE_SIZE); | |
| 127 | + ret = amdgpu_gem_object_create(adev, aligned_size, 0, | |
| 128 | + AMDGPU_GEM_DOMAIN_VRAM, | |
| 129 | + 0, true, | |
| 130 | + &gobj); | |
| 131 | + if (ret) { | |
| 132 | + printk(KERN_ERR "failed to allocate framebuffer (%d)\n", | |
| 133 | + aligned_size); | |
| 134 | + return -ENOMEM; | |
| 135 | + } | |
| 136 | + rbo = gem_to_amdgpu_bo(gobj); | |
| 137 | + | |
| 138 | + if (fb_tiled) | |
| 139 | + tiling_flags = AMDGPU_TILING_SET(ARRAY_MODE, GRPH_ARRAY_2D_TILED_THIN1); | |
| 140 | + | |
| 141 | + ret = amdgpu_bo_reserve(rbo, false); | |
| 142 | + if (unlikely(ret != 0)) | |
| 143 | + goto out_unref; | |
| 144 | + | |
| 145 | + if (tiling_flags) { | |
| 146 | + ret = amdgpu_bo_set_tiling_flags(rbo, | |
| 147 | + tiling_flags); | |
| 148 | + if (ret) | |
| 149 | + dev_err(adev->dev, "FB failed to set tiling flags\n"); | |
| 150 | + } | |
| 151 | + | |
| 152 | + | |
| 153 | + ret = amdgpu_bo_pin_restricted(rbo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, NULL); | |
| 154 | + if (ret) { | |
| 155 | + amdgpu_bo_unreserve(rbo); | |
| 156 | + goto out_unref; | |
| 157 | + } | |
| 158 | + ret = amdgpu_bo_kmap(rbo, NULL); | |
| 159 | + amdgpu_bo_unreserve(rbo); | |
| 160 | + if (ret) { | |
| 161 | + goto out_unref; | |
| 162 | + } | |
| 163 | + | |
| 164 | + *gobj_p = gobj; | |
| 165 | + return 0; | |
| 166 | +out_unref: | |
| 167 | + amdgpufb_destroy_pinned_object(gobj); | |
| 168 | + *gobj_p = NULL; | |
| 169 | + return ret; | |
| 170 | +} | |
| 171 | + | |
| 172 | +static int amdgpufb_create(struct drm_fb_helper *helper, | |
| 173 | + struct drm_fb_helper_surface_size *sizes) | |
| 174 | +{ | |
| 175 | + struct amdgpu_fbdev *rfbdev = (struct amdgpu_fbdev *)helper; | |
| 176 | + struct amdgpu_device *adev = rfbdev->adev; | |
| 177 | + struct fb_info *info; | |
| 178 | + struct drm_framebuffer *fb = NULL; | |
| 179 | + struct drm_mode_fb_cmd2 mode_cmd; | |
| 180 | + struct drm_gem_object *gobj = NULL; | |
| 181 | + struct amdgpu_bo *rbo = NULL; | |
| 182 | + struct device *device = &adev->pdev->dev; | |
| 183 | + int ret; | |
| 184 | + unsigned long tmp; | |
| 185 | + | |
| 186 | + mode_cmd.width = sizes->surface_width; | |
| 187 | + mode_cmd.height = sizes->surface_height; | |
| 188 | + | |
| 189 | + if (sizes->surface_bpp == 24) | |
| 190 | + sizes->surface_bpp = 32; | |
| 191 | + | |
| 192 | + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
| 193 | + sizes->surface_depth); | |
| 194 | + | |
| 195 | + ret = amdgpufb_create_pinned_object(rfbdev, &mode_cmd, &gobj); | |
| 196 | + if (ret) { | |
| 197 | + DRM_ERROR("failed to create fbcon object %d\n", ret); | |
| 198 | + return ret; | |
| 199 | + } | |
| 200 | + | |
| 201 | + rbo = gem_to_amdgpu_bo(gobj); | |
| 202 | + | |
| 203 | + /* okay we have an object now allocate the framebuffer */ | |
| 204 | + info = framebuffer_alloc(0, device); | |
| 205 | + if (info == NULL) { | |
| 206 | + ret = -ENOMEM; | |
| 207 | + goto out_unref; | |
| 208 | + } | |
| 209 | + | |
| 210 | + info->par = rfbdev; | |
| 211 | + | |
| 212 | + ret = amdgpu_framebuffer_init(adev->ddev, &rfbdev->rfb, &mode_cmd, gobj); | |
| 213 | + if (ret) { | |
| 214 | + DRM_ERROR("failed to initialize framebuffer %d\n", ret); | |
| 215 | + goto out_unref; | |
| 216 | + } | |
| 217 | + | |
| 218 | + fb = &rfbdev->rfb.base; | |
| 219 | + | |
| 220 | + /* setup helper */ | |
| 221 | + rfbdev->helper.fb = fb; | |
| 222 | + rfbdev->helper.fbdev = info; | |
| 223 | + | |
| 224 | + memset_io(rbo->kptr, 0x0, amdgpu_bo_size(rbo)); | |
| 225 | + | |
| 226 | + strcpy(info->fix.id, "amdgpudrmfb"); | |
| 227 | + | |
| 228 | + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); | |
| 229 | + | |
| 230 | + info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; | |
| 231 | + info->fbops = &amdgpufb_ops; | |
| 232 | + | |
| 233 | + tmp = amdgpu_bo_gpu_offset(rbo) - adev->mc.vram_start; | |
| 234 | + info->fix.smem_start = adev->mc.aper_base + tmp; | |
| 235 | + info->fix.smem_len = amdgpu_bo_size(rbo); | |
| 236 | + info->screen_base = rbo->kptr; | |
| 237 | + info->screen_size = amdgpu_bo_size(rbo); | |
| 238 | + | |
| 239 | + drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height); | |
| 240 | + | |
| 241 | + /* setup aperture base/size for vesafb takeover */ | |
| 242 | + info->apertures = alloc_apertures(1); | |
| 243 | + if (!info->apertures) { | |
| 244 | + ret = -ENOMEM; | |
| 245 | + goto out_unref; | |
| 246 | + } | |
| 247 | + info->apertures->ranges[0].base = adev->ddev->mode_config.fb_base; | |
| 248 | + info->apertures->ranges[0].size = adev->mc.aper_size; | |
| 249 | + | |
| 250 | + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ | |
| 251 | + | |
| 252 | + if (info->screen_base == NULL) { | |
| 253 | + ret = -ENOSPC; | |
| 254 | + goto out_unref; | |
| 255 | + } | |
| 256 | + | |
| 257 | + ret = fb_alloc_cmap(&info->cmap, 256, 0); | |
| 258 | + if (ret) { | |
| 259 | + ret = -ENOMEM; | |
| 260 | + goto out_unref; | |
| 261 | + } | |
| 262 | + | |
| 263 | + DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); | |
| 264 | + DRM_INFO("vram apper at 0x%lX\n", (unsigned long)adev->mc.aper_base); | |
| 265 | + DRM_INFO("size %lu\n", (unsigned long)amdgpu_bo_size(rbo)); | |
| 266 | + DRM_INFO("fb depth is %d\n", fb->depth); | |
| 267 | + DRM_INFO(" pitch is %d\n", fb->pitches[0]); | |
| 268 | + | |
| 269 | + vga_switcheroo_client_fb_set(adev->ddev->pdev, info); | |
| 270 | + return 0; | |
| 271 | + | |
| 272 | +out_unref: | |
| 273 | + if (rbo) { | |
| 274 | + | |
| 275 | + } | |
| 276 | + if (fb && ret) { | |
| 277 | + drm_gem_object_unreference(gobj); | |
| 278 | + drm_framebuffer_unregister_private(fb); | |
| 279 | + drm_framebuffer_cleanup(fb); | |
| 280 | + kfree(fb); | |
| 281 | + } | |
| 282 | + return ret; | |
| 283 | +} | |
| 284 | + | |
| 285 | +void amdgpu_fb_output_poll_changed(struct amdgpu_device *adev) | |
| 286 | +{ | |
| 287 | + if (adev->mode_info.rfbdev) | |
| 288 | + drm_fb_helper_hotplug_event(&adev->mode_info.rfbdev->helper); | |
| 289 | +} | |
| 290 | + | |
| 291 | +static int amdgpu_fbdev_destroy(struct drm_device *dev, struct amdgpu_fbdev *rfbdev) | |
| 292 | +{ | |
| 293 | + struct fb_info *info; | |
| 294 | + struct amdgpu_framebuffer *rfb = &rfbdev->rfb; | |
| 295 | + | |
| 296 | + if (rfbdev->helper.fbdev) { | |
| 297 | + info = rfbdev->helper.fbdev; | |
| 298 | + | |
| 299 | + unregister_framebuffer(info); | |
| 300 | + if (info->cmap.len) | |
| 301 | + fb_dealloc_cmap(&info->cmap); | |
| 302 | + framebuffer_release(info); | |
| 303 | + } | |
| 304 | + | |
| 305 | + if (rfb->obj) { | |
| 306 | + amdgpufb_destroy_pinned_object(rfb->obj); | |
| 307 | + rfb->obj = NULL; | |
| 308 | + } | |
| 309 | + drm_fb_helper_fini(&rfbdev->helper); | |
| 310 | + drm_framebuffer_unregister_private(&rfb->base); | |
| 311 | + drm_framebuffer_cleanup(&rfb->base); | |
| 312 | + | |
| 313 | + return 0; | |
| 314 | +} | |
| 315 | + | |
| 316 | +/** Sets the color ramps on behalf of fbcon */ | |
| 317 | +static void amdgpu_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, | |
| 318 | + u16 blue, int regno) | |
| 319 | +{ | |
| 320 | + struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); | |
| 321 | + | |
| 322 | + amdgpu_crtc->lut_r[regno] = red >> 6; | |
| 323 | + amdgpu_crtc->lut_g[regno] = green >> 6; | |
| 324 | + amdgpu_crtc->lut_b[regno] = blue >> 6; | |
| 325 | +} | |
| 326 | + | |
| 327 | +/** Gets the color ramps on behalf of fbcon */ | |
| 328 | +static void amdgpu_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, | |
| 329 | + u16 *blue, int regno) | |
| 330 | +{ | |
| 331 | + struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); | |
| 332 | + | |
| 333 | + *red = amdgpu_crtc->lut_r[regno] << 6; | |
| 334 | + *green = amdgpu_crtc->lut_g[regno] << 6; | |
| 335 | + *blue = amdgpu_crtc->lut_b[regno] << 6; | |
| 336 | +} | |
| 337 | + | |
| 338 | +static const struct drm_fb_helper_funcs amdgpu_fb_helper_funcs = { | |
| 339 | + .gamma_set = amdgpu_crtc_fb_gamma_set, | |
| 340 | + .gamma_get = amdgpu_crtc_fb_gamma_get, | |
| 341 | + .fb_probe = amdgpufb_create, | |
| 342 | +}; | |
| 343 | + | |
| 344 | +int amdgpu_fbdev_init(struct amdgpu_device *adev) | |
| 345 | +{ | |
| 346 | + struct amdgpu_fbdev *rfbdev; | |
| 347 | + int bpp_sel = 32; | |
| 348 | + int ret; | |
| 349 | + | |
| 350 | + /* don't init fbdev on hw without DCE */ | |
| 351 | + if (!adev->mode_info.mode_config_initialized) | |
| 352 | + return 0; | |
| 353 | + | |
| 354 | + /* select 8 bpp console on low vram cards */ | |
| 355 | + if (adev->mc.real_vram_size <= (32*1024*1024)) | |
| 356 | + bpp_sel = 8; | |
| 357 | + | |
| 358 | + rfbdev = kzalloc(sizeof(struct amdgpu_fbdev), GFP_KERNEL); | |
| 359 | + if (!rfbdev) | |
| 360 | + return -ENOMEM; | |
| 361 | + | |
| 362 | + rfbdev->adev = adev; | |
| 363 | + adev->mode_info.rfbdev = rfbdev; | |
| 364 | + | |
| 365 | + drm_fb_helper_prepare(adev->ddev, &rfbdev->helper, | |
| 366 | + &amdgpu_fb_helper_funcs); | |
| 367 | + | |
| 368 | + ret = drm_fb_helper_init(adev->ddev, &rfbdev->helper, | |
| 369 | + adev->mode_info.num_crtc, | |
| 370 | + AMDGPUFB_CONN_LIMIT); | |
| 371 | + if (ret) { | |
| 372 | + kfree(rfbdev); | |
| 373 | + return ret; | |
| 374 | + } | |
| 375 | + | |
| 376 | + drm_fb_helper_single_add_all_connectors(&rfbdev->helper); | |
| 377 | + | |
| 378 | + /* disable all the possible outputs/crtcs before entering KMS mode */ | |
| 379 | + drm_helper_disable_unused_functions(adev->ddev); | |
| 380 | + | |
| 381 | + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); | |
| 382 | + return 0; | |
| 383 | +} | |
| 384 | + | |
| 385 | +void amdgpu_fbdev_fini(struct amdgpu_device *adev) | |
| 386 | +{ | |
| 387 | + if (!adev->mode_info.rfbdev) | |
| 388 | + return; | |
| 389 | + | |
| 390 | + amdgpu_fbdev_destroy(adev->ddev, adev->mode_info.rfbdev); | |
| 391 | + kfree(adev->mode_info.rfbdev); | |
| 392 | + adev->mode_info.rfbdev = NULL; | |
| 393 | +} | |
| 394 | + | |
| 395 | +void amdgpu_fbdev_set_suspend(struct amdgpu_device *adev, int state) | |
| 396 | +{ | |
| 397 | + if (adev->mode_info.rfbdev) | |
| 398 | + fb_set_suspend(adev->mode_info.rfbdev->helper.fbdev, state); | |
| 399 | +} | |
| 400 | + | |
| 401 | +int amdgpu_fbdev_total_size(struct amdgpu_device *adev) | |
| 402 | +{ | |
| 403 | + struct amdgpu_bo *robj; | |
| 404 | + int size = 0; | |
| 405 | + | |
| 406 | + if (!adev->mode_info.rfbdev) | |
| 407 | + return 0; | |
| 408 | + | |
| 409 | + robj = gem_to_amdgpu_bo(adev->mode_info.rfbdev->rfb.obj); | |
| 410 | + size += amdgpu_bo_size(robj); | |
| 411 | + return size; | |
| 412 | +} | |
| 413 | + | |
| 414 | +bool amdgpu_fbdev_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj) | |
| 415 | +{ | |
| 416 | + if (!adev->mode_info.rfbdev) | |
| 417 | + return false; | |
| 418 | + if (robj == gem_to_amdgpu_bo(adev->mode_info.rfbdev->rfb.obj)) | |
| 419 | + return true; | |
| 420 | + return false; | |
| 421 | +} | |
| @@ -0,0 +1,1133 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2009 Jerome Glisse. | |
| 3 | + * All Rights Reserved. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the | |
| 7 | + * "Software"), to deal in the Software without restriction, including | |
| 8 | + * without limitation the rights to use, copy, modify, merge, publish, | |
| 9 | + * distribute, sub license, and/or sell copies of the Software, and to | |
| 10 | + * permit persons to whom the Software is furnished to do so, subject to | |
| 11 | + * the following conditions: | |
| 12 | + * | |
| 13 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 14 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 15 | + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
| 16 | + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | |
| 17 | + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
| 18 | + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
| 19 | + * USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 20 | + * | |
| 21 | + * The above copyright notice and this permission notice (including the | |
| 22 | + * next paragraph) shall be included in all copies or substantial portions | |
| 23 | + * of the Software. | |
| 24 | + * | |
| 25 | + */ | |
| 26 | +/* | |
| 27 | + * Authors: | |
| 28 | + * Jerome Glisse <glisse@freedesktop.org> | |
| 29 | + * Dave Airlie | |
| 30 | + */ | |
| 31 | +#include <linux/seq_file.h> | |
| 32 | +#include <linux/atomic.h> | |
| 33 | +#include <linux/wait.h> | |
| 34 | +#include <linux/kref.h> | |
| 35 | +#include <linux/slab.h> | |
| 36 | +#include <linux/firmware.h> | |
| 37 | +#include <drm/drmP.h> | |
| 38 | +#include "amdgpu.h" | |
| 39 | +#include "amdgpu_trace.h" | |
| 40 | + | |
| 41 | +/* | |
| 42 | + * Fences | |
| 43 | + * Fences mark an event in the GPUs pipeline and are used | |
| 44 | + * for GPU/CPU synchronization. When the fence is written, | |
| 45 | + * it is expected that all buffers associated with that fence | |
| 46 | + * are no longer in use by the associated ring on the GPU and | |
| 47 | + * that the the relevant GPU caches have been flushed. | |
| 48 | + */ | |
| 49 | + | |
| 50 | +/** | |
| 51 | + * amdgpu_fence_write - write a fence value | |
| 52 | + * | |
| 53 | + * @ring: ring the fence is associated with | |
| 54 | + * @seq: sequence number to write | |
| 55 | + * | |
| 56 | + * Writes a fence value to memory (all asics). | |
| 57 | + */ | |
| 58 | +static void amdgpu_fence_write(struct amdgpu_ring *ring, u32 seq) | |
| 59 | +{ | |
| 60 | + struct amdgpu_fence_driver *drv = &ring->fence_drv; | |
| 61 | + | |
| 62 | + if (drv->cpu_addr) | |
| 63 | + *drv->cpu_addr = cpu_to_le32(seq); | |
| 64 | +} | |
| 65 | + | |
| 66 | +/** | |
| 67 | + * amdgpu_fence_read - read a fence value | |
| 68 | + * | |
| 69 | + * @ring: ring the fence is associated with | |
| 70 | + * | |
| 71 | + * Reads a fence value from memory (all asics). | |
| 72 | + * Returns the value of the fence read from memory. | |
| 73 | + */ | |
| 74 | +static u32 amdgpu_fence_read(struct amdgpu_ring *ring) | |
| 75 | +{ | |
| 76 | + struct amdgpu_fence_driver *drv = &ring->fence_drv; | |
| 77 | + u32 seq = 0; | |
| 78 | + | |
| 79 | + if (drv->cpu_addr) | |
| 80 | + seq = le32_to_cpu(*drv->cpu_addr); | |
| 81 | + else | |
| 82 | + seq = lower_32_bits(atomic64_read(&drv->last_seq)); | |
| 83 | + | |
| 84 | + return seq; | |
| 85 | +} | |
| 86 | + | |
| 87 | +/** | |
| 88 | + * amdgpu_fence_schedule_check - schedule lockup check | |
| 89 | + * | |
| 90 | + * @ring: pointer to struct amdgpu_ring | |
| 91 | + * | |
| 92 | + * Queues a delayed work item to check for lockups. | |
| 93 | + */ | |
| 94 | +static void amdgpu_fence_schedule_check(struct amdgpu_ring *ring) | |
| 95 | +{ | |
| 96 | + /* | |
| 97 | + * Do not reset the timer here with mod_delayed_work, | |
| 98 | + * this can livelock in an interaction with TTM delayed destroy. | |
| 99 | + */ | |
| 100 | + queue_delayed_work(system_power_efficient_wq, | |
| 101 | + &ring->fence_drv.lockup_work, | |
| 102 | + AMDGPU_FENCE_JIFFIES_TIMEOUT); | |
| 103 | +} | |
| 104 | + | |
| 105 | +/** | |
| 106 | + * amdgpu_fence_emit - emit a fence on the requested ring | |
| 107 | + * | |
| 108 | + * @ring: ring the fence is associated with | |
| 109 | + * @owner: creator of the fence | |
| 110 | + * @fence: amdgpu fence object | |
| 111 | + * | |
| 112 | + * Emits a fence command on the requested ring (all asics). | |
| 113 | + * Returns 0 on success, -ENOMEM on failure. | |
| 114 | + */ | |
| 115 | +int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner, | |
| 116 | + struct amdgpu_fence **fence) | |
| 117 | +{ | |
| 118 | + struct amdgpu_device *adev = ring->adev; | |
| 119 | + | |
| 120 | + /* we are protected by the ring emission mutex */ | |
| 121 | + *fence = kmalloc(sizeof(struct amdgpu_fence), GFP_KERNEL); | |
| 122 | + if ((*fence) == NULL) { | |
| 123 | + return -ENOMEM; | |
| 124 | + } | |
| 125 | + (*fence)->seq = ++ring->fence_drv.sync_seq[ring->idx]; | |
| 126 | + (*fence)->ring = ring; | |
| 127 | + (*fence)->owner = owner; | |
| 128 | + fence_init(&(*fence)->base, &amdgpu_fence_ops, | |
| 129 | + &adev->fence_queue.lock, adev->fence_context + ring->idx, | |
| 130 | + (*fence)->seq); | |
| 131 | + amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr, | |
| 132 | + (*fence)->seq, | |
| 133 | + AMDGPU_FENCE_FLAG_INT); | |
| 134 | + trace_amdgpu_fence_emit(ring->adev->ddev, ring->idx, (*fence)->seq); | |
| 135 | + return 0; | |
| 136 | +} | |
| 137 | + | |
| 138 | +/** | |
| 139 | + * amdgpu_fence_check_signaled - callback from fence_queue | |
| 140 | + * | |
| 141 | + * this function is called with fence_queue lock held, which is also used | |
| 142 | + * for the fence locking itself, so unlocked variants are used for | |
| 143 | + * fence_signal, and remove_wait_queue. | |
| 144 | + */ | |
| 145 | +static int amdgpu_fence_check_signaled(wait_queue_t *wait, unsigned mode, int flags, void *key) | |
| 146 | +{ | |
| 147 | + struct amdgpu_fence *fence; | |
| 148 | + struct amdgpu_device *adev; | |
| 149 | + u64 seq; | |
| 150 | + int ret; | |
| 151 | + | |
| 152 | + fence = container_of(wait, struct amdgpu_fence, fence_wake); | |
| 153 | + adev = fence->ring->adev; | |
| 154 | + | |
| 155 | + /* | |
| 156 | + * We cannot use amdgpu_fence_process here because we're already | |
| 157 | + * in the waitqueue, in a call from wake_up_all. | |
| 158 | + */ | |
| 159 | + seq = atomic64_read(&fence->ring->fence_drv.last_seq); | |
| 160 | + if (seq >= fence->seq) { | |
| 161 | + ret = fence_signal_locked(&fence->base); | |
| 162 | + if (!ret) | |
| 163 | + FENCE_TRACE(&fence->base, "signaled from irq context\n"); | |
| 164 | + else | |
| 165 | + FENCE_TRACE(&fence->base, "was already signaled\n"); | |
| 166 | + | |
| 167 | + amdgpu_irq_put(adev, fence->ring->fence_drv.irq_src, | |
| 168 | + fence->ring->fence_drv.irq_type); | |
| 169 | + __remove_wait_queue(&adev->fence_queue, &fence->fence_wake); | |
| 170 | + fence_put(&fence->base); | |
| 171 | + } else | |
| 172 | + FENCE_TRACE(&fence->base, "pending\n"); | |
| 173 | + return 0; | |
| 174 | +} | |
| 175 | + | |
| 176 | +/** | |
| 177 | + * amdgpu_fence_activity - check for fence activity | |
| 178 | + * | |
| 179 | + * @ring: pointer to struct amdgpu_ring | |
| 180 | + * | |
| 181 | + * Checks the current fence value and calculates the last | |
| 182 | + * signalled fence value. Returns true if activity occured | |
| 183 | + * on the ring, and the fence_queue should be waken up. | |
| 184 | + */ | |
| 185 | +static bool amdgpu_fence_activity(struct amdgpu_ring *ring) | |
| 186 | +{ | |
| 187 | + uint64_t seq, last_seq, last_emitted; | |
| 188 | + unsigned count_loop = 0; | |
| 189 | + bool wake = false; | |
| 190 | + | |
| 191 | + /* Note there is a scenario here for an infinite loop but it's | |
| 192 | + * very unlikely to happen. For it to happen, the current polling | |
| 193 | + * process need to be interrupted by another process and another | |
| 194 | + * process needs to update the last_seq btw the atomic read and | |
| 195 | + * xchg of the current process. | |
| 196 | + * | |
| 197 | + * More over for this to go in infinite loop there need to be | |
| 198 | + * continuously new fence signaled ie amdgpu_fence_read needs | |
| 199 | + * to return a different value each time for both the currently | |
| 200 | + * polling process and the other process that xchg the last_seq | |
| 201 | + * btw atomic read and xchg of the current process. And the | |
| 202 | + * value the other process set as last seq must be higher than | |
| 203 | + * the seq value we just read. Which means that current process | |
| 204 | + * need to be interrupted after amdgpu_fence_read and before | |
| 205 | + * atomic xchg. | |
| 206 | + * | |
| 207 | + * To be even more safe we count the number of time we loop and | |
| 208 | + * we bail after 10 loop just accepting the fact that we might | |
| 209 | + * have temporarly set the last_seq not to the true real last | |
| 210 | + * seq but to an older one. | |
| 211 | + */ | |
| 212 | + last_seq = atomic64_read(&ring->fence_drv.last_seq); | |
| 213 | + do { | |
| 214 | + last_emitted = ring->fence_drv.sync_seq[ring->idx]; | |
| 215 | + seq = amdgpu_fence_read(ring); | |
| 216 | + seq |= last_seq & 0xffffffff00000000LL; | |
| 217 | + if (seq < last_seq) { | |
| 218 | + seq &= 0xffffffff; | |
| 219 | + seq |= last_emitted & 0xffffffff00000000LL; | |
| 220 | + } | |
| 221 | + | |
| 222 | + if (seq <= last_seq || seq > last_emitted) { | |
| 223 | + break; | |
| 224 | + } | |
| 225 | + /* If we loop over we don't want to return without | |
| 226 | + * checking if a fence is signaled as it means that the | |
| 227 | + * seq we just read is different from the previous on. | |
| 228 | + */ | |
| 229 | + wake = true; | |
| 230 | + last_seq = seq; | |
| 231 | + if ((count_loop++) > 10) { | |
| 232 | + /* We looped over too many time leave with the | |
| 233 | + * fact that we might have set an older fence | |
| 234 | + * seq then the current real last seq as signaled | |
| 235 | + * by the hw. | |
| 236 | + */ | |
| 237 | + break; | |
| 238 | + } | |
| 239 | + } while (atomic64_xchg(&ring->fence_drv.last_seq, seq) > seq); | |
| 240 | + | |
| 241 | + if (seq < last_emitted) | |
| 242 | + amdgpu_fence_schedule_check(ring); | |
| 243 | + | |
| 244 | + return wake; | |
| 245 | +} | |
| 246 | + | |
| 247 | +/** | |
| 248 | + * amdgpu_fence_check_lockup - check for hardware lockup | |
| 249 | + * | |
| 250 | + * @work: delayed work item | |
| 251 | + * | |
| 252 | + * Checks for fence activity and if there is none probe | |
| 253 | + * the hardware if a lockup occured. | |
| 254 | + */ | |
| 255 | +static void amdgpu_fence_check_lockup(struct work_struct *work) | |
| 256 | +{ | |
| 257 | + struct amdgpu_fence_driver *fence_drv; | |
| 258 | + struct amdgpu_ring *ring; | |
| 259 | + | |
| 260 | + fence_drv = container_of(work, struct amdgpu_fence_driver, | |
| 261 | + lockup_work.work); | |
| 262 | + ring = fence_drv->ring; | |
| 263 | + | |
| 264 | + if (!down_read_trylock(&ring->adev->exclusive_lock)) { | |
| 265 | + /* just reschedule the check if a reset is going on */ | |
| 266 | + amdgpu_fence_schedule_check(ring); | |
| 267 | + return; | |
| 268 | + } | |
| 269 | + | |
| 270 | + if (fence_drv->delayed_irq && ring->adev->ddev->irq_enabled) { | |
| 271 | + fence_drv->delayed_irq = false; | |
| 272 | + amdgpu_irq_update(ring->adev, fence_drv->irq_src, | |
| 273 | + fence_drv->irq_type); | |
| 274 | + } | |
| 275 | + | |
| 276 | + if (amdgpu_fence_activity(ring)) | |
| 277 | + wake_up_all(&ring->adev->fence_queue); | |
| 278 | + else if (amdgpu_ring_is_lockup(ring)) { | |
| 279 | + /* good news we believe it's a lockup */ | |
| 280 | + dev_warn(ring->adev->dev, "GPU lockup (current fence id " | |
| 281 | + "0x%016llx last fence id 0x%016llx on ring %d)\n", | |
| 282 | + (uint64_t)atomic64_read(&fence_drv->last_seq), | |
| 283 | + fence_drv->sync_seq[ring->idx], ring->idx); | |
| 284 | + | |
| 285 | + /* remember that we need an reset */ | |
| 286 | + ring->adev->needs_reset = true; | |
| 287 | + wake_up_all(&ring->adev->fence_queue); | |
| 288 | + } | |
| 289 | + up_read(&ring->adev->exclusive_lock); | |
| 290 | +} | |
| 291 | + | |
| 292 | +/** | |
| 293 | + * amdgpu_fence_process - process a fence | |
| 294 | + * | |
| 295 | + * @adev: amdgpu_device pointer | |
| 296 | + * @ring: ring index the fence is associated with | |
| 297 | + * | |
| 298 | + * Checks the current fence value and wakes the fence queue | |
| 299 | + * if the sequence number has increased (all asics). | |
| 300 | + */ | |
| 301 | +void amdgpu_fence_process(struct amdgpu_ring *ring) | |
| 302 | +{ | |
| 303 | + uint64_t seq, last_seq, last_emitted; | |
| 304 | + unsigned count_loop = 0; | |
| 305 | + bool wake = false; | |
| 306 | + | |
| 307 | + /* Note there is a scenario here for an infinite loop but it's | |
| 308 | + * very unlikely to happen. For it to happen, the current polling | |
| 309 | + * process need to be interrupted by another process and another | |
| 310 | + * process needs to update the last_seq btw the atomic read and | |
| 311 | + * xchg of the current process. | |
| 312 | + * | |
| 313 | + * More over for this to go in infinite loop there need to be | |
| 314 | + * continuously new fence signaled ie amdgpu_fence_read needs | |
| 315 | + * to return a different value each time for both the currently | |
| 316 | + * polling process and the other process that xchg the last_seq | |
| 317 | + * btw atomic read and xchg of the current process. And the | |
| 318 | + * value the other process set as last seq must be higher than | |
| 319 | + * the seq value we just read. Which means that current process | |
| 320 | + * need to be interrupted after amdgpu_fence_read and before | |
| 321 | + * atomic xchg. | |
| 322 | + * | |
| 323 | + * To be even more safe we count the number of time we loop and | |
| 324 | + * we bail after 10 loop just accepting the fact that we might | |
| 325 | + * have temporarly set the last_seq not to the true real last | |
| 326 | + * seq but to an older one. | |
| 327 | + */ | |
| 328 | + last_seq = atomic64_read(&ring->fence_drv.last_seq); | |
| 329 | + do { | |
| 330 | + last_emitted = ring->fence_drv.sync_seq[ring->idx]; | |
| 331 | + seq = amdgpu_fence_read(ring); | |
| 332 | + seq |= last_seq & 0xffffffff00000000LL; | |
| 333 | + if (seq < last_seq) { | |
| 334 | + seq &= 0xffffffff; | |
| 335 | + seq |= last_emitted & 0xffffffff00000000LL; | |
| 336 | + } | |
| 337 | + | |
| 338 | + if (seq <= last_seq || seq > last_emitted) { | |
| 339 | + break; | |
| 340 | + } | |
| 341 | + /* If we loop over we don't want to return without | |
| 342 | + * checking if a fence is signaled as it means that the | |
| 343 | + * seq we just read is different from the previous on. | |
| 344 | + */ | |
| 345 | + wake = true; | |
| 346 | + last_seq = seq; | |
| 347 | + if ((count_loop++) > 10) { | |
| 348 | + /* We looped over too many time leave with the | |
| 349 | + * fact that we might have set an older fence | |
| 350 | + * seq then the current real last seq as signaled | |
| 351 | + * by the hw. | |
| 352 | + */ | |
| 353 | + break; | |
| 354 | + } | |
| 355 | + } while (atomic64_xchg(&ring->fence_drv.last_seq, seq) > seq); | |
| 356 | + | |
| 357 | + if (wake) | |
| 358 | + wake_up_all(&ring->adev->fence_queue); | |
| 359 | +} | |
| 360 | + | |
| 361 | +/** | |
| 362 | + * amdgpu_fence_seq_signaled - check if a fence sequence number has signaled | |
| 363 | + * | |
| 364 | + * @ring: ring the fence is associated with | |
| 365 | + * @seq: sequence number | |
| 366 | + * | |
| 367 | + * Check if the last signaled fence sequnce number is >= the requested | |
| 368 | + * sequence number (all asics). | |
| 369 | + * Returns true if the fence has signaled (current fence value | |
| 370 | + * is >= requested value) or false if it has not (current fence | |
| 371 | + * value is < the requested value. Helper function for | |
| 372 | + * amdgpu_fence_signaled(). | |
| 373 | + */ | |
| 374 | +static bool amdgpu_fence_seq_signaled(struct amdgpu_ring *ring, u64 seq) | |
| 375 | +{ | |
| 376 | + if (atomic64_read(&ring->fence_drv.last_seq) >= seq) | |
| 377 | + return true; | |
| 378 | + | |
| 379 | + /* poll new last sequence at least once */ | |
| 380 | + amdgpu_fence_process(ring); | |
| 381 | + if (atomic64_read(&ring->fence_drv.last_seq) >= seq) | |
| 382 | + return true; | |
| 383 | + | |
| 384 | + return false; | |
| 385 | +} | |
| 386 | + | |
| 387 | +static bool amdgpu_fence_is_signaled(struct fence *f) | |
| 388 | +{ | |
| 389 | + struct amdgpu_fence *fence = to_amdgpu_fence(f); | |
| 390 | + struct amdgpu_ring *ring = fence->ring; | |
| 391 | + struct amdgpu_device *adev = ring->adev; | |
| 392 | + | |
| 393 | + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) | |
| 394 | + return true; | |
| 395 | + | |
| 396 | + if (down_read_trylock(&adev->exclusive_lock)) { | |
| 397 | + amdgpu_fence_process(ring); | |
| 398 | + up_read(&adev->exclusive_lock); | |
| 399 | + | |
| 400 | + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) | |
| 401 | + return true; | |
| 402 | + } | |
| 403 | + return false; | |
| 404 | +} | |
| 405 | + | |
| 406 | +/** | |
| 407 | + * amdgpu_fence_enable_signaling - enable signalling on fence | |
| 408 | + * @fence: fence | |
| 409 | + * | |
| 410 | + * This function is called with fence_queue lock held, and adds a callback | |
| 411 | + * to fence_queue that checks if this fence is signaled, and if so it | |
| 412 | + * signals the fence and removes itself. | |
| 413 | + */ | |
| 414 | +static bool amdgpu_fence_enable_signaling(struct fence *f) | |
| 415 | +{ | |
| 416 | + struct amdgpu_fence *fence = to_amdgpu_fence(f); | |
| 417 | + struct amdgpu_ring *ring = fence->ring; | |
| 418 | + struct amdgpu_device *adev = ring->adev; | |
| 419 | + | |
| 420 | + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) | |
| 421 | + return false; | |
| 422 | + | |
| 423 | + if (down_read_trylock(&adev->exclusive_lock)) { | |
| 424 | + amdgpu_irq_get(adev, ring->fence_drv.irq_src, | |
| 425 | + ring->fence_drv.irq_type); | |
| 426 | + if (amdgpu_fence_activity(ring)) | |
| 427 | + wake_up_all_locked(&adev->fence_queue); | |
| 428 | + | |
| 429 | + /* did fence get signaled after we enabled the sw irq? */ | |
| 430 | + if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) { | |
| 431 | + amdgpu_irq_put(adev, ring->fence_drv.irq_src, | |
| 432 | + ring->fence_drv.irq_type); | |
| 433 | + up_read(&adev->exclusive_lock); | |
| 434 | + return false; | |
| 435 | + } | |
| 436 | + | |
| 437 | + up_read(&adev->exclusive_lock); | |
| 438 | + } else { | |
| 439 | + /* we're probably in a lockup, lets not fiddle too much */ | |
| 440 | + if (amdgpu_irq_get_delayed(adev, ring->fence_drv.irq_src, | |
| 441 | + ring->fence_drv.irq_type)) | |
| 442 | + ring->fence_drv.delayed_irq = true; | |
| 443 | + amdgpu_fence_schedule_check(ring); | |
| 444 | + } | |
| 445 | + | |
| 446 | + fence->fence_wake.flags = 0; | |
| 447 | + fence->fence_wake.private = NULL; | |
| 448 | + fence->fence_wake.func = amdgpu_fence_check_signaled; | |
| 449 | + __add_wait_queue(&adev->fence_queue, &fence->fence_wake); | |
| 450 | + fence_get(f); | |
| 451 | + FENCE_TRACE(&fence->base, "armed on ring %i!\n", ring->idx); | |
| 452 | + return true; | |
| 453 | +} | |
| 454 | + | |
| 455 | +/** | |
| 456 | + * amdgpu_fence_signaled - check if a fence has signaled | |
| 457 | + * | |
| 458 | + * @fence: amdgpu fence object | |
| 459 | + * | |
| 460 | + * Check if the requested fence has signaled (all asics). | |
| 461 | + * Returns true if the fence has signaled or false if it has not. | |
| 462 | + */ | |
| 463 | +bool amdgpu_fence_signaled(struct amdgpu_fence *fence) | |
| 464 | +{ | |
| 465 | + if (!fence) | |
| 466 | + return true; | |
| 467 | + | |
| 468 | + if (amdgpu_fence_seq_signaled(fence->ring, fence->seq)) { | |
| 469 | + if (!fence_signal(&fence->base)) | |
| 470 | + FENCE_TRACE(&fence->base, "signaled from amdgpu_fence_signaled\n"); | |
| 471 | + return true; | |
| 472 | + } | |
| 473 | + | |
| 474 | + return false; | |
| 475 | +} | |
| 476 | + | |
| 477 | +/** | |
| 478 | + * amdgpu_fence_any_seq_signaled - check if any sequence number is signaled | |
| 479 | + * | |
| 480 | + * @adev: amdgpu device pointer | |
| 481 | + * @seq: sequence numbers | |
| 482 | + * | |
| 483 | + * Check if the last signaled fence sequnce number is >= the requested | |
| 484 | + * sequence number (all asics). | |
| 485 | + * Returns true if any has signaled (current value is >= requested value) | |
| 486 | + * or false if it has not. Helper function for amdgpu_fence_wait_seq. | |
| 487 | + */ | |
| 488 | +static bool amdgpu_fence_any_seq_signaled(struct amdgpu_device *adev, u64 *seq) | |
| 489 | +{ | |
| 490 | + unsigned i; | |
| 491 | + | |
| 492 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 493 | + if (!adev->rings[i] || !seq[i]) | |
| 494 | + continue; | |
| 495 | + | |
| 496 | + if (amdgpu_fence_seq_signaled(adev->rings[i], seq[i])) | |
| 497 | + return true; | |
| 498 | + } | |
| 499 | + | |
| 500 | + return false; | |
| 501 | +} | |
| 502 | + | |
| 503 | +/** | |
| 504 | + * amdgpu_fence_wait_seq_timeout - wait for a specific sequence numbers | |
| 505 | + * | |
| 506 | + * @adev: amdgpu device pointer | |
| 507 | + * @target_seq: sequence number(s) we want to wait for | |
| 508 | + * @intr: use interruptable sleep | |
| 509 | + * @timeout: maximum time to wait, or MAX_SCHEDULE_TIMEOUT for infinite wait | |
| 510 | + * | |
| 511 | + * Wait for the requested sequence number(s) to be written by any ring | |
| 512 | + * (all asics). Sequnce number array is indexed by ring id. | |
| 513 | + * @intr selects whether to use interruptable (true) or non-interruptable | |
| 514 | + * (false) sleep when waiting for the sequence number. Helper function | |
| 515 | + * for amdgpu_fence_wait_*(). | |
| 516 | + * Returns remaining time if the sequence number has passed, 0 when | |
| 517 | + * the wait timeout, or an error for all other cases. | |
| 518 | + * -EDEADLK is returned when a GPU lockup has been detected. | |
| 519 | + */ | |
| 520 | +long amdgpu_fence_wait_seq_timeout(struct amdgpu_device *adev, u64 *target_seq, | |
| 521 | + bool intr, long timeout) | |
| 522 | +{ | |
| 523 | + uint64_t last_seq[AMDGPU_MAX_RINGS]; | |
| 524 | + bool signaled; | |
| 525 | + int i, r; | |
| 526 | + | |
| 527 | + if (timeout == 0) { | |
| 528 | + return amdgpu_fence_any_seq_signaled(adev, target_seq); | |
| 529 | + } | |
| 530 | + | |
| 531 | + while (!amdgpu_fence_any_seq_signaled(adev, target_seq)) { | |
| 532 | + | |
| 533 | + /* Save current sequence values, used to check for GPU lockups */ | |
| 534 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 535 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 536 | + | |
| 537 | + if (!ring || !target_seq[i]) | |
| 538 | + continue; | |
| 539 | + | |
| 540 | + last_seq[i] = atomic64_read(&ring->fence_drv.last_seq); | |
| 541 | + trace_amdgpu_fence_wait_begin(adev->ddev, i, target_seq[i]); | |
| 542 | + amdgpu_irq_get(adev, ring->fence_drv.irq_src, | |
| 543 | + ring->fence_drv.irq_type); | |
| 544 | + } | |
| 545 | + | |
| 546 | + if (intr) { | |
| 547 | + r = wait_event_interruptible_timeout(adev->fence_queue, ( | |
| 548 | + (signaled = amdgpu_fence_any_seq_signaled(adev, target_seq)) | |
| 549 | + || adev->needs_reset), AMDGPU_FENCE_JIFFIES_TIMEOUT); | |
| 550 | + } else { | |
| 551 | + r = wait_event_timeout(adev->fence_queue, ( | |
| 552 | + (signaled = amdgpu_fence_any_seq_signaled(adev, target_seq)) | |
| 553 | + || adev->needs_reset), AMDGPU_FENCE_JIFFIES_TIMEOUT); | |
| 554 | + } | |
| 555 | + | |
| 556 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 557 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 558 | + | |
| 559 | + if (!ring || !target_seq[i]) | |
| 560 | + continue; | |
| 561 | + | |
| 562 | + amdgpu_irq_put(adev, ring->fence_drv.irq_src, | |
| 563 | + ring->fence_drv.irq_type); | |
| 564 | + trace_amdgpu_fence_wait_end(adev->ddev, i, target_seq[i]); | |
| 565 | + } | |
| 566 | + | |
| 567 | + if (unlikely(r < 0)) | |
| 568 | + return r; | |
| 569 | + | |
| 570 | + if (unlikely(!signaled)) { | |
| 571 | + | |
| 572 | + if (adev->needs_reset) | |
| 573 | + return -EDEADLK; | |
| 574 | + | |
| 575 | + /* we were interrupted for some reason and fence | |
| 576 | + * isn't signaled yet, resume waiting */ | |
| 577 | + if (r) | |
| 578 | + continue; | |
| 579 | + | |
| 580 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 581 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 582 | + | |
| 583 | + if (!ring || !target_seq[i]) | |
| 584 | + continue; | |
| 585 | + | |
| 586 | + if (last_seq[i] != atomic64_read(&ring->fence_drv.last_seq)) | |
| 587 | + break; | |
| 588 | + } | |
| 589 | + | |
| 590 | + if (i != AMDGPU_MAX_RINGS) | |
| 591 | + continue; | |
| 592 | + | |
| 593 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 594 | + if (!adev->rings[i] || !target_seq[i]) | |
| 595 | + continue; | |
| 596 | + | |
| 597 | + if (amdgpu_ring_is_lockup(adev->rings[i])) | |
| 598 | + break; | |
| 599 | + } | |
| 600 | + | |
| 601 | + if (i < AMDGPU_MAX_RINGS) { | |
| 602 | + /* good news we believe it's a lockup */ | |
| 603 | + dev_warn(adev->dev, "GPU lockup (waiting for " | |
| 604 | + "0x%016llx last fence id 0x%016llx on" | |
| 605 | + " ring %d)\n", | |
| 606 | + target_seq[i], last_seq[i], i); | |
| 607 | + | |
| 608 | + /* remember that we need an reset */ | |
| 609 | + adev->needs_reset = true; | |
| 610 | + wake_up_all(&adev->fence_queue); | |
| 611 | + return -EDEADLK; | |
| 612 | + } | |
| 613 | + | |
| 614 | + if (timeout < MAX_SCHEDULE_TIMEOUT) { | |
| 615 | + timeout -= AMDGPU_FENCE_JIFFIES_TIMEOUT; | |
| 616 | + if (timeout <= 0) { | |
| 617 | + return 0; | |
| 618 | + } | |
| 619 | + } | |
| 620 | + } | |
| 621 | + } | |
| 622 | + return timeout; | |
| 623 | +} | |
| 624 | + | |
| 625 | +/** | |
| 626 | + * amdgpu_fence_wait - wait for a fence to signal | |
| 627 | + * | |
| 628 | + * @fence: amdgpu fence object | |
| 629 | + * @intr: use interruptable sleep | |
| 630 | + * | |
| 631 | + * Wait for the requested fence to signal (all asics). | |
| 632 | + * @intr selects whether to use interruptable (true) or non-interruptable | |
| 633 | + * (false) sleep when waiting for the fence. | |
| 634 | + * Returns 0 if the fence has passed, error for all other cases. | |
| 635 | + */ | |
| 636 | +int amdgpu_fence_wait(struct amdgpu_fence *fence, bool intr) | |
| 637 | +{ | |
| 638 | + uint64_t seq[AMDGPU_MAX_RINGS] = {}; | |
| 639 | + long r; | |
| 640 | + | |
| 641 | + seq[fence->ring->idx] = fence->seq; | |
| 642 | + r = amdgpu_fence_wait_seq_timeout(fence->ring->adev, seq, intr, MAX_SCHEDULE_TIMEOUT); | |
| 643 | + if (r < 0) { | |
| 644 | + return r; | |
| 645 | + } | |
| 646 | + | |
| 647 | + r = fence_signal(&fence->base); | |
| 648 | + if (!r) | |
| 649 | + FENCE_TRACE(&fence->base, "signaled from fence_wait\n"); | |
| 650 | + return 0; | |
| 651 | +} | |
| 652 | + | |
| 653 | +/** | |
| 654 | + * amdgpu_fence_wait_any - wait for a fence to signal on any ring | |
| 655 | + * | |
| 656 | + * @adev: amdgpu device pointer | |
| 657 | + * @fences: amdgpu fence object(s) | |
| 658 | + * @intr: use interruptable sleep | |
| 659 | + * | |
| 660 | + * Wait for any requested fence to signal (all asics). Fence | |
| 661 | + * array is indexed by ring id. @intr selects whether to use | |
| 662 | + * interruptable (true) or non-interruptable (false) sleep when | |
| 663 | + * waiting for the fences. Used by the suballocator. | |
| 664 | + * Returns 0 if any fence has passed, error for all other cases. | |
| 665 | + */ | |
| 666 | +int amdgpu_fence_wait_any(struct amdgpu_device *adev, | |
| 667 | + struct amdgpu_fence **fences, | |
| 668 | + bool intr) | |
| 669 | +{ | |
| 670 | + uint64_t seq[AMDGPU_MAX_RINGS]; | |
| 671 | + unsigned i, num_rings = 0; | |
| 672 | + long r; | |
| 673 | + | |
| 674 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 675 | + seq[i] = 0; | |
| 676 | + | |
| 677 | + if (!fences[i]) { | |
| 678 | + continue; | |
| 679 | + } | |
| 680 | + | |
| 681 | + seq[i] = fences[i]->seq; | |
| 682 | + ++num_rings; | |
| 683 | + } | |
| 684 | + | |
| 685 | + /* nothing to wait for ? */ | |
| 686 | + if (num_rings == 0) | |
| 687 | + return -ENOENT; | |
| 688 | + | |
| 689 | + r = amdgpu_fence_wait_seq_timeout(adev, seq, intr, MAX_SCHEDULE_TIMEOUT); | |
| 690 | + if (r < 0) { | |
| 691 | + return r; | |
| 692 | + } | |
| 693 | + return 0; | |
| 694 | +} | |
| 695 | + | |
| 696 | +/** | |
| 697 | + * amdgpu_fence_wait_next - wait for the next fence to signal | |
| 698 | + * | |
| 699 | + * @adev: amdgpu device pointer | |
| 700 | + * @ring: ring index the fence is associated with | |
| 701 | + * | |
| 702 | + * Wait for the next fence on the requested ring to signal (all asics). | |
| 703 | + * Returns 0 if the next fence has passed, error for all other cases. | |
| 704 | + * Caller must hold ring lock. | |
| 705 | + */ | |
| 706 | +int amdgpu_fence_wait_next(struct amdgpu_ring *ring) | |
| 707 | +{ | |
| 708 | + uint64_t seq[AMDGPU_MAX_RINGS] = {}; | |
| 709 | + long r; | |
| 710 | + | |
| 711 | + seq[ring->idx] = atomic64_read(&ring->fence_drv.last_seq) + 1ULL; | |
| 712 | + if (seq[ring->idx] >= ring->fence_drv.sync_seq[ring->idx]) { | |
| 713 | + /* nothing to wait for, last_seq is | |
| 714 | + already the last emited fence */ | |
| 715 | + return -ENOENT; | |
| 716 | + } | |
| 717 | + r = amdgpu_fence_wait_seq_timeout(ring->adev, seq, false, MAX_SCHEDULE_TIMEOUT); | |
| 718 | + if (r < 0) | |
| 719 | + return r; | |
| 720 | + return 0; | |
| 721 | +} | |
| 722 | + | |
| 723 | +/** | |
| 724 | + * amdgpu_fence_wait_empty - wait for all fences to signal | |
| 725 | + * | |
| 726 | + * @adev: amdgpu device pointer | |
| 727 | + * @ring: ring index the fence is associated with | |
| 728 | + * | |
| 729 | + * Wait for all fences on the requested ring to signal (all asics). | |
| 730 | + * Returns 0 if the fences have passed, error for all other cases. | |
| 731 | + * Caller must hold ring lock. | |
| 732 | + */ | |
| 733 | +int amdgpu_fence_wait_empty(struct amdgpu_ring *ring) | |
| 734 | +{ | |
| 735 | + struct amdgpu_device *adev = ring->adev; | |
| 736 | + uint64_t seq[AMDGPU_MAX_RINGS] = {}; | |
| 737 | + long r; | |
| 738 | + | |
| 739 | + seq[ring->idx] = ring->fence_drv.sync_seq[ring->idx]; | |
| 740 | + if (!seq[ring->idx]) | |
| 741 | + return 0; | |
| 742 | + | |
| 743 | + r = amdgpu_fence_wait_seq_timeout(adev, seq, false, MAX_SCHEDULE_TIMEOUT); | |
| 744 | + if (r < 0) { | |
| 745 | + if (r == -EDEADLK) | |
| 746 | + return -EDEADLK; | |
| 747 | + | |
| 748 | + dev_err(adev->dev, "error waiting for ring[%d] to become idle (%ld)\n", | |
| 749 | + ring->idx, r); | |
| 750 | + } | |
| 751 | + return 0; | |
| 752 | +} | |
| 753 | + | |
| 754 | +/** | |
| 755 | + * amdgpu_fence_ref - take a ref on a fence | |
| 756 | + * | |
| 757 | + * @fence: amdgpu fence object | |
| 758 | + * | |
| 759 | + * Take a reference on a fence (all asics). | |
| 760 | + * Returns the fence. | |
| 761 | + */ | |
| 762 | +struct amdgpu_fence *amdgpu_fence_ref(struct amdgpu_fence *fence) | |
| 763 | +{ | |
| 764 | + fence_get(&fence->base); | |
| 765 | + return fence; | |
| 766 | +} | |
| 767 | + | |
| 768 | +/** | |
| 769 | + * amdgpu_fence_unref - remove a ref on a fence | |
| 770 | + * | |
| 771 | + * @fence: amdgpu fence object | |
| 772 | + * | |
| 773 | + * Remove a reference on a fence (all asics). | |
| 774 | + */ | |
| 775 | +void amdgpu_fence_unref(struct amdgpu_fence **fence) | |
| 776 | +{ | |
| 777 | + struct amdgpu_fence *tmp = *fence; | |
| 778 | + | |
| 779 | + *fence = NULL; | |
| 780 | + if (tmp) | |
| 781 | + fence_put(&tmp->base); | |
| 782 | +} | |
| 783 | + | |
| 784 | +/** | |
| 785 | + * amdgpu_fence_count_emitted - get the count of emitted fences | |
| 786 | + * | |
| 787 | + * @ring: ring the fence is associated with | |
| 788 | + * | |
| 789 | + * Get the number of fences emitted on the requested ring (all asics). | |
| 790 | + * Returns the number of emitted fences on the ring. Used by the | |
| 791 | + * dynpm code to ring track activity. | |
| 792 | + */ | |
| 793 | +unsigned amdgpu_fence_count_emitted(struct amdgpu_ring *ring) | |
| 794 | +{ | |
| 795 | + uint64_t emitted; | |
| 796 | + | |
| 797 | + /* We are not protected by ring lock when reading the last sequence | |
| 798 | + * but it's ok to report slightly wrong fence count here. | |
| 799 | + */ | |
| 800 | + amdgpu_fence_process(ring); | |
| 801 | + emitted = ring->fence_drv.sync_seq[ring->idx] | |
| 802 | + - atomic64_read(&ring->fence_drv.last_seq); | |
| 803 | + /* to avoid 32bits warp around */ | |
| 804 | + if (emitted > 0x10000000) | |
| 805 | + emitted = 0x10000000; | |
| 806 | + | |
| 807 | + return (unsigned)emitted; | |
| 808 | +} | |
| 809 | + | |
| 810 | +/** | |
| 811 | + * amdgpu_fence_need_sync - do we need a semaphore | |
| 812 | + * | |
| 813 | + * @fence: amdgpu fence object | |
| 814 | + * @dst_ring: which ring to check against | |
| 815 | + * | |
| 816 | + * Check if the fence needs to be synced against another ring | |
| 817 | + * (all asics). If so, we need to emit a semaphore. | |
| 818 | + * Returns true if we need to sync with another ring, false if | |
| 819 | + * not. | |
| 820 | + */ | |
| 821 | +bool amdgpu_fence_need_sync(struct amdgpu_fence *fence, | |
| 822 | + struct amdgpu_ring *dst_ring) | |
| 823 | +{ | |
| 824 | + struct amdgpu_fence_driver *fdrv; | |
| 825 | + | |
| 826 | + if (!fence) | |
| 827 | + return false; | |
| 828 | + | |
| 829 | + if (fence->ring == dst_ring) | |
| 830 | + return false; | |
| 831 | + | |
| 832 | + /* we are protected by the ring mutex */ | |
| 833 | + fdrv = &dst_ring->fence_drv; | |
| 834 | + if (fence->seq <= fdrv->sync_seq[fence->ring->idx]) | |
| 835 | + return false; | |
| 836 | + | |
| 837 | + return true; | |
| 838 | +} | |
| 839 | + | |
| 840 | +/** | |
| 841 | + * amdgpu_fence_note_sync - record the sync point | |
| 842 | + * | |
| 843 | + * @fence: amdgpu fence object | |
| 844 | + * @dst_ring: which ring to check against | |
| 845 | + * | |
| 846 | + * Note the sequence number at which point the fence will | |
| 847 | + * be synced with the requested ring (all asics). | |
| 848 | + */ | |
| 849 | +void amdgpu_fence_note_sync(struct amdgpu_fence *fence, | |
| 850 | + struct amdgpu_ring *dst_ring) | |
| 851 | +{ | |
| 852 | + struct amdgpu_fence_driver *dst, *src; | |
| 853 | + unsigned i; | |
| 854 | + | |
| 855 | + if (!fence) | |
| 856 | + return; | |
| 857 | + | |
| 858 | + if (fence->ring == dst_ring) | |
| 859 | + return; | |
| 860 | + | |
| 861 | + /* we are protected by the ring mutex */ | |
| 862 | + src = &fence->ring->fence_drv; | |
| 863 | + dst = &dst_ring->fence_drv; | |
| 864 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 865 | + if (i == dst_ring->idx) | |
| 866 | + continue; | |
| 867 | + | |
| 868 | + dst->sync_seq[i] = max(dst->sync_seq[i], src->sync_seq[i]); | |
| 869 | + } | |
| 870 | +} | |
| 871 | + | |
| 872 | +/** | |
| 873 | + * amdgpu_fence_driver_start_ring - make the fence driver | |
| 874 | + * ready for use on the requested ring. | |
| 875 | + * | |
| 876 | + * @ring: ring to start the fence driver on | |
| 877 | + * @irq_src: interrupt source to use for this ring | |
| 878 | + * @irq_type: interrupt type to use for this ring | |
| 879 | + * | |
| 880 | + * Make the fence driver ready for processing (all asics). | |
| 881 | + * Not all asics have all rings, so each asic will only | |
| 882 | + * start the fence driver on the rings it has. | |
| 883 | + * Returns 0 for success, errors for failure. | |
| 884 | + */ | |
| 885 | +int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, | |
| 886 | + struct amdgpu_irq_src *irq_src, | |
| 887 | + unsigned irq_type) | |
| 888 | +{ | |
| 889 | + struct amdgpu_device *adev = ring->adev; | |
| 890 | + uint64_t index; | |
| 891 | + | |
| 892 | + if (ring != &adev->uvd.ring) { | |
| 893 | + ring->fence_drv.cpu_addr = &adev->wb.wb[ring->fence_offs]; | |
| 894 | + ring->fence_drv.gpu_addr = adev->wb.gpu_addr + (ring->fence_offs * 4); | |
| 895 | + } else { | |
| 896 | + /* put fence directly behind firmware */ | |
| 897 | + index = ALIGN(adev->uvd.fw->size, 8); | |
| 898 | + ring->fence_drv.cpu_addr = adev->uvd.cpu_addr + index; | |
| 899 | + ring->fence_drv.gpu_addr = adev->uvd.gpu_addr + index; | |
| 900 | + } | |
| 901 | + amdgpu_fence_write(ring, atomic64_read(&ring->fence_drv.last_seq)); | |
| 902 | + ring->fence_drv.initialized = true; | |
| 903 | + ring->fence_drv.irq_src = irq_src; | |
| 904 | + ring->fence_drv.irq_type = irq_type; | |
| 905 | + dev_info(adev->dev, "fence driver on ring %d use gpu addr 0x%016llx, " | |
| 906 | + "cpu addr 0x%p\n", ring->idx, | |
| 907 | + ring->fence_drv.gpu_addr, ring->fence_drv.cpu_addr); | |
| 908 | + return 0; | |
| 909 | +} | |
| 910 | + | |
| 911 | +/** | |
| 912 | + * amdgpu_fence_driver_init_ring - init the fence driver | |
| 913 | + * for the requested ring. | |
| 914 | + * | |
| 915 | + * @ring: ring to init the fence driver on | |
| 916 | + * | |
| 917 | + * Init the fence driver for the requested ring (all asics). | |
| 918 | + * Helper function for amdgpu_fence_driver_init(). | |
| 919 | + */ | |
| 920 | +void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) | |
| 921 | +{ | |
| 922 | + int i; | |
| 923 | + | |
| 924 | + ring->fence_drv.cpu_addr = NULL; | |
| 925 | + ring->fence_drv.gpu_addr = 0; | |
| 926 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) | |
| 927 | + ring->fence_drv.sync_seq[i] = 0; | |
| 928 | + | |
| 929 | + atomic64_set(&ring->fence_drv.last_seq, 0); | |
| 930 | + ring->fence_drv.initialized = false; | |
| 931 | + | |
| 932 | + INIT_DELAYED_WORK(&ring->fence_drv.lockup_work, | |
| 933 | + amdgpu_fence_check_lockup); | |
| 934 | + ring->fence_drv.ring = ring; | |
| 935 | +} | |
| 936 | + | |
| 937 | +/** | |
| 938 | + * amdgpu_fence_driver_init - init the fence driver | |
| 939 | + * for all possible rings. | |
| 940 | + * | |
| 941 | + * @adev: amdgpu device pointer | |
| 942 | + * | |
| 943 | + * Init the fence driver for all possible rings (all asics). | |
| 944 | + * Not all asics have all rings, so each asic will only | |
| 945 | + * start the fence driver on the rings it has using | |
| 946 | + * amdgpu_fence_driver_start_ring(). | |
| 947 | + * Returns 0 for success. | |
| 948 | + */ | |
| 949 | +int amdgpu_fence_driver_init(struct amdgpu_device *adev) | |
| 950 | +{ | |
| 951 | + init_waitqueue_head(&adev->fence_queue); | |
| 952 | + if (amdgpu_debugfs_fence_init(adev)) | |
| 953 | + dev_err(adev->dev, "fence debugfs file creation failed\n"); | |
| 954 | + | |
| 955 | + return 0; | |
| 956 | +} | |
| 957 | + | |
| 958 | +/** | |
| 959 | + * amdgpu_fence_driver_fini - tear down the fence driver | |
| 960 | + * for all possible rings. | |
| 961 | + * | |
| 962 | + * @adev: amdgpu device pointer | |
| 963 | + * | |
| 964 | + * Tear down the fence driver for all possible rings (all asics). | |
| 965 | + */ | |
| 966 | +void amdgpu_fence_driver_fini(struct amdgpu_device *adev) | |
| 967 | +{ | |
| 968 | + int i, r; | |
| 969 | + | |
| 970 | + mutex_lock(&adev->ring_lock); | |
| 971 | + for (i = 0; i < AMDGPU_MAX_RINGS; i++) { | |
| 972 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 973 | + if (!ring || !ring->fence_drv.initialized) | |
| 974 | + continue; | |
| 975 | + r = amdgpu_fence_wait_empty(ring); | |
| 976 | + if (r) { | |
| 977 | + /* no need to trigger GPU reset as we are unloading */ | |
| 978 | + amdgpu_fence_driver_force_completion(adev); | |
| 979 | + } | |
| 980 | + wake_up_all(&adev->fence_queue); | |
| 981 | + ring->fence_drv.initialized = false; | |
| 982 | + } | |
| 983 | + mutex_unlock(&adev->ring_lock); | |
| 984 | +} | |
| 985 | + | |
| 986 | +/** | |
| 987 | + * amdgpu_fence_driver_force_completion - force all fence waiter to complete | |
| 988 | + * | |
| 989 | + * @adev: amdgpu device pointer | |
| 990 | + * | |
| 991 | + * In case of GPU reset failure make sure no process keep waiting on fence | |
| 992 | + * that will never complete. | |
| 993 | + */ | |
| 994 | +void amdgpu_fence_driver_force_completion(struct amdgpu_device *adev) | |
| 995 | +{ | |
| 996 | + int i; | |
| 997 | + | |
| 998 | + for (i = 0; i < AMDGPU_MAX_RINGS; i++) { | |
| 999 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 1000 | + if (!ring || !ring->fence_drv.initialized) | |
| 1001 | + continue; | |
| 1002 | + | |
| 1003 | + amdgpu_fence_write(ring, ring->fence_drv.sync_seq[i]); | |
| 1004 | + } | |
| 1005 | +} | |
| 1006 | + | |
| 1007 | + | |
| 1008 | +/* | |
| 1009 | + * Fence debugfs | |
| 1010 | + */ | |
| 1011 | +#if defined(CONFIG_DEBUG_FS) | |
| 1012 | +static int amdgpu_debugfs_fence_info(struct seq_file *m, void *data) | |
| 1013 | +{ | |
| 1014 | + struct drm_info_node *node = (struct drm_info_node *)m->private; | |
| 1015 | + struct drm_device *dev = node->minor->dev; | |
| 1016 | + struct amdgpu_device *adev = dev->dev_private; | |
| 1017 | + int i, j; | |
| 1018 | + | |
| 1019 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 1020 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 1021 | + if (!ring || !ring->fence_drv.initialized) | |
| 1022 | + continue; | |
| 1023 | + | |
| 1024 | + amdgpu_fence_process(ring); | |
| 1025 | + | |
| 1026 | + seq_printf(m, "--- ring %d ---\n", i); | |
| 1027 | + seq_printf(m, "Last signaled fence 0x%016llx\n", | |
| 1028 | + (unsigned long long)atomic64_read(&ring->fence_drv.last_seq)); | |
| 1029 | + seq_printf(m, "Last emitted 0x%016llx\n", | |
| 1030 | + ring->fence_drv.sync_seq[i]); | |
| 1031 | + | |
| 1032 | + for (j = 0; j < AMDGPU_MAX_RINGS; ++j) { | |
| 1033 | + struct amdgpu_ring *other = adev->rings[j]; | |
| 1034 | + if (i != j && other && other->fence_drv.initialized) | |
| 1035 | + seq_printf(m, "Last sync to ring %d 0x%016llx\n", | |
| 1036 | + j, ring->fence_drv.sync_seq[j]); | |
| 1037 | + } | |
| 1038 | + } | |
| 1039 | + return 0; | |
| 1040 | +} | |
| 1041 | + | |
| 1042 | +static struct drm_info_list amdgpu_debugfs_fence_list[] = { | |
| 1043 | + {"amdgpu_fence_info", &amdgpu_debugfs_fence_info, 0, NULL}, | |
| 1044 | +}; | |
| 1045 | +#endif | |
| 1046 | + | |
| 1047 | +int amdgpu_debugfs_fence_init(struct amdgpu_device *adev) | |
| 1048 | +{ | |
| 1049 | +#if defined(CONFIG_DEBUG_FS) | |
| 1050 | + return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_fence_list, 1); | |
| 1051 | +#else | |
| 1052 | + return 0; | |
| 1053 | +#endif | |
| 1054 | +} | |
| 1055 | + | |
| 1056 | +static const char *amdgpu_fence_get_driver_name(struct fence *fence) | |
| 1057 | +{ | |
| 1058 | + return "amdgpu"; | |
| 1059 | +} | |
| 1060 | + | |
| 1061 | +static const char *amdgpu_fence_get_timeline_name(struct fence *f) | |
| 1062 | +{ | |
| 1063 | + struct amdgpu_fence *fence = to_amdgpu_fence(f); | |
| 1064 | + return (const char *)fence->ring->name; | |
| 1065 | +} | |
| 1066 | + | |
| 1067 | +static inline bool amdgpu_test_signaled(struct amdgpu_fence *fence) | |
| 1068 | +{ | |
| 1069 | + return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); | |
| 1070 | +} | |
| 1071 | + | |
| 1072 | +struct amdgpu_wait_cb { | |
| 1073 | + struct fence_cb base; | |
| 1074 | + struct task_struct *task; | |
| 1075 | +}; | |
| 1076 | + | |
| 1077 | +static void amdgpu_fence_wait_cb(struct fence *fence, struct fence_cb *cb) | |
| 1078 | +{ | |
| 1079 | + struct amdgpu_wait_cb *wait = | |
| 1080 | + container_of(cb, struct amdgpu_wait_cb, base); | |
| 1081 | + wake_up_process(wait->task); | |
| 1082 | +} | |
| 1083 | + | |
| 1084 | +static signed long amdgpu_fence_default_wait(struct fence *f, bool intr, | |
| 1085 | + signed long t) | |
| 1086 | +{ | |
| 1087 | + struct amdgpu_fence *fence = to_amdgpu_fence(f); | |
| 1088 | + struct amdgpu_device *adev = fence->ring->adev; | |
| 1089 | + struct amdgpu_wait_cb cb; | |
| 1090 | + | |
| 1091 | + cb.task = current; | |
| 1092 | + | |
| 1093 | + if (fence_add_callback(f, &cb.base, amdgpu_fence_wait_cb)) | |
| 1094 | + return t; | |
| 1095 | + | |
| 1096 | + while (t > 0) { | |
| 1097 | + if (intr) | |
| 1098 | + set_current_state(TASK_INTERRUPTIBLE); | |
| 1099 | + else | |
| 1100 | + set_current_state(TASK_UNINTERRUPTIBLE); | |
| 1101 | + | |
| 1102 | + /* | |
| 1103 | + * amdgpu_test_signaled must be called after | |
| 1104 | + * set_current_state to prevent a race with wake_up_process | |
| 1105 | + */ | |
| 1106 | + if (amdgpu_test_signaled(fence)) | |
| 1107 | + break; | |
| 1108 | + | |
| 1109 | + if (adev->needs_reset) { | |
| 1110 | + t = -EDEADLK; | |
| 1111 | + break; | |
| 1112 | + } | |
| 1113 | + | |
| 1114 | + t = schedule_timeout(t); | |
| 1115 | + | |
| 1116 | + if (t > 0 && intr && signal_pending(current)) | |
| 1117 | + t = -ERESTARTSYS; | |
| 1118 | + } | |
| 1119 | + | |
| 1120 | + __set_current_state(TASK_RUNNING); | |
| 1121 | + fence_remove_callback(f, &cb.base); | |
| 1122 | + | |
| 1123 | + return t; | |
| 1124 | +} | |
| 1125 | + | |
| 1126 | +const struct fence_ops amdgpu_fence_ops = { | |
| 1127 | + .get_driver_name = amdgpu_fence_get_driver_name, | |
| 1128 | + .get_timeline_name = amdgpu_fence_get_timeline_name, | |
| 1129 | + .enable_signaling = amdgpu_fence_enable_signaling, | |
| 1130 | + .signaled = amdgpu_fence_is_signaled, | |
| 1131 | + .wait = amdgpu_fence_default_wait, | |
| 1132 | + .release = NULL, | |
| 1133 | +}; | |
| @@ -0,0 +1,371 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + */ | |
| 28 | +#include <drm/drmP.h> | |
| 29 | +#include <drm/amdgpu_drm.h> | |
| 30 | +#include "amdgpu.h" | |
| 31 | + | |
| 32 | +/* | |
| 33 | + * GART | |
| 34 | + * The GART (Graphics Aperture Remapping Table) is an aperture | |
| 35 | + * in the GPU's address space. System pages can be mapped into | |
| 36 | + * the aperture and look like contiguous pages from the GPU's | |
| 37 | + * perspective. A page table maps the pages in the aperture | |
| 38 | + * to the actual backing pages in system memory. | |
| 39 | + * | |
| 40 | + * Radeon GPUs support both an internal GART, as described above, | |
| 41 | + * and AGP. AGP works similarly, but the GART table is configured | |
| 42 | + * and maintained by the northbridge rather than the driver. | |
| 43 | + * Radeon hw has a separate AGP aperture that is programmed to | |
| 44 | + * point to the AGP aperture provided by the northbridge and the | |
| 45 | + * requests are passed through to the northbridge aperture. | |
| 46 | + * Both AGP and internal GART can be used at the same time, however | |
| 47 | + * that is not currently supported by the driver. | |
| 48 | + * | |
| 49 | + * This file handles the common internal GART management. | |
| 50 | + */ | |
| 51 | + | |
| 52 | +/* | |
| 53 | + * Common GART table functions. | |
| 54 | + */ | |
| 55 | +/** | |
| 56 | + * amdgpu_gart_table_ram_alloc - allocate system ram for gart page table | |
| 57 | + * | |
| 58 | + * @adev: amdgpu_device pointer | |
| 59 | + * | |
| 60 | + * Allocate system memory for GART page table | |
| 61 | + * (r1xx-r3xx, non-pcie r4xx, rs400). These asics require the | |
| 62 | + * gart table to be in system memory. | |
| 63 | + * Returns 0 for success, -ENOMEM for failure. | |
| 64 | + */ | |
| 65 | +int amdgpu_gart_table_ram_alloc(struct amdgpu_device *adev) | |
| 66 | +{ | |
| 67 | + void *ptr; | |
| 68 | + | |
| 69 | + ptr = pci_alloc_consistent(adev->pdev, adev->gart.table_size, | |
| 70 | + &adev->gart.table_addr); | |
| 71 | + if (ptr == NULL) { | |
| 72 | + return -ENOMEM; | |
| 73 | + } | |
| 74 | +#ifdef CONFIG_X86 | |
| 75 | + if (0) { | |
| 76 | + set_memory_uc((unsigned long)ptr, | |
| 77 | + adev->gart.table_size >> PAGE_SHIFT); | |
| 78 | + } | |
| 79 | +#endif | |
| 80 | + adev->gart.ptr = ptr; | |
| 81 | + memset((void *)adev->gart.ptr, 0, adev->gart.table_size); | |
| 82 | + return 0; | |
| 83 | +} | |
| 84 | + | |
| 85 | +/** | |
| 86 | + * amdgpu_gart_table_ram_free - free system ram for gart page table | |
| 87 | + * | |
| 88 | + * @adev: amdgpu_device pointer | |
| 89 | + * | |
| 90 | + * Free system memory for GART page table | |
| 91 | + * (r1xx-r3xx, non-pcie r4xx, rs400). These asics require the | |
| 92 | + * gart table to be in system memory. | |
| 93 | + */ | |
| 94 | +void amdgpu_gart_table_ram_free(struct amdgpu_device *adev) | |
| 95 | +{ | |
| 96 | + if (adev->gart.ptr == NULL) { | |
| 97 | + return; | |
| 98 | + } | |
| 99 | +#ifdef CONFIG_X86 | |
| 100 | + if (0) { | |
| 101 | + set_memory_wb((unsigned long)adev->gart.ptr, | |
| 102 | + adev->gart.table_size >> PAGE_SHIFT); | |
| 103 | + } | |
| 104 | +#endif | |
| 105 | + pci_free_consistent(adev->pdev, adev->gart.table_size, | |
| 106 | + (void *)adev->gart.ptr, | |
| 107 | + adev->gart.table_addr); | |
| 108 | + adev->gart.ptr = NULL; | |
| 109 | + adev->gart.table_addr = 0; | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** | |
| 113 | + * amdgpu_gart_table_vram_alloc - allocate vram for gart page table | |
| 114 | + * | |
| 115 | + * @adev: amdgpu_device pointer | |
| 116 | + * | |
| 117 | + * Allocate video memory for GART page table | |
| 118 | + * (pcie r4xx, r5xx+). These asics require the | |
| 119 | + * gart table to be in video memory. | |
| 120 | + * Returns 0 for success, error for failure. | |
| 121 | + */ | |
| 122 | +int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev) | |
| 123 | +{ | |
| 124 | + int r; | |
| 125 | + | |
| 126 | + if (adev->gart.robj == NULL) { | |
| 127 | + r = amdgpu_bo_create(adev, adev->gart.table_size, | |
| 128 | + PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, 0, | |
| 129 | + NULL, &adev->gart.robj); | |
| 130 | + if (r) { | |
| 131 | + return r; | |
| 132 | + } | |
| 133 | + } | |
| 134 | + return 0; | |
| 135 | +} | |
| 136 | + | |
| 137 | +/** | |
| 138 | + * amdgpu_gart_table_vram_pin - pin gart page table in vram | |
| 139 | + * | |
| 140 | + * @adev: amdgpu_device pointer | |
| 141 | + * | |
| 142 | + * Pin the GART page table in vram so it will not be moved | |
| 143 | + * by the memory manager (pcie r4xx, r5xx+). These asics require the | |
| 144 | + * gart table to be in video memory. | |
| 145 | + * Returns 0 for success, error for failure. | |
| 146 | + */ | |
| 147 | +int amdgpu_gart_table_vram_pin(struct amdgpu_device *adev) | |
| 148 | +{ | |
| 149 | + uint64_t gpu_addr; | |
| 150 | + int r; | |
| 151 | + | |
| 152 | + r = amdgpu_bo_reserve(adev->gart.robj, false); | |
| 153 | + if (unlikely(r != 0)) | |
| 154 | + return r; | |
| 155 | + r = amdgpu_bo_pin(adev->gart.robj, | |
| 156 | + AMDGPU_GEM_DOMAIN_VRAM, &gpu_addr); | |
| 157 | + if (r) { | |
| 158 | + amdgpu_bo_unreserve(adev->gart.robj); | |
| 159 | + return r; | |
| 160 | + } | |
| 161 | + r = amdgpu_bo_kmap(adev->gart.robj, &adev->gart.ptr); | |
| 162 | + if (r) | |
| 163 | + amdgpu_bo_unpin(adev->gart.robj); | |
| 164 | + amdgpu_bo_unreserve(adev->gart.robj); | |
| 165 | + adev->gart.table_addr = gpu_addr; | |
| 166 | + return r; | |
| 167 | +} | |
| 168 | + | |
| 169 | +/** | |
| 170 | + * amdgpu_gart_table_vram_unpin - unpin gart page table in vram | |
| 171 | + * | |
| 172 | + * @adev: amdgpu_device pointer | |
| 173 | + * | |
| 174 | + * Unpin the GART page table in vram (pcie r4xx, r5xx+). | |
| 175 | + * These asics require the gart table to be in video memory. | |
| 176 | + */ | |
| 177 | +void amdgpu_gart_table_vram_unpin(struct amdgpu_device *adev) | |
| 178 | +{ | |
| 179 | + int r; | |
| 180 | + | |
| 181 | + if (adev->gart.robj == NULL) { | |
| 182 | + return; | |
| 183 | + } | |
| 184 | + r = amdgpu_bo_reserve(adev->gart.robj, false); | |
| 185 | + if (likely(r == 0)) { | |
| 186 | + amdgpu_bo_kunmap(adev->gart.robj); | |
| 187 | + amdgpu_bo_unpin(adev->gart.robj); | |
| 188 | + amdgpu_bo_unreserve(adev->gart.robj); | |
| 189 | + adev->gart.ptr = NULL; | |
| 190 | + } | |
| 191 | +} | |
| 192 | + | |
| 193 | +/** | |
| 194 | + * amdgpu_gart_table_vram_free - free gart page table vram | |
| 195 | + * | |
| 196 | + * @adev: amdgpu_device pointer | |
| 197 | + * | |
| 198 | + * Free the video memory used for the GART page table | |
| 199 | + * (pcie r4xx, r5xx+). These asics require the gart table to | |
| 200 | + * be in video memory. | |
| 201 | + */ | |
| 202 | +void amdgpu_gart_table_vram_free(struct amdgpu_device *adev) | |
| 203 | +{ | |
| 204 | + if (adev->gart.robj == NULL) { | |
| 205 | + return; | |
| 206 | + } | |
| 207 | + amdgpu_bo_unref(&adev->gart.robj); | |
| 208 | +} | |
| 209 | + | |
| 210 | +/* | |
| 211 | + * Common gart functions. | |
| 212 | + */ | |
| 213 | +/** | |
| 214 | + * amdgpu_gart_unbind - unbind pages from the gart page table | |
| 215 | + * | |
| 216 | + * @adev: amdgpu_device pointer | |
| 217 | + * @offset: offset into the GPU's gart aperture | |
| 218 | + * @pages: number of pages to unbind | |
| 219 | + * | |
| 220 | + * Unbinds the requested pages from the gart page table and | |
| 221 | + * replaces them with the dummy page (all asics). | |
| 222 | + */ | |
| 223 | +void amdgpu_gart_unbind(struct amdgpu_device *adev, unsigned offset, | |
| 224 | + int pages) | |
| 225 | +{ | |
| 226 | + unsigned t; | |
| 227 | + unsigned p; | |
| 228 | + int i, j; | |
| 229 | + u64 page_base; | |
| 230 | + uint32_t flags = AMDGPU_PTE_SYSTEM; | |
| 231 | + | |
| 232 | + if (!adev->gart.ready) { | |
| 233 | + WARN(1, "trying to unbind memory from uninitialized GART !\n"); | |
| 234 | + return; | |
| 235 | + } | |
| 236 | + | |
| 237 | + t = offset / AMDGPU_GPU_PAGE_SIZE; | |
| 238 | + p = t / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); | |
| 239 | + for (i = 0; i < pages; i++, p++) { | |
| 240 | + if (adev->gart.pages[p]) { | |
| 241 | + adev->gart.pages[p] = NULL; | |
| 242 | + adev->gart.pages_addr[p] = adev->dummy_page.addr; | |
| 243 | + page_base = adev->gart.pages_addr[p]; | |
| 244 | + if (!adev->gart.ptr) | |
| 245 | + continue; | |
| 246 | + | |
| 247 | + for (j = 0; j < (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); j++, t++) { | |
| 248 | + amdgpu_gart_set_pte_pde(adev, adev->gart.ptr, | |
| 249 | + t, page_base, flags); | |
| 250 | + page_base += AMDGPU_GPU_PAGE_SIZE; | |
| 251 | + } | |
| 252 | + } | |
| 253 | + } | |
| 254 | + mb(); | |
| 255 | + amdgpu_gart_flush_gpu_tlb(adev, 0); | |
| 256 | +} | |
| 257 | + | |
| 258 | +/** | |
| 259 | + * amdgpu_gart_bind - bind pages into the gart page table | |
| 260 | + * | |
| 261 | + * @adev: amdgpu_device pointer | |
| 262 | + * @offset: offset into the GPU's gart aperture | |
| 263 | + * @pages: number of pages to bind | |
| 264 | + * @pagelist: pages to bind | |
| 265 | + * @dma_addr: DMA addresses of pages | |
| 266 | + * | |
| 267 | + * Binds the requested pages to the gart page table | |
| 268 | + * (all asics). | |
| 269 | + * Returns 0 for success, -EINVAL for failure. | |
| 270 | + */ | |
| 271 | +int amdgpu_gart_bind(struct amdgpu_device *adev, unsigned offset, | |
| 272 | + int pages, struct page **pagelist, dma_addr_t *dma_addr, | |
| 273 | + uint32_t flags) | |
| 274 | +{ | |
| 275 | + unsigned t; | |
| 276 | + unsigned p; | |
| 277 | + uint64_t page_base; | |
| 278 | + int i, j; | |
| 279 | + | |
| 280 | + if (!adev->gart.ready) { | |
| 281 | + WARN(1, "trying to bind memory to uninitialized GART !\n"); | |
| 282 | + return -EINVAL; | |
| 283 | + } | |
| 284 | + | |
| 285 | + t = offset / AMDGPU_GPU_PAGE_SIZE; | |
| 286 | + p = t / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); | |
| 287 | + | |
| 288 | + for (i = 0; i < pages; i++, p++) { | |
| 289 | + adev->gart.pages_addr[p] = dma_addr[i]; | |
| 290 | + adev->gart.pages[p] = pagelist[i]; | |
| 291 | + if (adev->gart.ptr) { | |
| 292 | + page_base = adev->gart.pages_addr[p]; | |
| 293 | + for (j = 0; j < (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); j++, t++) { | |
| 294 | + amdgpu_gart_set_pte_pde(adev, adev->gart.ptr, t, page_base, flags); | |
| 295 | + page_base += AMDGPU_GPU_PAGE_SIZE; | |
| 296 | + } | |
| 297 | + } | |
| 298 | + } | |
| 299 | + mb(); | |
| 300 | + amdgpu_gart_flush_gpu_tlb(adev, 0); | |
| 301 | + return 0; | |
| 302 | +} | |
| 303 | + | |
| 304 | +/** | |
| 305 | + * amdgpu_gart_init - init the driver info for managing the gart | |
| 306 | + * | |
| 307 | + * @adev: amdgpu_device pointer | |
| 308 | + * | |
| 309 | + * Allocate the dummy page and init the gart driver info (all asics). | |
| 310 | + * Returns 0 for success, error for failure. | |
| 311 | + */ | |
| 312 | +int amdgpu_gart_init(struct amdgpu_device *adev) | |
| 313 | +{ | |
| 314 | + int r, i; | |
| 315 | + | |
| 316 | + if (adev->gart.pages) { | |
| 317 | + return 0; | |
| 318 | + } | |
| 319 | + /* We need PAGE_SIZE >= AMDGPU_GPU_PAGE_SIZE */ | |
| 320 | + if (PAGE_SIZE < AMDGPU_GPU_PAGE_SIZE) { | |
| 321 | + DRM_ERROR("Page size is smaller than GPU page size!\n"); | |
| 322 | + return -EINVAL; | |
| 323 | + } | |
| 324 | + r = amdgpu_dummy_page_init(adev); | |
| 325 | + if (r) | |
| 326 | + return r; | |
| 327 | + /* Compute table size */ | |
| 328 | + adev->gart.num_cpu_pages = adev->mc.gtt_size / PAGE_SIZE; | |
| 329 | + adev->gart.num_gpu_pages = adev->mc.gtt_size / AMDGPU_GPU_PAGE_SIZE; | |
| 330 | + DRM_INFO("GART: num cpu pages %u, num gpu pages %u\n", | |
| 331 | + adev->gart.num_cpu_pages, adev->gart.num_gpu_pages); | |
| 332 | + /* Allocate pages table */ | |
| 333 | + adev->gart.pages = vzalloc(sizeof(void *) * adev->gart.num_cpu_pages); | |
| 334 | + if (adev->gart.pages == NULL) { | |
| 335 | + amdgpu_gart_fini(adev); | |
| 336 | + return -ENOMEM; | |
| 337 | + } | |
| 338 | + adev->gart.pages_addr = vzalloc(sizeof(dma_addr_t) * | |
| 339 | + adev->gart.num_cpu_pages); | |
| 340 | + if (adev->gart.pages_addr == NULL) { | |
| 341 | + amdgpu_gart_fini(adev); | |
| 342 | + return -ENOMEM; | |
| 343 | + } | |
| 344 | + /* set GART entry to point to the dummy page by default */ | |
| 345 | + for (i = 0; i < adev->gart.num_cpu_pages; i++) { | |
| 346 | + adev->gart.pages_addr[i] = adev->dummy_page.addr; | |
| 347 | + } | |
| 348 | + return 0; | |
| 349 | +} | |
| 350 | + | |
| 351 | +/** | |
| 352 | + * amdgpu_gart_fini - tear down the driver info for managing the gart | |
| 353 | + * | |
| 354 | + * @adev: amdgpu_device pointer | |
| 355 | + * | |
| 356 | + * Tear down the gart driver info and free the dummy page (all asics). | |
| 357 | + */ | |
| 358 | +void amdgpu_gart_fini(struct amdgpu_device *adev) | |
| 359 | +{ | |
| 360 | + if (adev->gart.pages && adev->gart.pages_addr && adev->gart.ready) { | |
| 361 | + /* unbind pages */ | |
| 362 | + amdgpu_gart_unbind(adev, 0, adev->gart.num_cpu_pages); | |
| 363 | + } | |
| 364 | + adev->gart.ready = false; | |
| 365 | + vfree(adev->gart.pages); | |
| 366 | + vfree(adev->gart.pages_addr); | |
| 367 | + adev->gart.pages = NULL; | |
| 368 | + adev->gart.pages_addr = NULL; | |
| 369 | + | |
| 370 | + amdgpu_dummy_page_fini(adev); | |
| 371 | +} | |
| @@ -0,0 +1,72 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#ifndef __AMDGPU_GDS_H__ | |
| 25 | +#define __AMDGPU_GDS_H__ | |
| 26 | + | |
| 27 | +/* Because TTM request that alloacted buffer should be PAGE_SIZE aligned, | |
| 28 | + * we should report GDS/GWS/OA size as PAGE_SIZE aligned | |
| 29 | + * */ | |
| 30 | +#define AMDGPU_GDS_SHIFT 2 | |
| 31 | +#define AMDGPU_GWS_SHIFT PAGE_SHIFT | |
| 32 | +#define AMDGPU_OA_SHIFT PAGE_SHIFT | |
| 33 | + | |
| 34 | +#define AMDGPU_PL_GDS TTM_PL_PRIV0 | |
| 35 | +#define AMDGPU_PL_GWS TTM_PL_PRIV1 | |
| 36 | +#define AMDGPU_PL_OA TTM_PL_PRIV2 | |
| 37 | + | |
| 38 | +#define AMDGPU_PL_FLAG_GDS TTM_PL_FLAG_PRIV0 | |
| 39 | +#define AMDGPU_PL_FLAG_GWS TTM_PL_FLAG_PRIV1 | |
| 40 | +#define AMDGPU_PL_FLAG_OA TTM_PL_FLAG_PRIV2 | |
| 41 | + | |
| 42 | +struct amdgpu_ring; | |
| 43 | +struct amdgpu_bo; | |
| 44 | + | |
| 45 | +struct amdgpu_gds_asic_info { | |
| 46 | + uint32_t total_size; | |
| 47 | + uint32_t gfx_partition_size; | |
| 48 | + uint32_t cs_partition_size; | |
| 49 | +}; | |
| 50 | + | |
| 51 | +struct amdgpu_gds { | |
| 52 | + struct amdgpu_gds_asic_info mem; | |
| 53 | + struct amdgpu_gds_asic_info gws; | |
| 54 | + struct amdgpu_gds_asic_info oa; | |
| 55 | + /* At present, GDS, GWS and OA resources for gfx (graphics) | |
| 56 | + * is always pre-allocated and available for graphics operation. | |
| 57 | + * Such resource is shared between all gfx clients. | |
| 58 | + * TODO: move this operation to user space | |
| 59 | + * */ | |
| 60 | + struct amdgpu_bo* gds_gfx_bo; | |
| 61 | + struct amdgpu_bo* gws_gfx_bo; | |
| 62 | + struct amdgpu_bo* oa_gfx_bo; | |
| 63 | +}; | |
| 64 | + | |
| 65 | +struct amdgpu_gds_reg_offset { | |
| 66 | + uint32_t mem_base; | |
| 67 | + uint32_t mem_size; | |
| 68 | + uint32_t gws; | |
| 69 | + uint32_t oa; | |
| 70 | +}; | |
| 71 | + | |
| 72 | +#endif /* __AMDGPU_GDS_H__ */ | |
| @@ -0,0 +1,716 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + */ | |
| 28 | +#include <linux/ktime.h> | |
| 29 | +#include <drm/drmP.h> | |
| 30 | +#include <drm/amdgpu_drm.h> | |
| 31 | +#include "amdgpu.h" | |
| 32 | + | |
| 33 | +void amdgpu_gem_object_free(struct drm_gem_object *gobj) | |
| 34 | +{ | |
| 35 | + struct amdgpu_bo *robj = gem_to_amdgpu_bo(gobj); | |
| 36 | + | |
| 37 | + if (robj) { | |
| 38 | + if (robj->gem_base.import_attach) | |
| 39 | + drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg); | |
| 40 | + amdgpu_mn_unregister(robj); | |
| 41 | + amdgpu_bo_unref(&robj); | |
| 42 | + } | |
| 43 | +} | |
| 44 | + | |
| 45 | +int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, | |
| 46 | + int alignment, u32 initial_domain, | |
| 47 | + u64 flags, bool kernel, | |
| 48 | + struct drm_gem_object **obj) | |
| 49 | +{ | |
| 50 | + struct amdgpu_bo *robj; | |
| 51 | + unsigned long max_size; | |
| 52 | + int r; | |
| 53 | + | |
| 54 | + *obj = NULL; | |
| 55 | + /* At least align on page size */ | |
| 56 | + if (alignment < PAGE_SIZE) { | |
| 57 | + alignment = PAGE_SIZE; | |
| 58 | + } | |
| 59 | + | |
| 60 | + if (!(initial_domain & (AMDGPU_GEM_DOMAIN_GDS | AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA))) { | |
| 61 | + /* Maximum bo size is the unpinned gtt size since we use the gtt to | |
| 62 | + * handle vram to system pool migrations. | |
| 63 | + */ | |
| 64 | + max_size = adev->mc.gtt_size - adev->gart_pin_size; | |
| 65 | + if (size > max_size) { | |
| 66 | + DRM_DEBUG("Allocation size %ldMb bigger than %ldMb limit\n", | |
| 67 | + size >> 20, max_size >> 20); | |
| 68 | + return -ENOMEM; | |
| 69 | + } | |
| 70 | + } | |
| 71 | +retry: | |
| 72 | + r = amdgpu_bo_create(adev, size, alignment, kernel, initial_domain, flags, NULL, &robj); | |
| 73 | + if (r) { | |
| 74 | + if (r != -ERESTARTSYS) { | |
| 75 | + if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) { | |
| 76 | + initial_domain |= AMDGPU_GEM_DOMAIN_GTT; | |
| 77 | + goto retry; | |
| 78 | + } | |
| 79 | + DRM_ERROR("Failed to allocate GEM object (%ld, %d, %u, %d)\n", | |
| 80 | + size, initial_domain, alignment, r); | |
| 81 | + } | |
| 82 | + return r; | |
| 83 | + } | |
| 84 | + *obj = &robj->gem_base; | |
| 85 | + robj->pid = task_pid_nr(current); | |
| 86 | + | |
| 87 | + mutex_lock(&adev->gem.mutex); | |
| 88 | + list_add_tail(&robj->list, &adev->gem.objects); | |
| 89 | + mutex_unlock(&adev->gem.mutex); | |
| 90 | + | |
| 91 | + return 0; | |
| 92 | +} | |
| 93 | + | |
| 94 | +int amdgpu_gem_init(struct amdgpu_device *adev) | |
| 95 | +{ | |
| 96 | + INIT_LIST_HEAD(&adev->gem.objects); | |
| 97 | + return 0; | |
| 98 | +} | |
| 99 | + | |
| 100 | +void amdgpu_gem_fini(struct amdgpu_device *adev) | |
| 101 | +{ | |
| 102 | + amdgpu_bo_force_delete(adev); | |
| 103 | +} | |
| 104 | + | |
| 105 | +/* | |
| 106 | + * Call from drm_gem_handle_create which appear in both new and open ioctl | |
| 107 | + * case. | |
| 108 | + */ | |
| 109 | +int amdgpu_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv) | |
| 110 | +{ | |
| 111 | + struct amdgpu_bo *rbo = gem_to_amdgpu_bo(obj); | |
| 112 | + struct amdgpu_device *adev = rbo->adev; | |
| 113 | + struct amdgpu_fpriv *fpriv = file_priv->driver_priv; | |
| 114 | + struct amdgpu_vm *vm = &fpriv->vm; | |
| 115 | + struct amdgpu_bo_va *bo_va; | |
| 116 | + int r; | |
| 117 | + | |
| 118 | + r = amdgpu_bo_reserve(rbo, false); | |
| 119 | + if (r) { | |
| 120 | + return r; | |
| 121 | + } | |
| 122 | + | |
| 123 | + bo_va = amdgpu_vm_bo_find(vm, rbo); | |
| 124 | + if (!bo_va) { | |
| 125 | + bo_va = amdgpu_vm_bo_add(adev, vm, rbo); | |
| 126 | + } else { | |
| 127 | + ++bo_va->ref_count; | |
| 128 | + } | |
| 129 | + amdgpu_bo_unreserve(rbo); | |
| 130 | + | |
| 131 | + return 0; | |
| 132 | +} | |
| 133 | + | |
| 134 | +void amdgpu_gem_object_close(struct drm_gem_object *obj, | |
| 135 | + struct drm_file *file_priv) | |
| 136 | +{ | |
| 137 | + struct amdgpu_bo *rbo = gem_to_amdgpu_bo(obj); | |
| 138 | + struct amdgpu_device *adev = rbo->adev; | |
| 139 | + struct amdgpu_fpriv *fpriv = file_priv->driver_priv; | |
| 140 | + struct amdgpu_vm *vm = &fpriv->vm; | |
| 141 | + struct amdgpu_bo_va *bo_va; | |
| 142 | + int r; | |
| 143 | + | |
| 144 | + r = amdgpu_bo_reserve(rbo, true); | |
| 145 | + if (r) { | |
| 146 | + dev_err(adev->dev, "leaking bo va because " | |
| 147 | + "we fail to reserve bo (%d)\n", r); | |
| 148 | + return; | |
| 149 | + } | |
| 150 | + bo_va = amdgpu_vm_bo_find(vm, rbo); | |
| 151 | + if (bo_va) { | |
| 152 | + if (--bo_va->ref_count == 0) { | |
| 153 | + amdgpu_vm_bo_rmv(adev, bo_va); | |
| 154 | + } | |
| 155 | + } | |
| 156 | + amdgpu_bo_unreserve(rbo); | |
| 157 | +} | |
| 158 | + | |
| 159 | +static int amdgpu_gem_handle_lockup(struct amdgpu_device *adev, int r) | |
| 160 | +{ | |
| 161 | + if (r == -EDEADLK) { | |
| 162 | + r = amdgpu_gpu_reset(adev); | |
| 163 | + if (!r) | |
| 164 | + r = -EAGAIN; | |
| 165 | + } | |
| 166 | + return r; | |
| 167 | +} | |
| 168 | + | |
| 169 | +/* | |
| 170 | + * GEM ioctls. | |
| 171 | + */ | |
| 172 | +int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, | |
| 173 | + struct drm_file *filp) | |
| 174 | +{ | |
| 175 | + struct amdgpu_device *adev = dev->dev_private; | |
| 176 | + union drm_amdgpu_gem_create *args = data; | |
| 177 | + uint64_t size = args->in.bo_size; | |
| 178 | + struct drm_gem_object *gobj; | |
| 179 | + uint32_t handle; | |
| 180 | + bool kernel = false; | |
| 181 | + int r; | |
| 182 | + | |
| 183 | + down_read(&adev->exclusive_lock); | |
| 184 | + /* create a gem object to contain this object in */ | |
| 185 | + if (args->in.domains & (AMDGPU_GEM_DOMAIN_GDS | | |
| 186 | + AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA)) { | |
| 187 | + kernel = true; | |
| 188 | + if (args->in.domains == AMDGPU_GEM_DOMAIN_GDS) | |
| 189 | + size = size << AMDGPU_GDS_SHIFT; | |
| 190 | + else if (args->in.domains == AMDGPU_GEM_DOMAIN_GWS) | |
| 191 | + size = size << AMDGPU_GWS_SHIFT; | |
| 192 | + else if (args->in.domains == AMDGPU_GEM_DOMAIN_OA) | |
| 193 | + size = size << AMDGPU_OA_SHIFT; | |
| 194 | + else { | |
| 195 | + r = -EINVAL; | |
| 196 | + goto error_unlock; | |
| 197 | + } | |
| 198 | + } | |
| 199 | + size = roundup(size, PAGE_SIZE); | |
| 200 | + | |
| 201 | + r = amdgpu_gem_object_create(adev, size, args->in.alignment, | |
| 202 | + (u32)(0xffffffff & args->in.domains), | |
| 203 | + args->in.domain_flags, | |
| 204 | + kernel, &gobj); | |
| 205 | + if (r) | |
| 206 | + goto error_unlock; | |
| 207 | + | |
| 208 | + r = drm_gem_handle_create(filp, gobj, &handle); | |
| 209 | + /* drop reference from allocate - handle holds it now */ | |
| 210 | + drm_gem_object_unreference_unlocked(gobj); | |
| 211 | + if (r) | |
| 212 | + goto error_unlock; | |
| 213 | + | |
| 214 | + memset(args, 0, sizeof(*args)); | |
| 215 | + args->out.handle = handle; | |
| 216 | + up_read(&adev->exclusive_lock); | |
| 217 | + return 0; | |
| 218 | + | |
| 219 | +error_unlock: | |
| 220 | + up_read(&adev->exclusive_lock); | |
| 221 | + r = amdgpu_gem_handle_lockup(adev, r); | |
| 222 | + return r; | |
| 223 | +} | |
| 224 | + | |
| 225 | +int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, | |
| 226 | + struct drm_file *filp) | |
| 227 | +{ | |
| 228 | + struct amdgpu_device *adev = dev->dev_private; | |
| 229 | + struct drm_amdgpu_gem_userptr *args = data; | |
| 230 | + struct drm_gem_object *gobj; | |
| 231 | + struct amdgpu_bo *bo; | |
| 232 | + uint32_t handle; | |
| 233 | + int r; | |
| 234 | + | |
| 235 | + if (offset_in_page(args->addr | args->size)) | |
| 236 | + return -EINVAL; | |
| 237 | + | |
| 238 | + /* reject unknown flag values */ | |
| 239 | + if (args->flags & ~(AMDGPU_GEM_USERPTR_READONLY | | |
| 240 | + AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_VALIDATE | | |
| 241 | + AMDGPU_GEM_USERPTR_REGISTER)) | |
| 242 | + return -EINVAL; | |
| 243 | + | |
| 244 | + if (!(args->flags & AMDGPU_GEM_USERPTR_ANONONLY) || | |
| 245 | + !(args->flags & AMDGPU_GEM_USERPTR_REGISTER)) { | |
| 246 | + | |
| 247 | + /* if we want to write to it we must require anonymous | |
| 248 | + memory and install a MMU notifier */ | |
| 249 | + return -EACCES; | |
| 250 | + } | |
| 251 | + | |
| 252 | + down_read(&adev->exclusive_lock); | |
| 253 | + | |
| 254 | + /* create a gem object to contain this object in */ | |
| 255 | + r = amdgpu_gem_object_create(adev, args->size, 0, | |
| 256 | + AMDGPU_GEM_DOMAIN_CPU, 0, | |
| 257 | + 0, &gobj); | |
| 258 | + if (r) | |
| 259 | + goto handle_lockup; | |
| 260 | + | |
| 261 | + bo = gem_to_amdgpu_bo(gobj); | |
| 262 | + r = amdgpu_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags); | |
| 263 | + if (r) | |
| 264 | + goto release_object; | |
| 265 | + | |
| 266 | + if (args->flags & AMDGPU_GEM_USERPTR_REGISTER) { | |
| 267 | + r = amdgpu_mn_register(bo, args->addr); | |
| 268 | + if (r) | |
| 269 | + goto release_object; | |
| 270 | + } | |
| 271 | + | |
| 272 | + if (args->flags & AMDGPU_GEM_USERPTR_VALIDATE) { | |
| 273 | + down_read(¤t->mm->mmap_sem); | |
| 274 | + r = amdgpu_bo_reserve(bo, true); | |
| 275 | + if (r) { | |
| 276 | + up_read(¤t->mm->mmap_sem); | |
| 277 | + goto release_object; | |
| 278 | + } | |
| 279 | + | |
| 280 | + amdgpu_ttm_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_GTT); | |
| 281 | + r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); | |
| 282 | + amdgpu_bo_unreserve(bo); | |
| 283 | + up_read(¤t->mm->mmap_sem); | |
| 284 | + if (r) | |
| 285 | + goto release_object; | |
| 286 | + } | |
| 287 | + | |
| 288 | + r = drm_gem_handle_create(filp, gobj, &handle); | |
| 289 | + /* drop reference from allocate - handle holds it now */ | |
| 290 | + drm_gem_object_unreference_unlocked(gobj); | |
| 291 | + if (r) | |
| 292 | + goto handle_lockup; | |
| 293 | + | |
| 294 | + args->handle = handle; | |
| 295 | + up_read(&adev->exclusive_lock); | |
| 296 | + return 0; | |
| 297 | + | |
| 298 | +release_object: | |
| 299 | + drm_gem_object_unreference_unlocked(gobj); | |
| 300 | + | |
| 301 | +handle_lockup: | |
| 302 | + up_read(&adev->exclusive_lock); | |
| 303 | + r = amdgpu_gem_handle_lockup(adev, r); | |
| 304 | + | |
| 305 | + return r; | |
| 306 | +} | |
| 307 | + | |
| 308 | +int amdgpu_mode_dumb_mmap(struct drm_file *filp, | |
| 309 | + struct drm_device *dev, | |
| 310 | + uint32_t handle, uint64_t *offset_p) | |
| 311 | +{ | |
| 312 | + struct drm_gem_object *gobj; | |
| 313 | + struct amdgpu_bo *robj; | |
| 314 | + | |
| 315 | + gobj = drm_gem_object_lookup(dev, filp, handle); | |
| 316 | + if (gobj == NULL) { | |
| 317 | + return -ENOENT; | |
| 318 | + } | |
| 319 | + robj = gem_to_amdgpu_bo(gobj); | |
| 320 | + if (amdgpu_ttm_tt_has_userptr(robj->tbo.ttm) || | |
| 321 | + (robj->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)) { | |
| 322 | + drm_gem_object_unreference_unlocked(gobj); | |
| 323 | + return -EPERM; | |
| 324 | + } | |
| 325 | + *offset_p = amdgpu_bo_mmap_offset(robj); | |
| 326 | + drm_gem_object_unreference_unlocked(gobj); | |
| 327 | + return 0; | |
| 328 | +} | |
| 329 | + | |
| 330 | +int amdgpu_gem_mmap_ioctl(struct drm_device *dev, void *data, | |
| 331 | + struct drm_file *filp) | |
| 332 | +{ | |
| 333 | + union drm_amdgpu_gem_mmap *args = data; | |
| 334 | + uint32_t handle = args->in.handle; | |
| 335 | + memset(args, 0, sizeof(*args)); | |
| 336 | + return amdgpu_mode_dumb_mmap(filp, dev, handle, &args->out.addr_ptr); | |
| 337 | +} | |
| 338 | + | |
| 339 | +/** | |
| 340 | + * amdgpu_gem_timeout - calculate jiffies timeout from absolute value | |
| 341 | + * | |
| 342 | + * @timeout_ns: timeout in ns | |
| 343 | + * | |
| 344 | + * Calculate the timeout in jiffies from an absolute timeout in ns. | |
| 345 | + */ | |
| 346 | +unsigned long amdgpu_gem_timeout(uint64_t timeout_ns) | |
| 347 | +{ | |
| 348 | + unsigned long timeout_jiffies; | |
| 349 | + ktime_t timeout; | |
| 350 | + | |
| 351 | + /* clamp timeout if it's to large */ | |
| 352 | + if (((int64_t)timeout_ns) < 0) | |
| 353 | + return MAX_SCHEDULE_TIMEOUT; | |
| 354 | + | |
| 355 | + timeout = ktime_sub_ns(ktime_get(), timeout_ns); | |
| 356 | + if (ktime_to_ns(timeout) < 0) | |
| 357 | + return 0; | |
| 358 | + | |
| 359 | + timeout_jiffies = nsecs_to_jiffies(ktime_to_ns(timeout)); | |
| 360 | + /* clamp timeout to avoid unsigned-> signed overflow */ | |
| 361 | + if (timeout_jiffies > MAX_SCHEDULE_TIMEOUT ) | |
| 362 | + return MAX_SCHEDULE_TIMEOUT - 1; | |
| 363 | + | |
| 364 | + return timeout_jiffies; | |
| 365 | +} | |
| 366 | + | |
| 367 | +int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data, | |
| 368 | + struct drm_file *filp) | |
| 369 | +{ | |
| 370 | + struct amdgpu_device *adev = dev->dev_private; | |
| 371 | + union drm_amdgpu_gem_wait_idle *args = data; | |
| 372 | + struct drm_gem_object *gobj; | |
| 373 | + struct amdgpu_bo *robj; | |
| 374 | + uint32_t handle = args->in.handle; | |
| 375 | + unsigned long timeout = amdgpu_gem_timeout(args->in.timeout); | |
| 376 | + int r = 0; | |
| 377 | + long ret; | |
| 378 | + | |
| 379 | + gobj = drm_gem_object_lookup(dev, filp, handle); | |
| 380 | + if (gobj == NULL) { | |
| 381 | + return -ENOENT; | |
| 382 | + } | |
| 383 | + robj = gem_to_amdgpu_bo(gobj); | |
| 384 | + if (timeout == 0) | |
| 385 | + ret = reservation_object_test_signaled_rcu(robj->tbo.resv, true); | |
| 386 | + else | |
| 387 | + ret = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, timeout); | |
| 388 | + | |
| 389 | + /* ret == 0 means not signaled, | |
| 390 | + * ret > 0 means signaled | |
| 391 | + * ret < 0 means interrupted before timeout | |
| 392 | + */ | |
| 393 | + if (ret >= 0) { | |
| 394 | + memset(args, 0, sizeof(*args)); | |
| 395 | + args->out.status = (ret == 0); | |
| 396 | + } else | |
| 397 | + r = ret; | |
| 398 | + | |
| 399 | + drm_gem_object_unreference_unlocked(gobj); | |
| 400 | + r = amdgpu_gem_handle_lockup(adev, r); | |
| 401 | + return r; | |
| 402 | +} | |
| 403 | + | |
| 404 | +int amdgpu_gem_metadata_ioctl(struct drm_device *dev, void *data, | |
| 405 | + struct drm_file *filp) | |
| 406 | +{ | |
| 407 | + struct drm_amdgpu_gem_metadata *args = data; | |
| 408 | + struct drm_gem_object *gobj; | |
| 409 | + struct amdgpu_bo *robj; | |
| 410 | + int r = -1; | |
| 411 | + | |
| 412 | + DRM_DEBUG("%d \n", args->handle); | |
| 413 | + gobj = drm_gem_object_lookup(dev, filp, args->handle); | |
| 414 | + if (gobj == NULL) | |
| 415 | + return -ENOENT; | |
| 416 | + robj = gem_to_amdgpu_bo(gobj); | |
| 417 | + | |
| 418 | + r = amdgpu_bo_reserve(robj, false); | |
| 419 | + if (unlikely(r != 0)) | |
| 420 | + goto out; | |
| 421 | + | |
| 422 | + if (args->op == AMDGPU_GEM_METADATA_OP_GET_METADATA) { | |
| 423 | + amdgpu_bo_get_tiling_flags(robj, &args->data.tiling_info); | |
| 424 | + r = amdgpu_bo_get_metadata(robj, args->data.data, | |
| 425 | + sizeof(args->data.data), | |
| 426 | + &args->data.data_size_bytes, | |
| 427 | + &args->data.flags); | |
| 428 | + } else if (args->op == AMDGPU_GEM_METADATA_OP_SET_METADATA) { | |
| 429 | + r = amdgpu_bo_set_tiling_flags(robj, args->data.tiling_info); | |
| 430 | + if (!r) | |
| 431 | + r = amdgpu_bo_set_metadata(robj, args->data.data, | |
| 432 | + args->data.data_size_bytes, | |
| 433 | + args->data.flags); | |
| 434 | + } | |
| 435 | + | |
| 436 | + amdgpu_bo_unreserve(robj); | |
| 437 | +out: | |
| 438 | + drm_gem_object_unreference_unlocked(gobj); | |
| 439 | + return r; | |
| 440 | +} | |
| 441 | + | |
| 442 | +/** | |
| 443 | + * amdgpu_gem_va_update_vm -update the bo_va in its VM | |
| 444 | + * | |
| 445 | + * @adev: amdgpu_device pointer | |
| 446 | + * @bo_va: bo_va to update | |
| 447 | + * | |
| 448 | + * Update the bo_va directly after setting it's address. Errors are not | |
| 449 | + * vital here, so they are not reported back to userspace. | |
| 450 | + */ | |
| 451 | +static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev, | |
| 452 | + struct amdgpu_bo_va *bo_va) | |
| 453 | +{ | |
| 454 | + struct ttm_validate_buffer tv, *entry; | |
| 455 | + struct amdgpu_bo_list_entry *vm_bos; | |
| 456 | + struct ww_acquire_ctx ticket; | |
| 457 | + struct list_head list; | |
| 458 | + unsigned domain; | |
| 459 | + int r; | |
| 460 | + | |
| 461 | + INIT_LIST_HEAD(&list); | |
| 462 | + | |
| 463 | + tv.bo = &bo_va->bo->tbo; | |
| 464 | + tv.shared = true; | |
| 465 | + list_add(&tv.head, &list); | |
| 466 | + | |
| 467 | + vm_bos = amdgpu_vm_get_bos(adev, bo_va->vm, &list); | |
| 468 | + if (!vm_bos) | |
| 469 | + return; | |
| 470 | + | |
| 471 | + r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); | |
| 472 | + if (r) | |
| 473 | + goto error_free; | |
| 474 | + | |
| 475 | + list_for_each_entry(entry, &list, head) { | |
| 476 | + domain = amdgpu_mem_type_to_domain(entry->bo->mem.mem_type); | |
| 477 | + /* if anything is swapped out don't swap it in here, | |
| 478 | + just abort and wait for the next CS */ | |
| 479 | + if (domain == AMDGPU_GEM_DOMAIN_CPU) | |
| 480 | + goto error_unreserve; | |
| 481 | + } | |
| 482 | + | |
| 483 | + mutex_lock(&bo_va->vm->mutex); | |
| 484 | + r = amdgpu_vm_clear_freed(adev, bo_va->vm); | |
| 485 | + if (r) | |
| 486 | + goto error_unlock; | |
| 487 | + | |
| 488 | + r = amdgpu_vm_bo_update(adev, bo_va, &bo_va->bo->tbo.mem); | |
| 489 | + | |
| 490 | +error_unlock: | |
| 491 | + mutex_unlock(&bo_va->vm->mutex); | |
| 492 | + | |
| 493 | +error_unreserve: | |
| 494 | + ttm_eu_backoff_reservation(&ticket, &list); | |
| 495 | + | |
| 496 | +error_free: | |
| 497 | + drm_free_large(vm_bos); | |
| 498 | + | |
| 499 | + if (r) | |
| 500 | + DRM_ERROR("Couldn't update BO_VA (%d)\n", r); | |
| 501 | +} | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | +int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, | |
| 506 | + struct drm_file *filp) | |
| 507 | +{ | |
| 508 | + struct drm_amdgpu_gem_va *args = data; | |
| 509 | + struct drm_gem_object *gobj; | |
| 510 | + struct amdgpu_device *adev = dev->dev_private; | |
| 511 | + struct amdgpu_fpriv *fpriv = filp->driver_priv; | |
| 512 | + struct amdgpu_bo *rbo; | |
| 513 | + struct amdgpu_bo_va *bo_va; | |
| 514 | + uint32_t invalid_flags, va_flags = 0; | |
| 515 | + int r = 0; | |
| 516 | + | |
| 517 | + if (!adev->vm_manager.enabled) | |
| 518 | + return -ENOTTY; | |
| 519 | + | |
| 520 | + if (args->va_address < AMDGPU_VA_RESERVED_SIZE) { | |
| 521 | + dev_err(&dev->pdev->dev, | |
| 522 | + "va_address 0x%lX is in reserved area 0x%X\n", | |
| 523 | + (unsigned long)args->va_address, | |
| 524 | + AMDGPU_VA_RESERVED_SIZE); | |
| 525 | + return -EINVAL; | |
| 526 | + } | |
| 527 | + | |
| 528 | + invalid_flags = ~(AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE | | |
| 529 | + AMDGPU_VM_PAGE_EXECUTABLE); | |
| 530 | + if ((args->flags & invalid_flags)) { | |
| 531 | + dev_err(&dev->pdev->dev, "invalid flags 0x%08X vs 0x%08X\n", | |
| 532 | + args->flags, invalid_flags); | |
| 533 | + return -EINVAL; | |
| 534 | + } | |
| 535 | + | |
| 536 | + switch (args->operation) { | |
| 537 | + case AMDGPU_VA_OP_MAP: | |
| 538 | + case AMDGPU_VA_OP_UNMAP: | |
| 539 | + break; | |
| 540 | + default: | |
| 541 | + dev_err(&dev->pdev->dev, "unsupported operation %d\n", | |
| 542 | + args->operation); | |
| 543 | + return -EINVAL; | |
| 544 | + } | |
| 545 | + | |
| 546 | + gobj = drm_gem_object_lookup(dev, filp, args->handle); | |
| 547 | + if (gobj == NULL) | |
| 548 | + return -ENOENT; | |
| 549 | + | |
| 550 | + rbo = gem_to_amdgpu_bo(gobj); | |
| 551 | + r = amdgpu_bo_reserve(rbo, false); | |
| 552 | + if (r) { | |
| 553 | + drm_gem_object_unreference_unlocked(gobj); | |
| 554 | + return r; | |
| 555 | + } | |
| 556 | + | |
| 557 | + bo_va = amdgpu_vm_bo_find(&fpriv->vm, rbo); | |
| 558 | + if (!bo_va) { | |
| 559 | + amdgpu_bo_unreserve(rbo); | |
| 560 | + return -ENOENT; | |
| 561 | + } | |
| 562 | + | |
| 563 | + switch (args->operation) { | |
| 564 | + case AMDGPU_VA_OP_MAP: | |
| 565 | + if (args->flags & AMDGPU_VM_PAGE_READABLE) | |
| 566 | + va_flags |= AMDGPU_PTE_READABLE; | |
| 567 | + if (args->flags & AMDGPU_VM_PAGE_WRITEABLE) | |
| 568 | + va_flags |= AMDGPU_PTE_WRITEABLE; | |
| 569 | + if (args->flags & AMDGPU_VM_PAGE_EXECUTABLE) | |
| 570 | + va_flags |= AMDGPU_PTE_EXECUTABLE; | |
| 571 | + r = amdgpu_vm_bo_map(adev, bo_va, args->va_address, | |
| 572 | + args->offset_in_bo, args->map_size, | |
| 573 | + va_flags); | |
| 574 | + break; | |
| 575 | + case AMDGPU_VA_OP_UNMAP: | |
| 576 | + r = amdgpu_vm_bo_unmap(adev, bo_va, args->va_address); | |
| 577 | + break; | |
| 578 | + default: | |
| 579 | + break; | |
| 580 | + } | |
| 581 | + | |
| 582 | + if (!r) | |
| 583 | + amdgpu_gem_va_update_vm(adev, bo_va); | |
| 584 | + | |
| 585 | + drm_gem_object_unreference_unlocked(gobj); | |
| 586 | + return r; | |
| 587 | +} | |
| 588 | + | |
| 589 | +int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, | |
| 590 | + struct drm_file *filp) | |
| 591 | +{ | |
| 592 | + struct drm_amdgpu_gem_op *args = data; | |
| 593 | + struct drm_gem_object *gobj; | |
| 594 | + struct amdgpu_bo *robj; | |
| 595 | + int r; | |
| 596 | + | |
| 597 | + gobj = drm_gem_object_lookup(dev, filp, args->handle); | |
| 598 | + if (gobj == NULL) { | |
| 599 | + return -ENOENT; | |
| 600 | + } | |
| 601 | + robj = gem_to_amdgpu_bo(gobj); | |
| 602 | + | |
| 603 | + r = amdgpu_bo_reserve(robj, false); | |
| 604 | + if (unlikely(r)) | |
| 605 | + goto out; | |
| 606 | + | |
| 607 | + switch (args->op) { | |
| 608 | + case AMDGPU_GEM_OP_GET_GEM_CREATE_INFO: { | |
| 609 | + struct drm_amdgpu_gem_create_in info; | |
| 610 | + void __user *out = (void __user *)(long)args->value; | |
| 611 | + | |
| 612 | + info.bo_size = robj->gem_base.size; | |
| 613 | + info.alignment = robj->tbo.mem.page_alignment << PAGE_SHIFT; | |
| 614 | + info.domains = robj->initial_domain; | |
| 615 | + info.domain_flags = robj->flags; | |
| 616 | + if (copy_to_user(out, &info, sizeof(info))) | |
| 617 | + r = -EFAULT; | |
| 618 | + break; | |
| 619 | + } | |
| 620 | + case AMDGPU_GEM_OP_SET_PLACEMENT: | |
| 621 | + if (amdgpu_ttm_tt_has_userptr(robj->tbo.ttm)) { | |
| 622 | + r = -EPERM; | |
| 623 | + break; | |
| 624 | + } | |
| 625 | + robj->initial_domain = args->value & (AMDGPU_GEM_DOMAIN_VRAM | | |
| 626 | + AMDGPU_GEM_DOMAIN_GTT | | |
| 627 | + AMDGPU_GEM_DOMAIN_CPU); | |
| 628 | + break; | |
| 629 | + default: | |
| 630 | + r = -EINVAL; | |
| 631 | + } | |
| 632 | + | |
| 633 | + amdgpu_bo_unreserve(robj); | |
| 634 | +out: | |
| 635 | + drm_gem_object_unreference_unlocked(gobj); | |
| 636 | + return r; | |
| 637 | +} | |
| 638 | + | |
| 639 | +int amdgpu_mode_dumb_create(struct drm_file *file_priv, | |
| 640 | + struct drm_device *dev, | |
| 641 | + struct drm_mode_create_dumb *args) | |
| 642 | +{ | |
| 643 | + struct amdgpu_device *adev = dev->dev_private; | |
| 644 | + struct drm_gem_object *gobj; | |
| 645 | + uint32_t handle; | |
| 646 | + int r; | |
| 647 | + | |
| 648 | + args->pitch = amdgpu_align_pitch(adev, args->width, args->bpp, 0) * ((args->bpp + 1) / 8); | |
| 649 | + args->size = args->pitch * args->height; | |
| 650 | + args->size = ALIGN(args->size, PAGE_SIZE); | |
| 651 | + | |
| 652 | + r = amdgpu_gem_object_create(adev, args->size, 0, | |
| 653 | + AMDGPU_GEM_DOMAIN_VRAM, | |
| 654 | + 0, ttm_bo_type_device, | |
| 655 | + &gobj); | |
| 656 | + if (r) | |
| 657 | + return -ENOMEM; | |
| 658 | + | |
| 659 | + r = drm_gem_handle_create(file_priv, gobj, &handle); | |
| 660 | + /* drop reference from allocate - handle holds it now */ | |
| 661 | + drm_gem_object_unreference_unlocked(gobj); | |
| 662 | + if (r) { | |
| 663 | + return r; | |
| 664 | + } | |
| 665 | + args->handle = handle; | |
| 666 | + return 0; | |
| 667 | +} | |
| 668 | + | |
| 669 | +#if defined(CONFIG_DEBUG_FS) | |
| 670 | +static int amdgpu_debugfs_gem_info(struct seq_file *m, void *data) | |
| 671 | +{ | |
| 672 | + struct drm_info_node *node = (struct drm_info_node *)m->private; | |
| 673 | + struct drm_device *dev = node->minor->dev; | |
| 674 | + struct amdgpu_device *adev = dev->dev_private; | |
| 675 | + struct amdgpu_bo *rbo; | |
| 676 | + unsigned i = 0; | |
| 677 | + | |
| 678 | + mutex_lock(&adev->gem.mutex); | |
| 679 | + list_for_each_entry(rbo, &adev->gem.objects, list) { | |
| 680 | + unsigned domain; | |
| 681 | + const char *placement; | |
| 682 | + | |
| 683 | + domain = amdgpu_mem_type_to_domain(rbo->tbo.mem.mem_type); | |
| 684 | + switch (domain) { | |
| 685 | + case AMDGPU_GEM_DOMAIN_VRAM: | |
| 686 | + placement = "VRAM"; | |
| 687 | + break; | |
| 688 | + case AMDGPU_GEM_DOMAIN_GTT: | |
| 689 | + placement = " GTT"; | |
| 690 | + break; | |
| 691 | + case AMDGPU_GEM_DOMAIN_CPU: | |
| 692 | + default: | |
| 693 | + placement = " CPU"; | |
| 694 | + break; | |
| 695 | + } | |
| 696 | + seq_printf(m, "bo[0x%08x] %8ldkB %8ldMB %s pid %8ld\n", | |
| 697 | + i, amdgpu_bo_size(rbo) >> 10, amdgpu_bo_size(rbo) >> 20, | |
| 698 | + placement, (unsigned long)rbo->pid); | |
| 699 | + i++; | |
| 700 | + } | |
| 701 | + mutex_unlock(&adev->gem.mutex); | |
| 702 | + return 0; | |
| 703 | +} | |
| 704 | + | |
| 705 | +static struct drm_info_list amdgpu_debugfs_gem_list[] = { | |
| 706 | + {"amdgpu_gem_info", &amdgpu_debugfs_gem_info, 0, NULL}, | |
| 707 | +}; | |
| 708 | +#endif | |
| 709 | + | |
| 710 | +int amdgpu_gem_debugfs_init(struct amdgpu_device *adev) | |
| 711 | +{ | |
| 712 | +#if defined(CONFIG_DEBUG_FS) | |
| 713 | + return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_gem_list, 1); | |
| 714 | +#endif | |
| 715 | + return 0; | |
| 716 | +} | |
| @@ -0,0 +1,72 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + */ | |
| 25 | +#include <drm/drmP.h> | |
| 26 | +#include "amdgpu.h" | |
| 27 | + | |
| 28 | +/* | |
| 29 | + * GPU scratch registers helpers function. | |
| 30 | + */ | |
| 31 | +/** | |
| 32 | + * amdgpu_gfx_scratch_get - Allocate a scratch register | |
| 33 | + * | |
| 34 | + * @adev: amdgpu_device pointer | |
| 35 | + * @reg: scratch register mmio offset | |
| 36 | + * | |
| 37 | + * Allocate a CP scratch register for use by the driver (all asics). | |
| 38 | + * Returns 0 on success or -EINVAL on failure. | |
| 39 | + */ | |
| 40 | +int amdgpu_gfx_scratch_get(struct amdgpu_device *adev, uint32_t *reg) | |
| 41 | +{ | |
| 42 | + int i; | |
| 43 | + | |
| 44 | + for (i = 0; i < adev->gfx.scratch.num_reg; i++) { | |
| 45 | + if (adev->gfx.scratch.free[i]) { | |
| 46 | + adev->gfx.scratch.free[i] = false; | |
| 47 | + *reg = adev->gfx.scratch.reg[i]; | |
| 48 | + return 0; | |
| 49 | + } | |
| 50 | + } | |
| 51 | + return -EINVAL; | |
| 52 | +} | |
| 53 | + | |
| 54 | +/** | |
| 55 | + * amdgpu_gfx_scratch_free - Free a scratch register | |
| 56 | + * | |
| 57 | + * @adev: amdgpu_device pointer | |
| 58 | + * @reg: scratch register mmio offset | |
| 59 | + * | |
| 60 | + * Free a CP scratch register allocated for use by the driver (all asics) | |
| 61 | + */ | |
| 62 | +void amdgpu_gfx_scratch_free(struct amdgpu_device *adev, uint32_t reg) | |
| 63 | +{ | |
| 64 | + int i; | |
| 65 | + | |
| 66 | + for (i = 0; i < adev->gfx.scratch.num_reg; i++) { | |
| 67 | + if (adev->gfx.scratch.reg[i] == reg) { | |
| 68 | + adev->gfx.scratch.free[i] = true; | |
| 69 | + return; | |
| 70 | + } | |
| 71 | + } | |
| 72 | +} | |
| @@ -0,0 +1,30 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#ifndef __AMDGPU_GFX_H__ | |
| 25 | +#define __AMDGPU_GFX_H__ | |
| 26 | + | |
| 27 | +int amdgpu_gfx_scratch_get(struct amdgpu_device *adev, uint32_t *reg); | |
| 28 | +void amdgpu_gfx_scratch_free(struct amdgpu_device *adev, uint32_t reg); | |
| 29 | + | |
| 30 | +#endif | |
| @@ -0,0 +1,395 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2007-8 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 6 | + * copy of this software and associated documentation files (the "Software"), | |
| 7 | + * to deal in the Software without restriction, including without limitation | |
| 8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 10 | + * Software is furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in | |
| 13 | + * all copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 18 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 19 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 20 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 21 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 22 | + * | |
| 23 | + * Authors: Dave Airlie | |
| 24 | + * Alex Deucher | |
| 25 | + */ | |
| 26 | +#include <linux/export.h> | |
| 27 | + | |
| 28 | +#include <drm/drmP.h> | |
| 29 | +#include <drm/drm_edid.h> | |
| 30 | +#include <drm/amdgpu_drm.h> | |
| 31 | +#include "amdgpu.h" | |
| 32 | +#include "amdgpu_i2c.h" | |
| 33 | +#include "amdgpu_atombios.h" | |
| 34 | +#include "atom.h" | |
| 35 | +#include "atombios_dp.h" | |
| 36 | +#include "atombios_i2c.h" | |
| 37 | + | |
| 38 | +/* bit banging i2c */ | |
| 39 | +static int amdgpu_i2c_pre_xfer(struct i2c_adapter *i2c_adap) | |
| 40 | +{ | |
| 41 | + struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); | |
| 42 | + struct amdgpu_device *adev = i2c->dev->dev_private; | |
| 43 | + struct amdgpu_i2c_bus_rec *rec = &i2c->rec; | |
| 44 | + uint32_t temp; | |
| 45 | + | |
| 46 | + mutex_lock(&i2c->mutex); | |
| 47 | + | |
| 48 | + /* switch the pads to ddc mode */ | |
| 49 | + if (rec->hw_capable) { | |
| 50 | + temp = RREG32(rec->mask_clk_reg); | |
| 51 | + temp &= ~(1 << 16); | |
| 52 | + WREG32(rec->mask_clk_reg, temp); | |
| 53 | + } | |
| 54 | + | |
| 55 | + /* clear the output pin values */ | |
| 56 | + temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask; | |
| 57 | + WREG32(rec->a_clk_reg, temp); | |
| 58 | + | |
| 59 | + temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask; | |
| 60 | + WREG32(rec->a_data_reg, temp); | |
| 61 | + | |
| 62 | + /* set the pins to input */ | |
| 63 | + temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask; | |
| 64 | + WREG32(rec->en_clk_reg, temp); | |
| 65 | + | |
| 66 | + temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask; | |
| 67 | + WREG32(rec->en_data_reg, temp); | |
| 68 | + | |
| 69 | + /* mask the gpio pins for software use */ | |
| 70 | + temp = RREG32(rec->mask_clk_reg) | rec->mask_clk_mask; | |
| 71 | + WREG32(rec->mask_clk_reg, temp); | |
| 72 | + temp = RREG32(rec->mask_clk_reg); | |
| 73 | + | |
| 74 | + temp = RREG32(rec->mask_data_reg) | rec->mask_data_mask; | |
| 75 | + WREG32(rec->mask_data_reg, temp); | |
| 76 | + temp = RREG32(rec->mask_data_reg); | |
| 77 | + | |
| 78 | + return 0; | |
| 79 | +} | |
| 80 | + | |
| 81 | +static void amdgpu_i2c_post_xfer(struct i2c_adapter *i2c_adap) | |
| 82 | +{ | |
| 83 | + struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); | |
| 84 | + struct amdgpu_device *adev = i2c->dev->dev_private; | |
| 85 | + struct amdgpu_i2c_bus_rec *rec = &i2c->rec; | |
| 86 | + uint32_t temp; | |
| 87 | + | |
| 88 | + /* unmask the gpio pins for software use */ | |
| 89 | + temp = RREG32(rec->mask_clk_reg) & ~rec->mask_clk_mask; | |
| 90 | + WREG32(rec->mask_clk_reg, temp); | |
| 91 | + temp = RREG32(rec->mask_clk_reg); | |
| 92 | + | |
| 93 | + temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask; | |
| 94 | + WREG32(rec->mask_data_reg, temp); | |
| 95 | + temp = RREG32(rec->mask_data_reg); | |
| 96 | + | |
| 97 | + mutex_unlock(&i2c->mutex); | |
| 98 | +} | |
| 99 | + | |
| 100 | +static int amdgpu_i2c_get_clock(void *i2c_priv) | |
| 101 | +{ | |
| 102 | + struct amdgpu_i2c_chan *i2c = i2c_priv; | |
| 103 | + struct amdgpu_device *adev = i2c->dev->dev_private; | |
| 104 | + struct amdgpu_i2c_bus_rec *rec = &i2c->rec; | |
| 105 | + uint32_t val; | |
| 106 | + | |
| 107 | + /* read the value off the pin */ | |
| 108 | + val = RREG32(rec->y_clk_reg); | |
| 109 | + val &= rec->y_clk_mask; | |
| 110 | + | |
| 111 | + return (val != 0); | |
| 112 | +} | |
| 113 | + | |
| 114 | + | |
| 115 | +static int amdgpu_i2c_get_data(void *i2c_priv) | |
| 116 | +{ | |
| 117 | + struct amdgpu_i2c_chan *i2c = i2c_priv; | |
| 118 | + struct amdgpu_device *adev = i2c->dev->dev_private; | |
| 119 | + struct amdgpu_i2c_bus_rec *rec = &i2c->rec; | |
| 120 | + uint32_t val; | |
| 121 | + | |
| 122 | + /* read the value off the pin */ | |
| 123 | + val = RREG32(rec->y_data_reg); | |
| 124 | + val &= rec->y_data_mask; | |
| 125 | + | |
| 126 | + return (val != 0); | |
| 127 | +} | |
| 128 | + | |
| 129 | +static void amdgpu_i2c_set_clock(void *i2c_priv, int clock) | |
| 130 | +{ | |
| 131 | + struct amdgpu_i2c_chan *i2c = i2c_priv; | |
| 132 | + struct amdgpu_device *adev = i2c->dev->dev_private; | |
| 133 | + struct amdgpu_i2c_bus_rec *rec = &i2c->rec; | |
| 134 | + uint32_t val; | |
| 135 | + | |
| 136 | + /* set pin direction */ | |
| 137 | + val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask; | |
| 138 | + val |= clock ? 0 : rec->en_clk_mask; | |
| 139 | + WREG32(rec->en_clk_reg, val); | |
| 140 | +} | |
| 141 | + | |
| 142 | +static void amdgpu_i2c_set_data(void *i2c_priv, int data) | |
| 143 | +{ | |
| 144 | + struct amdgpu_i2c_chan *i2c = i2c_priv; | |
| 145 | + struct amdgpu_device *adev = i2c->dev->dev_private; | |
| 146 | + struct amdgpu_i2c_bus_rec *rec = &i2c->rec; | |
| 147 | + uint32_t val; | |
| 148 | + | |
| 149 | + /* set pin direction */ | |
| 150 | + val = RREG32(rec->en_data_reg) & ~rec->en_data_mask; | |
| 151 | + val |= data ? 0 : rec->en_data_mask; | |
| 152 | + WREG32(rec->en_data_reg, val); | |
| 153 | +} | |
| 154 | + | |
| 155 | +static const struct i2c_algorithm amdgpu_atombios_i2c_algo = { | |
| 156 | + .master_xfer = amdgpu_atombios_i2c_xfer, | |
| 157 | + .functionality = amdgpu_atombios_i2c_func, | |
| 158 | +}; | |
| 159 | + | |
| 160 | +struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev, | |
| 161 | + struct amdgpu_i2c_bus_rec *rec, | |
| 162 | + const char *name) | |
| 163 | +{ | |
| 164 | + struct amdgpu_i2c_chan *i2c; | |
| 165 | + int ret; | |
| 166 | + | |
| 167 | + /* don't add the mm_i2c bus unless hw_i2c is enabled */ | |
| 168 | + if (rec->mm_i2c && (amdgpu_hw_i2c == 0)) | |
| 169 | + return NULL; | |
| 170 | + | |
| 171 | + i2c = kzalloc(sizeof(struct amdgpu_i2c_chan), GFP_KERNEL); | |
| 172 | + if (i2c == NULL) | |
| 173 | + return NULL; | |
| 174 | + | |
| 175 | + i2c->rec = *rec; | |
| 176 | + i2c->adapter.owner = THIS_MODULE; | |
| 177 | + i2c->adapter.class = I2C_CLASS_DDC; | |
| 178 | + i2c->adapter.dev.parent = &dev->pdev->dev; | |
| 179 | + i2c->dev = dev; | |
| 180 | + i2c_set_adapdata(&i2c->adapter, i2c); | |
| 181 | + mutex_init(&i2c->mutex); | |
| 182 | + if (rec->hw_capable && | |
| 183 | + amdgpu_hw_i2c) { | |
| 184 | + /* hw i2c using atom */ | |
| 185 | + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), | |
| 186 | + "AMDGPU i2c hw bus %s", name); | |
| 187 | + i2c->adapter.algo = &amdgpu_atombios_i2c_algo; | |
| 188 | + ret = i2c_add_adapter(&i2c->adapter); | |
| 189 | + if (ret) { | |
| 190 | + DRM_ERROR("Failed to register hw i2c %s\n", name); | |
| 191 | + goto out_free; | |
| 192 | + } | |
| 193 | + } else { | |
| 194 | + /* set the amdgpu bit adapter */ | |
| 195 | + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), | |
| 196 | + "AMDGPU i2c bit bus %s", name); | |
| 197 | + i2c->adapter.algo_data = &i2c->bit; | |
| 198 | + i2c->bit.pre_xfer = amdgpu_i2c_pre_xfer; | |
| 199 | + i2c->bit.post_xfer = amdgpu_i2c_post_xfer; | |
| 200 | + i2c->bit.setsda = amdgpu_i2c_set_data; | |
| 201 | + i2c->bit.setscl = amdgpu_i2c_set_clock; | |
| 202 | + i2c->bit.getsda = amdgpu_i2c_get_data; | |
| 203 | + i2c->bit.getscl = amdgpu_i2c_get_clock; | |
| 204 | + i2c->bit.udelay = 10; | |
| 205 | + i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */ | |
| 206 | + i2c->bit.data = i2c; | |
| 207 | + ret = i2c_bit_add_bus(&i2c->adapter); | |
| 208 | + if (ret) { | |
| 209 | + DRM_ERROR("Failed to register bit i2c %s\n", name); | |
| 210 | + goto out_free; | |
| 211 | + } | |
| 212 | + } | |
| 213 | + | |
| 214 | + return i2c; | |
| 215 | +out_free: | |
| 216 | + kfree(i2c); | |
| 217 | + return NULL; | |
| 218 | + | |
| 219 | +} | |
| 220 | + | |
| 221 | +void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c) | |
| 222 | +{ | |
| 223 | + if (!i2c) | |
| 224 | + return; | |
| 225 | + i2c_del_adapter(&i2c->adapter); | |
| 226 | + kfree(i2c); | |
| 227 | +} | |
| 228 | + | |
| 229 | +/* Add the default buses */ | |
| 230 | +void amdgpu_i2c_init(struct amdgpu_device *adev) | |
| 231 | +{ | |
| 232 | + if (amdgpu_hw_i2c) | |
| 233 | + DRM_INFO("hw_i2c forced on, you may experience display detection problems!\n"); | |
| 234 | + | |
| 235 | + if (adev->is_atom_bios) | |
| 236 | + amdgpu_atombios_i2c_init(adev); | |
| 237 | +} | |
| 238 | + | |
| 239 | +/* remove all the buses */ | |
| 240 | +void amdgpu_i2c_fini(struct amdgpu_device *adev) | |
| 241 | +{ | |
| 242 | + int i; | |
| 243 | + | |
| 244 | + for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) { | |
| 245 | + if (adev->i2c_bus[i]) { | |
| 246 | + amdgpu_i2c_destroy(adev->i2c_bus[i]); | |
| 247 | + adev->i2c_bus[i] = NULL; | |
| 248 | + } | |
| 249 | + } | |
| 250 | +} | |
| 251 | + | |
| 252 | +/* Add additional buses */ | |
| 253 | +void amdgpu_i2c_add(struct amdgpu_device *adev, | |
| 254 | + struct amdgpu_i2c_bus_rec *rec, | |
| 255 | + const char *name) | |
| 256 | +{ | |
| 257 | + struct drm_device *dev = adev->ddev; | |
| 258 | + int i; | |
| 259 | + | |
| 260 | + for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) { | |
| 261 | + if (!adev->i2c_bus[i]) { | |
| 262 | + adev->i2c_bus[i] = amdgpu_i2c_create(dev, rec, name); | |
| 263 | + return; | |
| 264 | + } | |
| 265 | + } | |
| 266 | +} | |
| 267 | + | |
| 268 | +/* looks up bus based on id */ | |
| 269 | +struct amdgpu_i2c_chan * | |
| 270 | +amdgpu_i2c_lookup(struct amdgpu_device *adev, | |
| 271 | + struct amdgpu_i2c_bus_rec *i2c_bus) | |
| 272 | +{ | |
| 273 | + int i; | |
| 274 | + | |
| 275 | + for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) { | |
| 276 | + if (adev->i2c_bus[i] && | |
| 277 | + (adev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) { | |
| 278 | + return adev->i2c_bus[i]; | |
| 279 | + } | |
| 280 | + } | |
| 281 | + return NULL; | |
| 282 | +} | |
| 283 | + | |
| 284 | +static void amdgpu_i2c_get_byte(struct amdgpu_i2c_chan *i2c_bus, | |
| 285 | + u8 slave_addr, | |
| 286 | + u8 addr, | |
| 287 | + u8 *val) | |
| 288 | +{ | |
| 289 | + u8 out_buf[2]; | |
| 290 | + u8 in_buf[2]; | |
| 291 | + struct i2c_msg msgs[] = { | |
| 292 | + { | |
| 293 | + .addr = slave_addr, | |
| 294 | + .flags = 0, | |
| 295 | + .len = 1, | |
| 296 | + .buf = out_buf, | |
| 297 | + }, | |
| 298 | + { | |
| 299 | + .addr = slave_addr, | |
| 300 | + .flags = I2C_M_RD, | |
| 301 | + .len = 1, | |
| 302 | + .buf = in_buf, | |
| 303 | + } | |
| 304 | + }; | |
| 305 | + | |
| 306 | + out_buf[0] = addr; | |
| 307 | + out_buf[1] = 0; | |
| 308 | + | |
| 309 | + if (i2c_transfer(&i2c_bus->adapter, msgs, 2) == 2) { | |
| 310 | + *val = in_buf[0]; | |
| 311 | + DRM_DEBUG("val = 0x%02x\n", *val); | |
| 312 | + } else { | |
| 313 | + DRM_DEBUG("i2c 0x%02x 0x%02x read failed\n", | |
| 314 | + addr, *val); | |
| 315 | + } | |
| 316 | +} | |
| 317 | + | |
| 318 | +static void amdgpu_i2c_put_byte(struct amdgpu_i2c_chan *i2c_bus, | |
| 319 | + u8 slave_addr, | |
| 320 | + u8 addr, | |
| 321 | + u8 val) | |
| 322 | +{ | |
| 323 | + uint8_t out_buf[2]; | |
| 324 | + struct i2c_msg msg = { | |
| 325 | + .addr = slave_addr, | |
| 326 | + .flags = 0, | |
| 327 | + .len = 2, | |
| 328 | + .buf = out_buf, | |
| 329 | + }; | |
| 330 | + | |
| 331 | + out_buf[0] = addr; | |
| 332 | + out_buf[1] = val; | |
| 333 | + | |
| 334 | + if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1) | |
| 335 | + DRM_DEBUG("i2c 0x%02x 0x%02x write failed\n", | |
| 336 | + addr, val); | |
| 337 | +} | |
| 338 | + | |
| 339 | +/* ddc router switching */ | |
| 340 | +void | |
| 341 | +amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector) | |
| 342 | +{ | |
| 343 | + u8 val; | |
| 344 | + | |
| 345 | + if (!amdgpu_connector->router.ddc_valid) | |
| 346 | + return; | |
| 347 | + | |
| 348 | + if (!amdgpu_connector->router_bus) | |
| 349 | + return; | |
| 350 | + | |
| 351 | + amdgpu_i2c_get_byte(amdgpu_connector->router_bus, | |
| 352 | + amdgpu_connector->router.i2c_addr, | |
| 353 | + 0x3, &val); | |
| 354 | + val &= ~amdgpu_connector->router.ddc_mux_control_pin; | |
| 355 | + amdgpu_i2c_put_byte(amdgpu_connector->router_bus, | |
| 356 | + amdgpu_connector->router.i2c_addr, | |
| 357 | + 0x3, val); | |
| 358 | + amdgpu_i2c_get_byte(amdgpu_connector->router_bus, | |
| 359 | + amdgpu_connector->router.i2c_addr, | |
| 360 | + 0x1, &val); | |
| 361 | + val &= ~amdgpu_connector->router.ddc_mux_control_pin; | |
| 362 | + val |= amdgpu_connector->router.ddc_mux_state; | |
| 363 | + amdgpu_i2c_put_byte(amdgpu_connector->router_bus, | |
| 364 | + amdgpu_connector->router.i2c_addr, | |
| 365 | + 0x1, val); | |
| 366 | +} | |
| 367 | + | |
| 368 | +/* clock/data router switching */ | |
| 369 | +void | |
| 370 | +amdgpu_i2c_router_select_cd_port(struct amdgpu_connector *amdgpu_connector) | |
| 371 | +{ | |
| 372 | + u8 val; | |
| 373 | + | |
| 374 | + if (!amdgpu_connector->router.cd_valid) | |
| 375 | + return; | |
| 376 | + | |
| 377 | + if (!amdgpu_connector->router_bus) | |
| 378 | + return; | |
| 379 | + | |
| 380 | + amdgpu_i2c_get_byte(amdgpu_connector->router_bus, | |
| 381 | + amdgpu_connector->router.i2c_addr, | |
| 382 | + 0x3, &val); | |
| 383 | + val &= ~amdgpu_connector->router.cd_mux_control_pin; | |
| 384 | + amdgpu_i2c_put_byte(amdgpu_connector->router_bus, | |
| 385 | + amdgpu_connector->router.i2c_addr, | |
| 386 | + 0x3, val); | |
| 387 | + amdgpu_i2c_get_byte(amdgpu_connector->router_bus, | |
| 388 | + amdgpu_connector->router.i2c_addr, | |
| 389 | + 0x1, &val); | |
| 390 | + val &= ~amdgpu_connector->router.cd_mux_control_pin; | |
| 391 | + val |= amdgpu_connector->router.cd_mux_state; | |
| 392 | + amdgpu_i2c_put_byte(amdgpu_connector->router_bus, | |
| 393 | + amdgpu_connector->router.i2c_addr, | |
| 394 | + 0x1, val); | |
| 395 | +} | |
| @@ -0,0 +1,44 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#ifndef __AMDGPU_I2C_H__ | |
| 25 | +#define __AMDGPU_I2C_H__ | |
| 26 | + | |
| 27 | +struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev, | |
| 28 | + struct amdgpu_i2c_bus_rec *rec, | |
| 29 | + const char *name); | |
| 30 | +void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c); | |
| 31 | +void amdgpu_i2c_init(struct amdgpu_device *adev); | |
| 32 | +void amdgpu_i2c_fini(struct amdgpu_device *adev); | |
| 33 | +void amdgpu_i2c_add(struct amdgpu_device *adev, | |
| 34 | + struct amdgpu_i2c_bus_rec *rec, | |
| 35 | + const char *name); | |
| 36 | +struct amdgpu_i2c_chan * | |
| 37 | +amdgpu_i2c_lookup(struct amdgpu_device *adev, | |
| 38 | + struct amdgpu_i2c_bus_rec *i2c_bus); | |
| 39 | +void | |
| 40 | +amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector); | |
| 41 | +void | |
| 42 | +amdgpu_i2c_router_select_cd_port(struct amdgpu_connector *amdgpu_connector); | |
| 43 | + | |
| 44 | +#endif | |
| @@ -0,0 +1,354 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2008 Advanced Micro Devices, Inc. | |
| 3 | + * Copyright 2008 Red Hat Inc. | |
| 4 | + * Copyright 2009 Jerome Glisse. | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 7 | + * copy of this software and associated documentation files (the "Software"), | |
| 8 | + * to deal in the Software without restriction, including without limitation | |
| 9 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 10 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 11 | + * Software is furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | + * | |
| 24 | + * Authors: Dave Airlie | |
| 25 | + * Alex Deucher | |
| 26 | + * Jerome Glisse | |
| 27 | + * Christian König | |
| 28 | + */ | |
| 29 | +#include <linux/seq_file.h> | |
| 30 | +#include <linux/slab.h> | |
| 31 | +#include <drm/drmP.h> | |
| 32 | +#include <drm/amdgpu_drm.h> | |
| 33 | +#include "amdgpu.h" | |
| 34 | +#include "atom.h" | |
| 35 | + | |
| 36 | +/* | |
| 37 | + * IB | |
| 38 | + * IBs (Indirect Buffers) and areas of GPU accessible memory where | |
| 39 | + * commands are stored. You can put a pointer to the IB in the | |
| 40 | + * command ring and the hw will fetch the commands from the IB | |
| 41 | + * and execute them. Generally userspace acceleration drivers | |
| 42 | + * produce command buffers which are send to the kernel and | |
| 43 | + * put in IBs for execution by the requested ring. | |
| 44 | + */ | |
| 45 | +static int amdgpu_debugfs_sa_init(struct amdgpu_device *adev); | |
| 46 | + | |
| 47 | +/** | |
| 48 | + * amdgpu_ib_get - request an IB (Indirect Buffer) | |
| 49 | + * | |
| 50 | + * @ring: ring index the IB is associated with | |
| 51 | + * @size: requested IB size | |
| 52 | + * @ib: IB object returned | |
| 53 | + * | |
| 54 | + * Request an IB (all asics). IBs are allocated using the | |
| 55 | + * suballocator. | |
| 56 | + * Returns 0 on success, error on failure. | |
| 57 | + */ | |
| 58 | +int amdgpu_ib_get(struct amdgpu_ring *ring, struct amdgpu_vm *vm, | |
| 59 | + unsigned size, struct amdgpu_ib *ib) | |
| 60 | +{ | |
| 61 | + struct amdgpu_device *adev = ring->adev; | |
| 62 | + int r; | |
| 63 | + | |
| 64 | + if (size) { | |
| 65 | + r = amdgpu_sa_bo_new(adev, &adev->ring_tmp_bo, | |
| 66 | + &ib->sa_bo, size, 256); | |
| 67 | + if (r) { | |
| 68 | + dev_err(adev->dev, "failed to get a new IB (%d)\n", r); | |
| 69 | + return r; | |
| 70 | + } | |
| 71 | + | |
| 72 | + ib->ptr = amdgpu_sa_bo_cpu_addr(ib->sa_bo); | |
| 73 | + | |
| 74 | + if (!vm) | |
| 75 | + ib->gpu_addr = amdgpu_sa_bo_gpu_addr(ib->sa_bo); | |
| 76 | + else | |
| 77 | + ib->gpu_addr = 0; | |
| 78 | + | |
| 79 | + } else { | |
| 80 | + ib->sa_bo = NULL; | |
| 81 | + ib->ptr = NULL; | |
| 82 | + ib->gpu_addr = 0; | |
| 83 | + } | |
| 84 | + | |
| 85 | + amdgpu_sync_create(&ib->sync); | |
| 86 | + | |
| 87 | + ib->ring = ring; | |
| 88 | + ib->fence = NULL; | |
| 89 | + ib->user = NULL; | |
| 90 | + ib->vm = vm; | |
| 91 | + ib->gds_base = 0; | |
| 92 | + ib->gds_size = 0; | |
| 93 | + ib->gws_base = 0; | |
| 94 | + ib->gws_size = 0; | |
| 95 | + ib->oa_base = 0; | |
| 96 | + ib->oa_size = 0; | |
| 97 | + ib->flags = 0; | |
| 98 | + | |
| 99 | + return 0; | |
| 100 | +} | |
| 101 | + | |
| 102 | +/** | |
| 103 | + * amdgpu_ib_free - free an IB (Indirect Buffer) | |
| 104 | + * | |
| 105 | + * @adev: amdgpu_device pointer | |
| 106 | + * @ib: IB object to free | |
| 107 | + * | |
| 108 | + * Free an IB (all asics). | |
| 109 | + */ | |
| 110 | +void amdgpu_ib_free(struct amdgpu_device *adev, struct amdgpu_ib *ib) | |
| 111 | +{ | |
| 112 | + amdgpu_sync_free(adev, &ib->sync, ib->fence); | |
| 113 | + amdgpu_sa_bo_free(adev, &ib->sa_bo, ib->fence); | |
| 114 | + amdgpu_fence_unref(&ib->fence); | |
| 115 | +} | |
| 116 | + | |
| 117 | +/** | |
| 118 | + * amdgpu_ib_schedule - schedule an IB (Indirect Buffer) on the ring | |
| 119 | + * | |
| 120 | + * @adev: amdgpu_device pointer | |
| 121 | + * @num_ibs: number of IBs to schedule | |
| 122 | + * @ibs: IB objects to schedule | |
| 123 | + * @owner: owner for creating the fences | |
| 124 | + * | |
| 125 | + * Schedule an IB on the associated ring (all asics). | |
| 126 | + * Returns 0 on success, error on failure. | |
| 127 | + * | |
| 128 | + * On SI, there are two parallel engines fed from the primary ring, | |
| 129 | + * the CE (Constant Engine) and the DE (Drawing Engine). Since | |
| 130 | + * resource descriptors have moved to memory, the CE allows you to | |
| 131 | + * prime the caches while the DE is updating register state so that | |
| 132 | + * the resource descriptors will be already in cache when the draw is | |
| 133 | + * processed. To accomplish this, the userspace driver submits two | |
| 134 | + * IBs, one for the CE and one for the DE. If there is a CE IB (called | |
| 135 | + * a CONST_IB), it will be put on the ring prior to the DE IB. Prior | |
| 136 | + * to SI there was just a DE IB. | |
| 137 | + */ | |
| 138 | +int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs, | |
| 139 | + struct amdgpu_ib *ibs, void *owner) | |
| 140 | +{ | |
| 141 | + struct amdgpu_ib *ib = &ibs[0]; | |
| 142 | + struct amdgpu_ring *ring; | |
| 143 | + struct amdgpu_ctx *ctx, *old_ctx; | |
| 144 | + struct amdgpu_vm *vm; | |
| 145 | + unsigned i; | |
| 146 | + int r = 0; | |
| 147 | + | |
| 148 | + if (num_ibs == 0) | |
| 149 | + return -EINVAL; | |
| 150 | + | |
| 151 | + ring = ibs->ring; | |
| 152 | + ctx = ibs->ctx; | |
| 153 | + vm = ibs->vm; | |
| 154 | + | |
| 155 | + if (!ring->ready) { | |
| 156 | + dev_err(adev->dev, "couldn't schedule ib\n"); | |
| 157 | + return -EINVAL; | |
| 158 | + } | |
| 159 | + | |
| 160 | + r = amdgpu_ring_lock(ring, (256 + AMDGPU_NUM_SYNCS * 8) * num_ibs); | |
| 161 | + if (r) { | |
| 162 | + dev_err(adev->dev, "scheduling IB failed (%d).\n", r); | |
| 163 | + return r; | |
| 164 | + } | |
| 165 | + | |
| 166 | + if (vm) { | |
| 167 | + /* grab a vm id if necessary */ | |
| 168 | + struct amdgpu_fence *vm_id_fence = NULL; | |
| 169 | + vm_id_fence = amdgpu_vm_grab_id(ibs->ring, ibs->vm); | |
| 170 | + amdgpu_sync_fence(&ibs->sync, vm_id_fence); | |
| 171 | + } | |
| 172 | + | |
| 173 | + r = amdgpu_sync_rings(&ibs->sync, ring); | |
| 174 | + if (r) { | |
| 175 | + amdgpu_ring_unlock_undo(ring); | |
| 176 | + dev_err(adev->dev, "failed to sync rings (%d)\n", r); | |
| 177 | + return r; | |
| 178 | + } | |
| 179 | + | |
| 180 | + if (vm) { | |
| 181 | + /* do context switch */ | |
| 182 | + amdgpu_vm_flush(ring, vm, ib->sync.last_vm_update); | |
| 183 | + } | |
| 184 | + | |
| 185 | + if (vm && ring->funcs->emit_gds_switch) | |
| 186 | + amdgpu_ring_emit_gds_switch(ring, ib->vm->ids[ring->idx].id, | |
| 187 | + ib->gds_base, ib->gds_size, | |
| 188 | + ib->gws_base, ib->gws_size, | |
| 189 | + ib->oa_base, ib->oa_size); | |
| 190 | + | |
| 191 | + if (ring->funcs->emit_hdp_flush) | |
| 192 | + amdgpu_ring_emit_hdp_flush(ring); | |
| 193 | + | |
| 194 | + old_ctx = ring->current_ctx; | |
| 195 | + for (i = 0; i < num_ibs; ++i) { | |
| 196 | + ib = &ibs[i]; | |
| 197 | + | |
| 198 | + if (ib->ring != ring || ib->ctx != ctx || ib->vm != vm) { | |
| 199 | + ring->current_ctx = old_ctx; | |
| 200 | + amdgpu_ring_unlock_undo(ring); | |
| 201 | + return -EINVAL; | |
| 202 | + } | |
| 203 | + amdgpu_ring_emit_ib(ring, ib); | |
| 204 | + ring->current_ctx = ctx; | |
| 205 | + } | |
| 206 | + | |
| 207 | + r = amdgpu_fence_emit(ring, owner, &ib->fence); | |
| 208 | + if (r) { | |
| 209 | + dev_err(adev->dev, "failed to emit fence (%d)\n", r); | |
| 210 | + ring->current_ctx = old_ctx; | |
| 211 | + amdgpu_ring_unlock_undo(ring); | |
| 212 | + return r; | |
| 213 | + } | |
| 214 | + | |
| 215 | + /* wrap the last IB with fence */ | |
| 216 | + if (ib->user) { | |
| 217 | + uint64_t addr = amdgpu_bo_gpu_offset(ib->user->bo); | |
| 218 | + addr += ib->user->offset; | |
| 219 | + amdgpu_ring_emit_fence(ring, addr, ib->fence->seq, | |
| 220 | + AMDGPU_FENCE_FLAG_64BIT); | |
| 221 | + } | |
| 222 | + | |
| 223 | + if (ib->vm) | |
| 224 | + amdgpu_vm_fence(adev, ib->vm, ib->fence); | |
| 225 | + | |
| 226 | + amdgpu_ring_unlock_commit(ring); | |
| 227 | + return 0; | |
| 228 | +} | |
| 229 | + | |
| 230 | +/** | |
| 231 | + * amdgpu_ib_pool_init - Init the IB (Indirect Buffer) pool | |
| 232 | + * | |
| 233 | + * @adev: amdgpu_device pointer | |
| 234 | + * | |
| 235 | + * Initialize the suballocator to manage a pool of memory | |
| 236 | + * for use as IBs (all asics). | |
| 237 | + * Returns 0 on success, error on failure. | |
| 238 | + */ | |
| 239 | +int amdgpu_ib_pool_init(struct amdgpu_device *adev) | |
| 240 | +{ | |
| 241 | + int r; | |
| 242 | + | |
| 243 | + if (adev->ib_pool_ready) { | |
| 244 | + return 0; | |
| 245 | + } | |
| 246 | + r = amdgpu_sa_bo_manager_init(adev, &adev->ring_tmp_bo, | |
| 247 | + AMDGPU_IB_POOL_SIZE*64*1024, | |
| 248 | + AMDGPU_GPU_PAGE_SIZE, | |
| 249 | + AMDGPU_GEM_DOMAIN_GTT); | |
| 250 | + if (r) { | |
| 251 | + return r; | |
| 252 | + } | |
| 253 | + | |
| 254 | + r = amdgpu_sa_bo_manager_start(adev, &adev->ring_tmp_bo); | |
| 255 | + if (r) { | |
| 256 | + return r; | |
| 257 | + } | |
| 258 | + | |
| 259 | + adev->ib_pool_ready = true; | |
| 260 | + if (amdgpu_debugfs_sa_init(adev)) { | |
| 261 | + dev_err(adev->dev, "failed to register debugfs file for SA\n"); | |
| 262 | + } | |
| 263 | + return 0; | |
| 264 | +} | |
| 265 | + | |
| 266 | +/** | |
| 267 | + * amdgpu_ib_pool_fini - Free the IB (Indirect Buffer) pool | |
| 268 | + * | |
| 269 | + * @adev: amdgpu_device pointer | |
| 270 | + * | |
| 271 | + * Tear down the suballocator managing the pool of memory | |
| 272 | + * for use as IBs (all asics). | |
| 273 | + */ | |
| 274 | +void amdgpu_ib_pool_fini(struct amdgpu_device *adev) | |
| 275 | +{ | |
| 276 | + if (adev->ib_pool_ready) { | |
| 277 | + amdgpu_sa_bo_manager_suspend(adev, &adev->ring_tmp_bo); | |
| 278 | + amdgpu_sa_bo_manager_fini(adev, &adev->ring_tmp_bo); | |
| 279 | + adev->ib_pool_ready = false; | |
| 280 | + } | |
| 281 | +} | |
| 282 | + | |
| 283 | +/** | |
| 284 | + * amdgpu_ib_ring_tests - test IBs on the rings | |
| 285 | + * | |
| 286 | + * @adev: amdgpu_device pointer | |
| 287 | + * | |
| 288 | + * Test an IB (Indirect Buffer) on each ring. | |
| 289 | + * If the test fails, disable the ring. | |
| 290 | + * Returns 0 on success, error if the primary GFX ring | |
| 291 | + * IB test fails. | |
| 292 | + */ | |
| 293 | +int amdgpu_ib_ring_tests(struct amdgpu_device *adev) | |
| 294 | +{ | |
| 295 | + unsigned i; | |
| 296 | + int r; | |
| 297 | + | |
| 298 | + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | |
| 299 | + struct amdgpu_ring *ring = adev->rings[i]; | |
| 300 | + | |
| 301 | + if (!ring || !ring->ready) | |
| 302 | + continue; | |
| 303 | + | |
| 304 | + r = amdgpu_ring_test_ib(ring); | |
| 305 | + if (r) { | |
| 306 | + ring->ready = false; | |
| 307 | + adev->needs_reset = false; | |
| 308 | + | |
| 309 | + if (ring == &adev->gfx.gfx_ring[0]) { | |
| 310 | + /* oh, oh, that's really bad */ | |
| 311 | + DRM_ERROR("amdgpu: failed testing IB on GFX ring (%d).\n", r); | |
| 312 | + adev->accel_working = false; | |
| 313 | + return r; | |
| 314 | + | |
| 315 | + } else { | |
| 316 | + /* still not good, but we can live with it */ | |
| 317 | + DRM_ERROR("amdgpu: failed testing IB on ring %d (%d).\n", i, r); | |
| 318 | + } | |
| 319 | + } | |
| 320 | + } | |
| 321 | + return 0; | |
| 322 | +} | |
| 323 | + | |
| 324 | +/* | |
| 325 | + * Debugfs info | |
| 326 | + */ | |
| 327 | +#if defined(CONFIG_DEBUG_FS) | |
| 328 | + | |
| 329 | +static int amdgpu_debugfs_sa_info(struct seq_file *m, void *data) | |
| 330 | +{ | |
| 331 | + struct drm_info_node *node = (struct drm_info_node *) m->private; | |
| 332 | + struct drm_device *dev = node->minor->dev; | |
| 333 | + struct amdgpu_device *adev = dev->dev_private; | |
| 334 | + | |
| 335 | + amdgpu_sa_bo_dump_debug_info(&adev->ring_tmp_bo, m); | |
| 336 | + | |
| 337 | + return 0; | |
| 338 | + | |
| 339 | +} | |
| 340 | + | |
| 341 | +static struct drm_info_list amdgpu_debugfs_sa_list[] = { | |
| 342 | + {"amdgpu_sa_info", &amdgpu_debugfs_sa_info, 0, NULL}, | |
| 343 | +}; | |
| 344 | + | |
| 345 | +#endif | |
| 346 | + | |
| 347 | +static int amdgpu_debugfs_sa_init(struct amdgpu_device *adev) | |
| 348 | +{ | |
| 349 | +#if defined(CONFIG_DEBUG_FS) | |
| 350 | + return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_sa_list, 1); | |
| 351 | +#else | |
| 352 | + return 0; | |
| 353 | +#endif | |
| 354 | +} | |
| @@ -0,0 +1,216 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
| 12 | + * all copies or substantial portions of the Software. | |
| 13 | + * | |
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 17 | + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 18 | + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 19 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 20 | + * OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | + * | |
| 22 | + */ | |
| 23 | + | |
| 24 | +#include <drm/drmP.h> | |
| 25 | +#include "amdgpu.h" | |
| 26 | +#include "amdgpu_ih.h" | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * amdgpu_ih_ring_alloc - allocate memory for the IH ring | |
| 30 | + * | |
| 31 | + * @adev: amdgpu_device pointer | |
| 32 | + * | |
| 33 | + * Allocate a ring buffer for the interrupt controller. | |
| 34 | + * Returns 0 for success, errors for failure. | |
| 35 | + */ | |
| 36 | +static int amdgpu_ih_ring_alloc(struct amdgpu_device *adev) | |
| 37 | +{ | |
| 38 | + int r; | |
| 39 | + | |
| 40 | + /* Allocate ring buffer */ | |
| 41 | + if (adev->irq.ih.ring_obj == NULL) { | |
| 42 | + r = amdgpu_bo_create(adev, adev->irq.ih.ring_size, | |
| 43 | + PAGE_SIZE, true, | |
| 44 | + AMDGPU_GEM_DOMAIN_GTT, 0, | |
| 45 | + NULL, &adev->irq.ih.ring_obj); | |
| 46 | + if (r) { | |
| 47 | + DRM_ERROR("amdgpu: failed to create ih ring buffer (%d).\n", r); | |
| 48 | + return r; | |
| 49 | + } | |
| 50 | + r = amdgpu_bo_reserve(adev->irq.ih.ring_obj, false); | |
| 51 | + if (unlikely(r != 0)) | |
| 52 | + return r; | |
| 53 | + r = amdgpu_bo_pin(adev->irq.ih.ring_obj, | |
| 54 | + AMDGPU_GEM_DOMAIN_GTT, | |
| 55 | + &adev->irq.ih.gpu_addr); | |
| 56 | + if (r) { | |
| 57 | + amdgpu_bo_unreserve(adev->irq.ih.ring_obj); | |
| 58 | + DRM_ERROR("amdgpu: failed to pin ih ring buffer (%d).\n", r); | |
| 59 | + return r; | |
| 60 | + } | |
| 61 | + r = amdgpu_bo_kmap(adev->irq.ih.ring_obj, | |
| 62 | + (void **)&adev->irq.ih.ring); | |
| 63 | + amdgpu_bo_unreserve(adev->irq.ih.ring_obj); | |
| 64 | + if (r) { | |
| 65 | + DRM_ERROR("amdgpu: failed to map ih ring buffer (%d).\n", r); | |
| 66 | + return r; | |
| 67 | + } | |
| 68 | + } | |
| 69 | + return 0; | |
| 70 | +} | |
| 71 | + | |
| 72 | +/** | |
| 73 | + * amdgpu_ih_ring_init - initialize the IH state | |
| 74 | + * | |
| 75 | + * @adev: amdgpu_device pointer | |
| 76 | + * | |
| 77 | + * Initializes the IH state and allocates a buffer | |
| 78 | + * for the IH ring buffer. | |
| 79 | + * Returns 0 for success, errors for failure. | |
| 80 | + */ | |
| 81 | +int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size, | |
| 82 | + bool use_bus_addr) | |
| 83 | +{ | |
| 84 | + u32 rb_bufsz; | |
| 85 | + int r; | |
| 86 | + | |
| 87 | + /* Align ring size */ | |
| 88 | + rb_bufsz = order_base_2(ring_size / 4); | |
| 89 | + ring_size = (1 << rb_bufsz) * 4; | |
| 90 | + adev->irq.ih.ring_size = ring_size; | |
| 91 | + adev->irq.ih.ptr_mask = adev->irq.ih.ring_size - 1; | |
| 92 | + adev->irq.ih.rptr = 0; | |
| 93 | + adev->irq.ih.use_bus_addr = use_bus_addr; | |
| 94 | + | |
| 95 | + if (adev->irq.ih.use_bus_addr) { | |
| 96 | + if (!adev->irq.ih.ring) { | |
| 97 | + /* add 8 bytes for the rptr/wptr shadows and | |
| 98 | + * add them to the end of the ring allocation. | |
| 99 | + */ | |
| 100 | + adev->irq.ih.ring = kzalloc(adev->irq.ih.ring_size + 8, GFP_KERNEL); | |
| 101 | + if (adev->irq.ih.ring == NULL) | |
| 102 | + return -ENOMEM; | |
| 103 | + adev->irq.ih.rb_dma_addr = pci_map_single(adev->pdev, | |
| 104 | + (void *)adev->irq.ih.ring, | |
| 105 | + adev->irq.ih.ring_size, | |
| 106 | + PCI_DMA_BIDIRECTIONAL); | |
| 107 | + if (pci_dma_mapping_error(adev->pdev, adev->irq.ih.rb_dma_addr)) { | |
| 108 | + dev_err(&adev->pdev->dev, "Failed to DMA MAP the IH RB page\n"); | |
| 109 | + kfree((void *)adev->irq.ih.ring); | |
| 110 | + return -ENOMEM; | |
| 111 | + } | |
| 112 | + adev->irq.ih.wptr_offs = (adev->irq.ih.ring_size / 4) + 0; | |
| 113 | + adev->irq.ih.rptr_offs = (adev->irq.ih.ring_size / 4) + 1; | |
| 114 | + } | |
| 115 | + return 0; | |
| 116 | + } else { | |
| 117 | + r = amdgpu_wb_get(adev, &adev->irq.ih.wptr_offs); | |
| 118 | + if (r) { | |
| 119 | + dev_err(adev->dev, "(%d) ih wptr_offs wb alloc failed\n", r); | |
| 120 | + return r; | |
| 121 | + } | |
| 122 | + | |
| 123 | + r = amdgpu_wb_get(adev, &adev->irq.ih.rptr_offs); | |
| 124 | + if (r) { | |
| 125 | + amdgpu_wb_free(adev, adev->irq.ih.wptr_offs); | |
| 126 | + dev_err(adev->dev, "(%d) ih rptr_offs wb alloc failed\n", r); | |
| 127 | + return r; | |
| 128 | + } | |
| 129 | + | |
| 130 | + return amdgpu_ih_ring_alloc(adev); | |
| 131 | + } | |
| 132 | +} | |
| 133 | + | |
| 134 | +/** | |
| 135 | + * amdgpu_ih_ring_fini - tear down the IH state | |
| 136 | + * | |
| 137 | + * @adev: amdgpu_device pointer | |
| 138 | + * | |
| 139 | + * Tears down the IH state and frees buffer | |
| 140 | + * used for the IH ring buffer. | |
| 141 | + */ | |
| 142 | +void amdgpu_ih_ring_fini(struct amdgpu_device *adev) | |
| 143 | +{ | |
| 144 | + int r; | |
| 145 | + | |
| 146 | + if (adev->irq.ih.use_bus_addr) { | |
| 147 | + if (adev->irq.ih.ring) { | |
| 148 | + /* add 8 bytes for the rptr/wptr shadows and | |
| 149 | + * add them to the end of the ring allocation. | |
| 150 | + */ | |
| 151 | + pci_unmap_single(adev->pdev, adev->irq.ih.rb_dma_addr, | |
| 152 | + adev->irq.ih.ring_size + 8, PCI_DMA_BIDIRECTIONAL); | |
| 153 | + kfree((void *)adev->irq.ih.ring); | |
| 154 | + adev->irq.ih.ring = NULL; | |
| 155 | + } | |
| 156 | + } else { | |
| 157 | + if (adev->irq.ih.ring_obj) { | |
| 158 | + r = amdgpu_bo_reserve(adev->irq.ih.ring_obj, false); | |
| 159 | + if (likely(r == 0)) { | |
| 160 | + amdgpu_bo_kunmap(adev->irq.ih.ring_obj); | |
| 161 | + amdgpu_bo_unpin(adev->irq.ih.ring_obj); | |
| 162 | + amdgpu_bo_unreserve(adev->irq.ih.ring_obj); | |
| 163 | + } | |
| 164 | + amdgpu_bo_unref(&adev->irq.ih.ring_obj); | |
| 165 | + adev->irq.ih.ring = NULL; | |
| 166 | + adev->irq.ih.ring_obj = NULL; | |
| 167 | + } | |
| 168 | + amdgpu_wb_free(adev, adev->irq.ih.wptr_offs); | |
| 169 | + amdgpu_wb_free(adev, adev->irq.ih.rptr_offs); | |
| 170 | + } | |
| 171 | +} | |
| 172 | + | |
| 173 | +/** | |
| 174 | + * amdgpu_ih_process - interrupt handler | |
| 175 | + * | |
| 176 | + * @adev: amdgpu_device pointer | |
| 177 | + * | |
| 178 | + * Interrupt hander (VI), walk the IH ring. | |
| 179 | + * Returns irq process return code. | |
| 180 | + */ | |
| 181 | +int amdgpu_ih_process(struct amdgpu_device *adev) | |
| 182 | +{ | |
| 183 | + struct amdgpu_iv_entry entry; | |
| 184 | + u32 wptr; | |
| 185 | + | |
| 186 | + if (!adev->irq.ih.enabled || adev->shutdown) | |
| 187 | + return IRQ_NONE; | |
| 188 | + | |
| 189 | + wptr = amdgpu_ih_get_wptr(adev); | |
| 190 | + | |
| 191 | +restart_ih: | |
| 192 | + /* is somebody else already processing irqs? */ | |
| 193 | + if (atomic_xchg(&adev->irq.ih.lock, 1)) | |
| 194 | + return IRQ_NONE; | |
| 195 | + | |
| 196 | + DRM_DEBUG("%s: rptr %d, wptr %d\n", __func__, adev->irq.ih.rptr, wptr); | |
| 197 | + | |
| 198 | + /* Order reading of wptr vs. reading of IH ring data */ | |
| 199 | + rmb(); | |
| 200 | + | |
| 201 | + while (adev->irq.ih.rptr != wptr) { | |
| 202 | + amdgpu_ih_decode_iv(adev, &entry); | |
| 203 | + adev->irq.ih.rptr &= adev->irq.ih.ptr_mask; | |
| 204 | + | |
| 205 | + amdgpu_irq_dispatch(adev, &entry); | |
| 206 | + } | |
| 207 | + amdgpu_ih_set_rptr(adev); | |
| 208 | + atomic_set(&adev->irq.ih.lock, 0); | |
| 209 | + | |
| 210 | + /* make sure wptr hasn't changed while processing */ | |
| 211 | + wptr = amdgpu_ih_get_wptr(adev); | |
| 212 | + if (wptr != adev->irq.ih.rptr) | |
| 213 | + goto restart_ih; | |
| 214 | + | |
| 215 | + return IRQ_HANDLED; | |
| 216 | +} | |
| @@ -0,0 +1,62 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright 2014 Advanced Micro Devices, Inc. | |
| 3 | + * | |
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 | + * copy of this software and associated documentation files (the "Software"), | |
| 6 | + * to deal in the Software without restriction, including without limitation | |
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
| 9 | + * Software is furnished to do so, subject to the following conditions: | |
| 10 | + * | |
| 11 | + * The above copyright notice and this permission notice shall be included in | |
51 more lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 459 lines aren’t shown here.
This diff is large: its 93 lines aren’t shown here.
This diff is large: its 698 lines aren’t shown here.
This diff is large: its 323 lines aren’t shown here.
This diff is large: its 587 lines aren’t shown here.
This diff is large: its 671 lines aren’t shown here.
This diff is large: its 204 lines aren’t shown here.
This diff is large: its 351 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 808 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 126 lines aren’t shown here.
This diff is large: its 562 lines aren’t shown here.
This diff is large: its 420 lines aren’t shown here.
This diff is large: its 103 lines aren’t shown here.
This diff is large: its 235 lines aren’t shown here.
This diff is large: its 553 lines aren’t shown here.
This diff is large: its 209 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 1216 lines aren’t shown here.
This diff is large: its 318 lines aren’t shown here.
This diff is large: its 177 lines aren’t shown here.
This diff is large: its 985 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 725 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 1270 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 1409 lines aren’t shown here.
This diff is large: its 160 lines aren’t shown here.
This diff is large: its 8556 lines aren’t shown here.
This diff is large: its 808 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 776 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 2067 lines aren’t shown here.
This diff is large: its 74 lines aren’t shown here.
This diff is large: its 159 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 6700 lines aren’t shown here.
This diff is large: its 349 lines aren’t shown here.
This diff is large: its 280 lines aren’t shown here.
This diff is large: its 2514 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 472 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 1408 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 556 lines aren’t shown here.
This diff is large: its 945 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 945 lines aren’t shown here.
This diff is large: its 1942 lines aren’t shown here.
This diff is large: its 238 lines aren’t shown here.
This diff is large: its 453 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 186 lines aren’t shown here.
This diff is large: its 963 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 3803 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 3802 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 3754 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 5645 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 4330 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 1340 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 1302 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 196 lines aren’t shown here.
This diff is large: its 451 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 2168 lines aren’t shown here.
This diff is large: its 676 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 3344 lines aren’t shown here.
This diff is large: its 230 lines aren’t shown here.
This diff is large: its 220 lines aren’t shown here.
This diff is large: its 197 lines aren’t shown here.
This diff is large: its 699 lines aren’t shown here.
This diff is large: its 1418 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 1484 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 171 lines aren’t shown here.
This diff is large: its 515 lines aren’t shown here.
This diff is large: its 301 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 128 lines aren’t shown here.
This diff is large: its 148 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 195 lines aren’t shown here.
This diff is large: its 474 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 199 lines aren’t shown here.
This diff is large: its 2241 lines aren’t shown here.
This diff is large: its 853 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 906 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 845 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 825 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 664 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 574 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 1378 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 369 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 219 lines aren’t shown here.
This diff is large: its 436 lines aren’t shown here.
This diff is large: its 887 lines aren’t shown here.
This diff is large: its 194 lines aren’t shown here.
This diff is large: its 169 lines aren’t shown here.
This diff is large: its 295 lines aren’t shown here.
This diff is large: its 138 lines aren’t shown here.
This diff is large: its 208 lines aren’t shown here.
This diff is large: its 76 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 970 lines aren’t shown here.
This diff is large: its 85 lines aren’t shown here.
This diff is large: its 189 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 117 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 291 lines aren’t shown here.
This diff is large: its 191 lines aren’t shown here.
This diff is large: its 118 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 922 lines aren’t shown here.
This diff is large: its 10251 lines aren’t shown here.
This diff is large: its 1069 lines aren’t shown here.
This diff is large: its 1199 lines aren’t shown here.
This diff is large: its 11495 lines aren’t shown here.
This diff is large: its 3578 lines aren’t shown here.
This diff is large: its 1069 lines aren’t shown here.
This diff is large: its 33081 lines aren’t shown here.
This diff is larger than a page shows: the files after these aren’t listed. Clone the repository to see all of it.